Linux kernel & device driver programming

Cross-Referenced Linux and Device Driver Code

[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ]
Version: [ 2.6.11.8 ] [ 2.6.25 ] [ 2.6.25.8 ] [ 2.6.31.13 ] Architecture: [ i386 ]
  1 /*
  2  * some generic framebuffer device stuff
  3  *
  4  */
  5 
  6 #include <stdio.h>
  7 #include <stdlib.h>
  8 #include <string.h>
  9 #include <unistd.h>
 10 #include <fcntl.h>
 11 #include <termios.h>
 12 #include <signal.h>
 13 #include <errno.h>
 14 #include <setjmp.h>
 15 #include <sys/ioctl.h>
 16 #include <sys/mman.h>
 17 #include <sys/wait.h>
 18 #include <sys/stat.h>
 19 
 20 #include <linux/kd.h>
 21 #include <linux/vt.h>
 22 #include <linux/fb.h>
 23 
 24 #include <asm/page.h>
 25 
 26 #include "fbtools.h"
 27 
 28 /* -------------------------------------------------------------------- */
 29 /* exported stuff                                                       */
 30 
 31 struct fb_fix_screeninfo   fb_fix;
 32 struct fb_var_screeninfo   fb_var;
 33 unsigned char             *fb_mem;
 34 int                        fb_mem_offset = 0;
 35 int                        fb_switch_state = FB_ACTIVE;
 36 
 37 /* -------------------------------------------------------------------- */
 38 /* internal variables                                                   */
 39 
 40 static int                       fb,tty;
 41 #if 0
 42 static int                       bpp,black,white;
 43 #endif
 44 
 45 static int                       orig_vt_no = 0;
 46 static struct vt_mode            vt_mode;
 47 
 48 static int                       kd_mode;
 49 static struct vt_mode            vt_omode;
 50 static struct termios            term;
 51 static struct fb_var_screeninfo  fb_ovar;
 52 static unsigned short            ored[256], ogreen[256], oblue[256];
 53 static struct fb_cmap            ocmap = { 0, 256, ored, ogreen, oblue };
 54 
 55 /* -------------------------------------------------------------------- */
 56 /* devices                                                              */
 57 
 58 struct DEVS {
 59     char *fb0;
 60     char *fbnr;
 61     char *ttynr;
 62 };
 63 
 64 struct DEVS devs_default = {
 65     fb0:   "/dev/fb0",
 66     fbnr:  "/dev/fb%d",
 67     ttynr: "/dev/tty%d",
 68 };
 69 struct DEVS devs_devfs = {
 70     fb0:   "/dev/fb/0",
 71     fbnr:  "/dev/fb/%d",
 72     ttynr: "/dev/vc/%d",
 73 };
 74 struct DEVS *devices;
 75 
 76 static void dev_init(void)
 77 {
 78     struct stat dummy;
 79 
 80     if (NULL != devices)
 81         return;
 82     if (0 == stat("/dev/.devfsd",&dummy))
 83         devices = &devs_devfs;
 84     else
 85         devices = &devs_default;
 86 }
 87 
 88 /* -------------------------------------------------------------------- */
 89 /* console switching                                                    */
 90 
 91 extern int debug;
 92 
 93 static void
 94 fb_switch_signal(int signal)
 95 {
 96     if (signal == SIGUSR1) {
 97         /* release */
 98         fb_switch_state = FB_REL_REQ;
 99         if (debug)
100             write(2,"vt: SIGUSR1\n",12);
101     }
102     if (signal == SIGUSR2) {
103         /* acquisition */
104         fb_switch_state = FB_ACQ_REQ;
105         if (debug)
106             write(2,"vt: SIGUSR2\n",12);
107     }
108 }
109 
110 void
111 fb_switch_release()
112 {
113     ioctl(tty, VT_RELDISP, 1);
114     fb_switch_state = FB_INACTIVE;
115     if (debug)
116         write(2,"vt: release\n",12);
117 }
118 
119 void
120 fb_switch_acquire()
121 {
122     ioctl(tty, VT_RELDISP, VT_ACKACQ);
123     fb_switch_state = FB_ACTIVE;
124     if (debug)
125         write(2,"vt: acquire\n",12);
126 }
127 
128 int
129 fb_switch_init()
130 {
131     struct sigaction act,old;
132 
133     memset(&act,0,sizeof(act));
134     act.sa_handler  = fb_switch_signal;
135     sigemptyset(&act.sa_mask);
136     sigaction(SIGUSR1,&act,&old);
137     sigaction(SIGUSR2,&act,&old);
138     
139     if (-1 == ioctl(tty,VT_GETMODE, &vt_mode)) {
140         perror("ioctl VT_GETMODE");
141         exit(1);
142     }
143     vt_mode.mode   = VT_PROCESS;
144     vt_mode.waitv  = 0;
145     vt_mode.relsig = SIGUSR1;
146     vt_mode.acqsig = SIGUSR2;
147     
148     if (-1 == ioctl(tty,VT_SETMODE, &vt_mode)) {
149         perror("ioctl VT_SETMODE");
150         exit(1);
151     }
152     return 0;
153 }
154 
155 /* -------------------------------------------------------------------- */
156 /* initialisation & cleanup                                             */
157 
158 void
159 fb_memset (void *addr, int c, size_t len)
160 {
161 #if 1 /* defined(__powerpc__) */
162     unsigned int i, *p;
163     
164     i = (c & 0xff) << 8;
165     i |= i << 16;
166     len >>= 2;
167     for (p = addr; len--; p++)
168         *p = i;
169 #else
170     memset(addr, c, len);
171 #endif
172 }
173 
174 static int
175 fb_setmode(char *name)
176 {
177     FILE *fp;
178     char line[80],label[32],value[16];
179     int  geometry=0, timings=0;
180     
181     /* load current values */
182     if (-1 == ioctl(fb,FBIOGET_VSCREENINFO,&fb_var)) {
183         perror("ioctl FBIOGET_VSCREENINFO");
184         exit(1);
185     }
186     
187     if (NULL == name)
188         return -1;
189     if (NULL == (fp = fopen("/etc/fb.modes","r")))
190         return -1;
191     while (NULL != fgets(line,79,fp)) {
192         if (1 == sscanf(line, "mode \"%31[^\"]\"",label) &&
193             0 == strcmp(label,name)) {
194             /* fill in new values */
195             fb_var.sync  = 0;
196             fb_var.vmode = 0;
197             while (NULL != fgets(line,79,fp) &&
198                    NULL == strstr(line,"endmode")) {
199                 if (5 == sscanf(line," geometry %d %d %d %d %d",
200                                 &fb_var.xres,&fb_var.yres,
201                                 &fb_var.xres_virtual,&fb_var.yres_virtual,
202                                 &fb_var.bits_per_pixel))
203                     geometry = 1;
204                 if (7 == sscanf(line," timings %d %d %d %d %d %d %d",
205                                 &fb_var.pixclock,
206                                 &fb_var.left_margin,  &fb_var.right_margin,
207                                 &fb_var.upper_margin, &fb_var.lower_margin,
208                                 &fb_var.hsync_len,    &fb_var.vsync_len))
209                     timings = 1;
210                 if (1 == sscanf(line, " hsync %15s",value) &&
211                     0 == strcasecmp(value,"high"))
212                     fb_var.sync |= FB_SYNC_HOR_HIGH_ACT;
213                 if (1 == sscanf(line, " vsync %15s",value) &&
214                     0 == strcasecmp(value,"high"))
215                     fb_var.sync |= FB_SYNC_VERT_HIGH_ACT;
216                 if (1 == sscanf(line, " csync %15s",value) &&
217                     0 == strcasecmp(value,"high"))
218                     fb_var.sync |= FB_SYNC_COMP_HIGH_ACT;
219                 if (1 == sscanf(line, " extsync %15s",value) &&
220                     0 == strcasecmp(value,"true"))
221                     fb_var.sync |= FB_SYNC_EXT;
222                 if (1 == sscanf(line, " laced %15s",value) &&
223                     0 == strcasecmp(value,"true"))
224                     fb_var.vmode |= FB_VMODE_INTERLACED;
225                 if (1 == sscanf(line, " double %15s",value) &&
226                     0 == strcasecmp(value,"true"))
227                     fb_var.vmode |= FB_VMODE_DOUBLE;
228             }
229             /* ok ? */
230             if (!geometry || !timings)
231                 return -1;
232             /* set */
233             fb_var.xoffset = 0;
234             fb_var.yoffset = 0;
235             if (-1 == ioctl(fb,FBIOPUT_VSCREENINFO,&fb_var))
236                 perror("ioctl FBIOPUT_VSCREENINFO");
237             /* look what we have now ... */
238             if (-1 == ioctl(fb,FBIOGET_VSCREENINFO,&fb_var)) {
239                 perror("ioctl FBIOGET_VSCREENINFO");
240                 exit(1);
241             }
242             return 0;
243         }
244     }
245     return -1;
246 }
247 
248 static void
249 fb_setvt(int vtno)
250 {
251     struct vt_stat vts;
252     char vtname[12];
253     
254     if (vtno < 0) {
255         if (-1 == ioctl(tty,VT_OPENQRY, &vtno) || vtno == -1) {
256             perror("ioctl VT_OPENQRY");
257             exit(1);
258         }
259     }
260 
261     vtno &= 0xff;
262     sprintf(vtname, devices->ttynr, vtno);
263     chown(vtname, getuid(), getgid());
264     if (-1 == access(vtname, R_OK | W_OK)) {
265         fprintf(stderr,"access %s: %s\n",vtname,strerror(errno));
266         exit(1);
267     }
268     switch (fork()) {
269     case 0:
270         break;
271     case -1:
272         perror("fork");
273         exit(1);
274     default:
275         exit(0);
276     }
277     close(tty);
278     close(0);
279     close(1);
280     close(2);
281     setsid();
282     open(vtname,O_RDWR);
283     dup(0);
284     dup(0);
285 
286     if (-1 == ioctl(tty,VT_GETSTATE, &vts)) {
287         perror("ioctl VT_GETSTATE");
288         exit(1);
289     }
290     orig_vt_no = vts.v_active;
291     if (-1 == ioctl(tty,VT_ACTIVATE, vtno)) {
292         perror("ioctl VT_ACTIVATE");
293         exit(1);
294     }
295     if (-1 == ioctl(tty,VT_WAITACTIVE, vtno)) {
296         perror("ioctl VT_WAITACTIVE");
297         exit(1);
298     }
299 }
300 
301 /* Hmm. radeonfb needs this. matroxfb doesn't. */
302 static int fb_activate_current(int tty)
303 {
304     struct vt_stat vts;
305     
306     if (-1 == ioctl(tty,VT_GETSTATE, &vts)) {
307         perror("ioctl VT_GETSTATE");
308         return -1;
309     }
310     if (-1 == ioctl(tty,VT_ACTIVATE, vts.v_active)) {
311         perror("ioctl VT_ACTIVATE");
312         return -1;
313     }
314     if (-1 == ioctl(tty,VT_WAITACTIVE, vts.v_active)) {
315         perror("ioctl VT_WAITACTIVE");
316         return -1;
317     }
318     return 0;
319 }
320 
321 int
322 fb_init(char *device, char *mode, int vt)
323 {
324     char   fbdev[16];
325     struct vt_stat vts;
326 
327     dev_init();
328     tty = 0;
329     if (vt != 0)
330         fb_setvt(vt);
331 
332     if (-1 == ioctl(tty,VT_GETSTATE, &vts)) {
333         fprintf(stderr,"ioctl VT_GETSTATE: %s (not a linux console?)\n",
334                 strerror(errno));
335         exit(1);
336     }
337     
338     if (NULL == device) {
339         device = getenv("FRAMEBUFFER");
340         if (NULL == device) {
341             struct fb_con2fbmap c2m;
342             if (-1 == (fb = open(devices->fb0,O_WRONLY,0))) {
343                 fprintf(stderr,"open %s: %s\n",devices->fb0,strerror(errno));
344                 exit(1);
345             }
346             c2m.console = vts.v_active;
347             if (-1 == ioctl(fb, FBIOGET_CON2FBMAP, &c2m)) {
348                 perror("ioctl FBIOGET_CON2FBMAP");
349                 exit(1);
350             }
351             close(fb);
352             fprintf(stderr,"map: vt%02d => fb%d\n",
353                     c2m.console,c2m.framebuffer);
354             sprintf(fbdev,devices->fbnr,c2m.framebuffer);
355             device = fbdev;
356         }
357     }
358 
359     /* get current settings (which we have to restore) */
360     if (-1 == (fb = open(device,O_RDWR /* O_WRONLY */))) {
361         fprintf(stderr,"open %s: %s\n",device,strerror(errno));
362         exit(1);
363     }
364     if (-1 == ioctl(fb,FBIOGET_VSCREENINFO,&fb_ovar)) {
365         perror("ioctl FBIOGET_VSCREENINFO");
366         exit(1);
367     }
368     if (-1 == ioctl(fb,FBIOGET_FSCREENINFO,&fb_fix)) {
369         perror("ioctl FBIOGET_FSCREENINFO");
370         exit(1);
371     }
372     if (fb_ovar.bits_per_pixel == 8 ||
373         fb_fix.visual == FB_VISUAL_DIRECTCOLOR) {
374         if (-1 == ioctl(fb,FBIOGETCMAP,&ocmap)) {
375             perror("ioctl FBIOGETCMAP");
376             exit(1);
377         }
378     }
379     if (-1 == ioctl(tty,KDGETMODE, &kd_mode)) {
380         perror("ioctl KDGETMODE");
381         exit(1);
382     }
383     if (-1 == ioctl(tty,VT_GETMODE, &vt_omode)) {
384         perror("ioctl VT_GETMODE");
385         exit(1);
386     }
387     tcgetattr(tty, &term);
388     
389     /* switch mode */
390     fb_setmode(mode);
391     
392     /* checks & initialisation */
393     if (-1 == ioctl(fb,FBIOGET_FSCREENINFO,&fb_fix)) {
394         perror("ioctl FBIOGET_FSCREENINFO");
395         exit(1);
396     }
397     if (fb_fix.type != FB_TYPE_PACKED_PIXELS) {
398         fprintf(stderr,"can handle only packed pixel frame buffers\n");
399         goto err;
400     }
401 #if 0
402     switch (fb_var.bits_per_pixel) {
403     case 8:
404         white = 255; black = 0; bpp = 1;
405         break;
406     case 15:
407     case 16:
408         if (fb_var.green.length == 6)
409             white = 0xffff;
410         else
411             white = 0x7fff;
412         black = 0; bpp = 2;
413         break;
414     case 24:
415         white = 0xffffff; black = 0; bpp = fb_var.bits_per_pixel/8;
416         break;
417     case 32:
418         white = 0xffffff; black = 0; bpp = fb_var.bits_per_pixel/8;
419         fb_setpixels = fb_setpixels4;
420         break;
421     default:
422         fprintf(stderr, "Oops: %i bit/pixel ???\n",
423                 fb_var.bits_per_pixel);
424         goto err;
425     }
426 #endif
427     fb_mem_offset = (unsigned long)(fb_fix.smem_start) & (~PAGE_MASK);
428     fb_mem = mmap(NULL,fb_fix.smem_len+fb_mem_offset,
429                   PROT_READ|PROT_WRITE,MAP_SHARED,fb,0);
430     if (-1L == (long)fb_mem) {
431         perror("mmap");
432         goto err;
433     }
434     /* move viewport to upper left corner */
435     if (fb_var.xoffset != 0 || fb_var.yoffset != 0) {
436         fb_var.xoffset = 0;
437         fb_var.yoffset = 0;
438         if (-1 == ioctl(fb,FBIOPAN_DISPLAY,&fb_var)) {
439             perror("ioctl FBIOPAN_DISPLAY");
440             goto err;
441         }
442     }
443     if (-1 == ioctl(tty,KDSETMODE, KD_GRAPHICS)) {
444         perror("ioctl KDSETMODE");
445         goto err;
446     }
447     fb_activate_current(tty);
448 
449     /* cls */
450     fb_memset(fb_mem+fb_mem_offset,0,fb_fix.smem_len);
451     return fb;
452 
453  err:
454     fb_cleanup();
455     exit(1);
456 }
457 
458 void
459 fb_cleanup(void)
460 {
461     /* restore console */
462     if (-1 == ioctl(fb,FBIOPUT_VSCREENINFO,&fb_ovar))
463         perror("ioctl FBIOPUT_VSCREENINFO");
464     if (-1 == ioctl(fb,FBIOGET_FSCREENINFO,&fb_fix))
465         perror("ioctl FBIOGET_FSCREENINFO");
466     if (fb_ovar.bits_per_pixel == 8 ||
467         fb_fix.visual == FB_VISUAL_DIRECTCOLOR) {
468         if (-1 == ioctl(fb,FBIOPUTCMAP,&ocmap))
469             perror("ioctl FBIOPUTCMAP");
470     }
471     close(fb);
472 
473     if (-1 == ioctl(tty,KDSETMODE, kd_mode))
474         perror("ioctl KDSETMODE");
475     if (-1 == ioctl(tty,VT_SETMODE, &vt_omode))
476         perror("ioctl VT_SETMODE");
477     if (orig_vt_no && -1 == ioctl(tty, VT_ACTIVATE, orig_vt_no))
478         perror("ioctl VT_ACTIVATE");
479     if (orig_vt_no && -1 == ioctl(tty, VT_WAITACTIVE, orig_vt_no))
480         perror("ioctl VT_WAITACTIVE");
481     tcsetattr(tty, TCSANOW, &term);
482     close(tty);
483 }
484 
485 /* -------------------------------------------------------------------- */
486 /* handle fatal errors                                                  */
487 
488 static jmp_buf fb_fatal_cleanup;
489 
490 static void
491 fb_catch_exit_signal(int signal)
492 {
493     siglongjmp(fb_fatal_cleanup,signal);
494 }
495 
496 void
497 fb_catch_exit_signals(void)
498 {
499     struct sigaction act,old;
500     int termsig;
501 
502     memset(&act,0,sizeof(act));
503     act.sa_handler = fb_catch_exit_signal;
504     sigemptyset(&act.sa_mask);
505     sigaction(SIGINT, &act,&old);
506     sigaction(SIGQUIT,&act,&old);
507     sigaction(SIGTERM,&act,&old);
508 
509     sigaction(SIGABRT,&act,&old);
510     sigaction(SIGTSTP,&act,&old);
511 
512     sigaction(SIGBUS, &act,&old);
513     sigaction(SIGILL, &act,&old);
514     sigaction(SIGSEGV,&act,&old);
515 
516     if (0 == (termsig = sigsetjmp(fb_fatal_cleanup,0)))
517         return;
518 
519     /* cleanup */
520     fb_cleanup();
521     fprintf(stderr,"Oops: %s\n",sys_siglist[termsig]);
522     exit(42);
523 }
524 
  This page was automatically generated by the LXR engine.