FreeBASIC  0.91.0
gfx_driver_fbdev.c
Go to the documentation of this file.
1 /* Framebuffer device gfx driver */
2 
3 #include "../fb_gfx.h"
4 #include "fb_gfx_linux.h"
5 #include "../../rtlib/unix/fb_private_console.h"
6 
7 #ifndef DISABLE_FBDEV
8 
9 #include <fcntl.h>
10 #include <sys/ioctl.h>
11 #include <sys/mman.h>
12 #include <sys/time.h>
13 #include <linux/fb.h>
14 #include <pthread.h>
15 
16 #ifndef FB_AUX_VGA_PLANES_VGA4
17 #define FB_AUX_VGA_PLANES_VGA4 0
18 #endif
19 
20 #define OUTB(port,value) { __asm__ __volatile__ ("outb %b0, %w1" : : "a"(value), "Nd"(port)); }
21 
22 
23 typedef struct FBDEVDRIVER
24 {
25  int w, h, depth, flags;
26  ssize_t refresh_rate;
28 } FBDEVDRIVER;
29 
31 
32 static int driver_init(char *title, int w, int h, int depth, int refresh_rate, int flags);
33 static void driver_exit(void);
34 static void driver_lock(void);
35 static void driver_unlock(void);
36 static void driver_set_palette(int index, int r, int g, int b);
37 static void driver_wait_vsync(void);
38 static int driver_get_mouse(int *x, int *y, int *z, int *buttons, int *clip);
39 static void driver_set_mouse(int x, int y, int cursor, int clip);
40 static int *driver_fetch_modes(int depth, int *size);
41 
43 {
44  "FBDev", /* char *name; */
45  driver_init, /* int (*init)(int w, int h, char *title, int fullscreen); */
46  driver_exit, /* void (*exit)(void); */
47  driver_lock, /* void (*lock)(void); */
48  driver_unlock, /* void (*unlock)(void); */
49  driver_set_palette, /* void (*set_palette)(int index, int r, int g, int b); */
50  driver_wait_vsync, /* void (*wait_vsync)(void); */
51  driver_get_mouse, /* int (*get_mouse)(int *x, int *y, int *z, int *buttons, int *clip); */
52  driver_set_mouse, /* void (*set_mouse)(int x, int y, int cursor, int clip); */
53  NULL, /* void (*set_window_title)(char *title); */
54  NULL, /* int (*set_window_pos)(int x, int y); */
55  driver_fetch_modes, /* int *(*fetch_modes)(void); */
56  NULL, /* void (*flip)(void); */
57  NULL /* void (*poll_events)(void); */
58 };
59 
60 
61 typedef struct {
62  int w, h;
63 } GFXMODE;
64 
65 
66 static const GFXMODE standard_mode[] = {
67  { 320, 200 }, { 320, 240 }, { 400, 300 }, { 512, 384 }, { 640, 400 }, { 640, 480 },
68  { 800, 600 }, { 1024, 768 }, { 1280, 1024 }, { 1600, 1200 }, { 0, 0 }
69 };
70 
71 static int device_fd = -1;
72 static struct fb_fix_screeninfo device_info;
73 static struct fb_var_screeninfo mode, orig_mode;
74 static struct fb_cmap cmap, orig_cmap;
75 static unsigned char *framebuffer = NULL;
76 static unsigned short *palette = NULL;
77 static unsigned char color_conv[4096];
78 static BLITTER *blitter;
83 static int mouse_clip = 0;
84 static unsigned int last_click_time = 0;
85 static pthread_t thread;
86 static pthread_mutex_t mutex;
87 static pthread_cond_t cond;
88 
89 static void vga16_blitter(unsigned char *dest, int pitch)
90 {
91  unsigned int color;
92  unsigned char buffer[fb_fbdev.w], pattern;
93  unsigned char *s, *source = __fb_gfx->framebuffer;
94  int x, y, plane, i, offset;
95 
96  OUTB(0x3CE, 0x03);
97  OUTB(0x3CF, 0x00);
98 
99  OUTB(0x3CE, 0x05);
100  OUTB(0x3CF, 0x00);
101 
102  OUTB(0x3CE, 0x01);
103  OUTB(0x3CF, 0x00);
104 
105  OUTB(0x3CE, 0x08);
106  OUTB(0x3CF, 0xFF);
107 
108  for (y = 0; y < fb_fbdev.h; y++) {
109  if (__fb_gfx->dirty[y]) {
110  offset = 0;
111  s = source;
112  for (x = 0; x < fb_fbdev.w; x += 8) {
113  for (plane = 0; plane < 4; plane++) {
114  pattern = 0;
115  for (i = 0; i < 8; i++) {
116  if (__fb_gfx->depth == 8) {
117  color = __fb_gfx->device_palette[s[i]];
118  color = color_conv[((color & 0xF0) >> 4) | ((color & 0xF000) >> 8) | ((color & 0xF00000) >> 12)];
119  }
120  else {
121  color = s[i];
122  }
123 
124  if (color & (1 << plane))
125  pattern |= 1 << (7 - i);
126  }
127  buffer[((fb_fbdev.w >> 3) * plane) + offset] = pattern;
128  }
129  offset++;
130  s += 8;
131  }
132  for (plane = 0; plane < 4; plane++) {
133  OUTB(0x3C4, 0x02);
134  OUTB(0x3C5, (1 << plane));
135  fb_hMemCpy(dest, buffer + ((fb_fbdev.w >> 3) * plane), (fb_fbdev.w >> 3));
136  }
137  }
138  dest += pitch;
139  source += __fb_gfx->pitch;
140  }
141 }
142 
143 static void *driver_thread(void *arg)
144 {
145  struct fb_vblank vblank;
146  unsigned int count, cur_time;
147  fd_set set;
148  struct timeval cur_tv, tv = { 0, 0 };
149  unsigned char buffer[1024];
150  int buttons, bytes_read, bytes_left = 0;
151  EVENT e;
152 
153  (void)arg;
154 
155  is_running = TRUE;
156 
157  pthread_mutex_lock(&mutex);
158  pthread_cond_signal(&cond);
159  pthread_mutex_unlock(&mutex);
160 
161  while (is_running) {
162  pthread_mutex_lock(&mutex);
163 
164  if (mouse_fd >= 0) {
165  FD_ZERO(&set);
166  FD_SET(mouse_fd, &set);
167  if (select(FD_SETSIZE, &set, NULL, NULL, &tv) > 0) {
168  bytes_read = read(mouse_fd, &buffer[bytes_left], sizeof(buffer) - bytes_left);
169  if (bytes_read > 0) {
170  bytes_left += bytes_read;
171  while (bytes_left >= mouse_packet_size) {
172  if (((mouse_packet_size == 3) && ((buffer[0] & 0xC0) != 0x00)) ||
173  ((mouse_packet_size == 4) && ((buffer[0] & 0xC8) != 0x08)))
174  bytes_read = 1;
175  else {
176  e.dx = (unsigned int)buffer[1] - ((int)(buffer[0] & 0x10) << 4);
177  e.dy = -(unsigned int)buffer[2] + ((int)(buffer[0] & 0x20) << 3);
178  mouse_x += e.dx;
179  mouse_y += e.dy;
180  e.x = mouse_x = MID(0, mouse_x, __fb_gfx->w - 1);
181  e.y = mouse_y = MID(0, mouse_y, __fb_gfx->h - 1);
182  if (e.dx || e.dy) {
184  fb_hPostEvent(&e);
185  }
186  buttons = mouse_buttons;
187  mouse_buttons = buffer[0] & 0x7;
188  if ((mouse_packet_size == 4) && (buffer[3] & 0xF)) {
189  mouse_z += (((buffer[3] & 0xF) - 7) >> 3);
191  e.z = mouse_z;
192  fb_hPostEvent(&e);
193  }
194  buttons = (mouse_buttons ^ buttons) & 0x7;
195  for (e.button = 0x4; e.button; e.button >>= 1) {
196  if (buttons & e.button) {
197  if (mouse_buttons & e.button) {
198  gettimeofday(&cur_tv, NULL);
199  cur_time = (cur_tv.tv_sec * 1000) + (cur_tv.tv_usec / 1000);
200  if (cur_time - last_click_time < DOUBLE_CLICK_TIME)
202  else
204  last_click_time = cur_time;
205  }
206  else
208  fb_hPostEvent(&e);
209  }
210  }
211  bytes_read = mouse_packet_size;
212  }
213  bytes_left -= bytes_read;
214  memcpy(buffer, &buffer[bytes_read], bytes_left);
215  }
216  }
217  }
218  }
219 
220  if (vsync_flags & (FB_VBLANK_HAVE_VBLANK | FB_VBLANK_HAVE_VCOUNT)) {
221  if (vsync_flags & FB_VBLANK_HAVE_VCOUNT) {
222  ioctl(device_fd, FBIOGET_VBLANK, &vblank);
223  do {
224  count = vblank.vcount;
225  } while ((ioctl(device_fd, FBIOGET_VBLANK, &vblank) == 0) && (vblank.vcount >= count));
226  }
227  else {
228  while ((ioctl(device_fd, FBIOGET_VBLANK, &vblank) == 0) && (vblank.flags & FB_VBLANK_VBLANKING))
229  ;
230  while ((ioctl(device_fd, FBIOGET_VBLANK, &vblank) == 0) && (!(vblank.flags & FB_VBLANK_VBLANKING)))
231  ;
232  }
233  }
234  pthread_cond_signal(&cond);
235 
236  if (is_active) {
237  if (is_palette_changed) {
238  if (device_info.type != FB_TYPE_VGA_PLANES)
239  ioctl(device_fd, FBIOPUTCMAP, &cmap);
240  else
241  fb_hMemSet(__fb_gfx->dirty, TRUE, fb_fbdev.h);
242  if (mouse_fd >= 0)
245  }
246  if ((mouse_fd >= 0) && (mouse_shown))
249  fb_hMemSet(__fb_gfx->dirty, FALSE, fb_fbdev.h);
250  if ((mouse_fd >= 0) && (mouse_shown))
252  }
253 
254  pthread_mutex_unlock(&mutex);
255 
256  if (vsync_flags & (FB_VBLANK_HAVE_VBLANK | FB_VBLANK_HAVE_VCOUNT))
257  usleep(8000);
258  else
259  usleep(1000000 / ((fb_fbdev.refresh_rate > 0) ? fb_fbdev.refresh_rate : 60));
260  }
261 
262  return NULL;
263 }
264 
265 static void driver_save_screen(void)
266 {
267  EVENT e;
268 
269  pthread_mutex_lock(&mutex);
270  is_active = FALSE;
271  pthread_mutex_unlock(&mutex);
272  ioctl(device_fd, FBIOPUTCMAP, &orig_cmap);
274  fb_hPostEvent(&e);
275 }
276 
277 static void driver_restore_screen(void)
278 {
279  EVENT e;
280 
281  pthread_mutex_lock(&mutex);
282  is_active = TRUE;
284  fb_hMemSet(framebuffer, 0, device_info.smem_len);
285  fb_hMemSet(__fb_gfx->dirty, TRUE, fb_fbdev.h);
286  pthread_mutex_unlock(&mutex);
288  fb_hPostEvent(&e);
289 }
290 
291 static void driver_key_handler( int pressed, int repeated, int scancode, int key )
292 {
293  EVENT e;
294 
295  if( pressed ) {
296  if( repeated ) {
298  } else {
299  e.type = EVENT_KEY_PRESS;
300  }
301  } else {
303  }
304 
305  e.scancode = scancode;
306 
307  /* Don't return extended keycodes in the ascii field */
308  e.ascii = ((key < 0) || (key > 0xFF)) ? 0 : key;
309 
310  fb_hPostEvent( &e );
311 }
312 
313 static int driver_init(char *title, int w, int h, int depth, int refresh_rate, int flags)
314 {
315  const char *device_name;
316  int try, res_index, i, j, r, g, b, dist, best_dist, best_index = 0;
317  ssize_t dummy;
318  int palette_len;
319  struct fb_vblank vblank;
320  const char *mouse_device[] = { "/dev/input/mice", "/dev/usbmouse", "/dev/psaux", NULL };
321  const unsigned char im_init[] = { 243, 200, 243, 100, 243, 80 };
322 
323  if (flags & DRIVER_OPENGL)
324  return -1;
325 
326  fb_fbdev.w = w;
327  fb_fbdev.h = h;
328  fb_fbdev.flags = flags;
329  depth = MAX(depth, 4);
330 
331  device_name = getenv("FBGFX_FRAMEBUFFER");
332  if (!device_name)
333  device_name = "/dev/fb0";
334  device_fd = open(device_name, O_RDWR, 0);
335  if (device_fd < 0)
336  return -1;
337 
338  if ((ioctl(device_fd, FBIOGET_FSCREENINFO, &device_info) < 0) ||
339  (ioctl(device_fd, FBIOGET_VSCREENINFO, &orig_mode) < 0) ||
340  ((device_info.type != FB_TYPE_PACKED_PIXELS)
341 #if defined(i386) && defined(FB_TYPE_VGA_PLANES)
342  && (device_info.type != FB_TYPE_VGA_PLANES)
343 #endif
344  ) ||
345  ((device_info.visual != FB_VISUAL_PSEUDOCOLOR) &&
346  (device_info.visual != FB_VISUAL_DIRECTCOLOR) &&
347  (device_info.visual != FB_VISUAL_TRUECOLOR))) {
348  close(device_fd);
349  device_fd = -1;
350  return -1;
351  }
352 
353 #if defined(i386) && defined(FB_TYPE_VGA_PLANES)
354  if ((device_info.type == FB_TYPE_VGA_PLANES) && (device_info.type_aux == FB_AUX_VGA_PLANES_VGA4)) {
355  mode = orig_mode;
356  if ((orig_mode.xres >= w) && (orig_mode.yres >= h) && (__fb_con.has_perm) && (depth <= 8)) {
357  /* we are in vga16 mode, got to live with it */
358  goto got_mode;
359  }
360 
361  close(device_fd);
362  device_fd = -1;
363  return -1;
364  }
365 #endif
366 
367  /* tries in order:
368  * 1) wanted resolution and color depth;
369  * 2) any higher resolution and wanted color depth;
370  * 3) wanted resolution and original color depth;
371  * 4) any higher resolution and original color depth;
372  */
373  for (try = 0; try < 4; try++) {
374  mode = orig_mode;
375 
376  mode.xoffset = 0;
377  mode.yoffset = 0;
378 
379  if (try < 2) {
380  mode.bits_per_pixel = depth;
381  mode.grayscale = 0;
382  switch (depth) {
383  case 15:
384  mode.red.offset = 10; mode.red.length = 5;
385  mode.green.offset = 5; mode.green.length = 5;
386  mode.blue.offset = 0; mode.blue.length = 5;
387  break;
388  case 16:
389  mode.red.offset = 11; mode.red.length = 5;
390  mode.green.offset = 5; mode.green.length = 6;
391  mode.blue.offset = 0; mode.blue.length = 5;
392  break;
393  case 24:
394  case 32:
395  mode.red.offset = 16; mode.red.length = 8;
396  mode.green.offset = 8; mode.green.length = 8;
397  mode.blue.offset = 0; mode.blue.length = 8;
398  break;
399  default:
400  mode.red.offset = mode.red.length = 0;
401  mode.green.offset = mode.green.length = 0;
402  mode.blue.offset = mode.blue.length = 0;
403  break;
404  }
405  mode.red.msb_right = mode.green.msb_right = mode.blue.msb_right = 0;
406  }
407 
408  if (try & 1) {
409  for (res_index = 0; standard_mode[res_index].w; res_index++) {
410  if ((standard_mode[res_index].w >= w) && (standard_mode[res_index].h > h)) {
411  mode.xres = mode.xres_virtual = standard_mode[res_index].w;
412  mode.yres = mode.yres_virtual = standard_mode[res_index].h;
413  if (ioctl(device_fd, FBIOPUT_VSCREENINFO, &mode) == 0)
414  goto got_mode;
415  }
416  }
417  }
418  else {
419  mode.xres = mode.xres_virtual = w;
420  mode.yres = mode.yres_virtual = h;
421  if (ioctl(device_fd, FBIOPUT_VSCREENINFO, &mode) == 0)
422  goto got_mode;
423  }
424  }
425 
426  mode = orig_mode;
427  if ((mode.xres >= w) && (mode.yres >= h))
428  goto got_mode;
429 
430  close(device_fd);
431  device_fd = -1;
432  return -1;
433 
434 got_mode:
436  return -1;
437 
438  fb_hFBDevInfo(&dummy, &dummy, &dummy, &fb_fbdev.refresh_rate);
439  __fb_gfx->refresh_rate = fb_fbdev.refresh_rate;
440 
441  if (ioctl(device_fd, FBIOGET_FSCREENINFO, &device_info) < 0)
442  return -1;
443 
444  framebuffer = mmap(NULL, device_info.smem_len, PROT_READ | PROT_WRITE, MAP_SHARED, device_fd, 0);
445  if (framebuffer == (unsigned char *)-1)
446  return -1;
447 
448  fb_hMemSet(framebuffer, 0, device_info.smem_len);
449 
450  if (mode.bits_per_pixel == 4) {
451  palette_len = 16;
452  framebuffer_offset = (((mode.yres - h) >> 1) * (mode.xres >> 3)) + ((mode.xres - w) >> 4);
454  } else {
455  palette_len = 256;
456  framebuffer_offset = (((mode.yres - h) >> 1) * device_info.line_length) +
457  (((mode.xres - w) >> 1) * BYTES_PER_PIXEL(mode.bits_per_pixel));
458  blitter = fb_hGetBlitter(mode.bits_per_pixel, (mode.red.offset == 0) ? TRUE : FALSE);
459  if (!blitter)
460  return -1;
461  }
462 
463  mouse_packet_size = 3;
464  for (try = 0; mouse_device[try]; try++) {
465  mouse_fd = open(mouse_device[try], O_RDWR, 0);
466  if ((mouse_fd >= 0) && (write(mouse_fd, im_init, sizeof(im_init)) == sizeof(im_init))) {
468  break;
469  }
470  if (mouse_fd < 0)
471  mouse_fd = open(mouse_device[try], O_RDONLY, 0);
472  if (mouse_fd >= 0)
473  break;
474  }
475  if (mouse_fd >= 0) {
476  mouse_x = w >> 1;
477  mouse_y = h >> 1;
478  mouse_buttons = mouse_z = 0;
479  mouse_shown = TRUE;
481  }
482 
483  palette = (unsigned short *)malloc(sizeof(unsigned short) * 1536);
484  orig_cmap.start = 0;
485  orig_cmap.len = palette_len;
486  orig_cmap.transp = NULL;
487  orig_cmap.red = palette;
488  orig_cmap.green = palette + 256;
489  orig_cmap.blue = palette + 512;
490  ioctl(device_fd, FBIOGETCMAP, &orig_cmap);
491  cmap.start = 0;
492  cmap.len = palette_len;
493  cmap.transp = NULL;
494  cmap.red = palette + 768;
495  cmap.green = palette + 1024;
496  cmap.blue = palette + 1280;
497  if ((mode.bits_per_pixel == 4) && (depth == 8)) {
498  /* set safe palette */
499  for (i = 0; i < 16; i++) {
500  r = cmap.red[i] = __fb_palette[FB_PALETTE_16].data[(i * 3) + 2] << 8;
501  g = cmap.green[i] = __fb_palette[FB_PALETTE_16].data[(i * 3) + 1] << 8;
502  b = cmap.blue[i] = __fb_palette[FB_PALETTE_16].data[(i * 3) ] << 8;
503  __fb_gfx->device_palette[i] = (r >> 8) | g | (b << 8);
504  }
505  ioctl(device_fd, FBIOPUTCMAP, &cmap);
506  for (i = 0; i < 4096; i++) {
507  best_dist = 1000000;
508  r = (i & 0xF) << 4;
509  g = (i & 0xF0);
510  b = (i & 0xF00) >> 4;
511  for (j = 0; j < 16; j++) {
512  dist = fb_hColorDistance(j, r, g, b);
513  if (dist < best_dist) {
514  best_dist = dist;
515  best_index = j;
516  }
517  }
518  color_conv[i] = best_index;
519  }
520  }
521 
522  if (ioctl(device_fd, FBIOGET_VBLANK, &vblank) == 0)
523  vsync_flags = vblank.flags;
524 
525  pthread_mutex_init(&mutex, NULL);
526  pthread_cond_init(&cond, NULL);
527  pthread_mutex_lock(&mutex);
528  if (pthread_create(&thread, NULL, driver_thread, NULL)) {
529  pthread_mutex_unlock(&mutex);
530  return -1;
531  }
532  pthread_cond_wait(&cond, &mutex);
533  pthread_mutex_unlock(&mutex);
534 
535  return 0;
536 }
537 
538 static void driver_exit(void)
539 {
540  if (is_running) {
541  is_running = FALSE;
542  pthread_join(thread, NULL);
543  pthread_mutex_destroy(&mutex);
544  pthread_cond_destroy(&cond);
545  }
546 
547  if (mouse_fd >= 0) {
549  close(mouse_fd);
550  mouse_fd = -1;
551  }
552 
553  if (device_fd >= 0) {
555  if (framebuffer) {
556  munmap(framebuffer, device_info.smem_len);
557  framebuffer = NULL;
558  }
559  if (palette) {
560  ioctl(device_fd, FBIOPUTCMAP, &orig_cmap);
561  free(palette);
562  palette = NULL;
563  }
564  ioctl(device_fd, FBIOPUT_VSCREENINFO, &orig_mode);
565  close(device_fd);
566  device_fd = -1;
567  }
568 }
569 
570 static void driver_lock(void)
571 {
572  pthread_mutex_lock(&mutex);
573 }
574 
575 static void driver_unlock(void)
576 {
577  pthread_mutex_unlock(&mutex);
578 }
579 
580 static void driver_set_palette(int index, int r, int g, int b)
581 {
582  cmap.red[index] = r << 8;
583  cmap.green[index] = g << 8;
584  cmap.blue[index] = b << 8;
586 }
587 
588 static void driver_wait_vsync(void)
589 {
590  pthread_mutex_lock(&mutex);
591  pthread_cond_wait(&cond, &mutex);
592  pthread_mutex_unlock(&mutex);
593 }
594 
595 static int driver_get_mouse(int *x, int *y, int *z, int *buttons, int *clip)
596 {
597  if (mouse_fd < 0)
598  return -1;
599  *x = mouse_x;
600  *y = mouse_y;
601  *z = mouse_z;
602  *buttons = mouse_buttons;
603  *clip = mouse_clip;
604  return 0;
605 }
606 
607 static void driver_set_mouse(int x, int y, int cursor, int clip)
608 {
609  if ((x >= 0) && (x < __fb_gfx->w))
610  mouse_x = x;
611  if ((y >= 0) && (y < __fb_gfx->h))
612  mouse_y = y;
613  mouse_shown = (cursor != 0);
614  if (clip == 0)
615  mouse_clip = FALSE;
616  else if (clip > 0)
617  mouse_clip = TRUE;
618 }
619 
620 static int *driver_fetch_modes(int depth, int *size)
621 {
622  const char *device_name;
623  int i, fd, num_sizes = 0, *sizes = NULL;
624 
625  if ((depth != 8) && (depth != 15) && (depth != 16) && (depth != 24) && (depth != 32))
626  return NULL;
627 
628  if (device_fd < 0) {
629  device_name = getenv("FBGFX_FRAMEBUFFER");
630  if (!device_name)
631  device_name = "/dev/fb0";
632  fd = open(device_name, O_RDWR, 0);
633  if (fd < 0)
634  return NULL;
635  }
636  else
637  fd = device_fd;
638 
639  ioctl(fd, FBIOGET_VSCREENINFO, &mode);
640  for (i = 0; standard_mode[i].w; i++) {
641  mode.bits_per_pixel = depth;
642  mode.activate = FB_ACTIVATE_TEST;
643  mode.xres = mode.xres_virtual = standard_mode[i].w;
644  mode.yres = mode.yres_virtual = standard_mode[i].h;
645  if (ioctl(fd, FBIOPUT_VSCREENINFO, &mode) == 0) {
646  num_sizes++;
647  sizes = realloc(sizes, num_sizes * sizeof(int));
648  sizes[num_sizes - 1] = (mode.xres << 16) | mode.yres;
649  }
650  }
651 
652  if (device_fd < 0)
653  close(fd);
654 
655  *size = num_sizes;
656  return sizes;
657 }
658 
659 int fb_hFBDevInfo(ssize_t *width, ssize_t *height, ssize_t *depth, ssize_t *refresh)
660 {
661  struct fb_var_screeninfo temp, *info;
662  int fd = -1, htotal, vtotal, flags, res;
663 
664  if (device_fd < 0) {
665  if ((fd = open("/dev/fb0", O_RDWR, 0)) < 0)
666  return -1;
667  res = ioctl(fd, FBIOGET_VSCREENINFO, &temp);
668  close(fd);
669  if (res < 0)
670  return -1;
671  info = &temp;
672  }
673  else
674  info = &mode;
675 
676  htotal = info->left_margin + info->xres + info->right_margin + info->hsync_len;
677  vtotal = info->upper_margin + info->yres + info->lower_margin + info->vsync_len;
678  flags = info->vmode & FB_VMODE_MASK;
679 
680  if (!(flags == FB_VMODE_INTERLACED))
681  vtotal <<= 1;
682  if (flags == FB_VMODE_DOUBLE)
683  vtotal <<= 1;
684 
685  *width = info->xres;
686  *height = info->yres;
687  *depth = info->bits_per_pixel;
688  if ((info->pixclock) && (htotal) && (vtotal))
689  *refresh = (((1e12 / info->pixclock) / htotal) / vtotal) * 2;
690 
691  return 0;
692 }
693 
694 #endif