The Design and Implementation of the FreeBSD Operating System, Second Edition
Now available: The Design and Implementation of the FreeBSD Operating System (Second Edition)


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]

FreeBSD/Linux Kernel Cross Reference
sys/pc/screen.c

Version: -  FREEBSD  -  FREEBSD-13-STABLE  -  FREEBSD-13-0  -  FREEBSD-12-STABLE  -  FREEBSD-12-0  -  FREEBSD-11-STABLE  -  FREEBSD-11-0  -  FREEBSD-10-STABLE  -  FREEBSD-10-0  -  FREEBSD-9-STABLE  -  FREEBSD-9-0  -  FREEBSD-8-STABLE  -  FREEBSD-8-0  -  FREEBSD-7-STABLE  -  FREEBSD-7-0  -  FREEBSD-6-STABLE  -  FREEBSD-6-0  -  FREEBSD-5-STABLE  -  FREEBSD-5-0  -  FREEBSD-4-STABLE  -  FREEBSD-3-STABLE  -  FREEBSD22  -  l41  -  OPENBSD  -  linux-2.6  -  MK84  -  PLAN9  -  xnu-8792 
SearchContext: -  none  -  3  -  10 

    1 #include "u.h"
    2 #include "../port/lib.h"
    3 #include "mem.h"
    4 #include "dat.h"
    5 #include "fns.h"
    6 #include "io.h"
    7 #include "ureg.h"
    8 #include "../port/error.h"
    9 
   10 #define Image   IMAGE
   11 #include <draw.h>
   12 #include <memdraw.h>
   13 #include <cursor.h>
   14 #include "screen.h"
   15 
   16 #define RGB2K(r,g,b)    ((156763*(r)+307758*(g)+59769*(b))>>19)
   17 
   18 Point ZP = {0, 0};
   19 
   20 Rectangle physgscreenr;
   21 
   22 Memdata gscreendata;
   23 Memimage *gscreen;
   24 
   25 VGAscr vgascreen[1];
   26 
   27 Cursor  arrow = {
   28         { -1, -1 },
   29         { 0xFF, 0xFF, 0x80, 0x01, 0x80, 0x02, 0x80, 0x0C, 
   30           0x80, 0x10, 0x80, 0x10, 0x80, 0x08, 0x80, 0x04, 
   31           0x80, 0x02, 0x80, 0x01, 0x80, 0x02, 0x8C, 0x04, 
   32           0x92, 0x08, 0x91, 0x10, 0xA0, 0xA0, 0xC0, 0x40, 
   33         },
   34         { 0x00, 0x00, 0x7F, 0xFE, 0x7F, 0xFC, 0x7F, 0xF0, 
   35           0x7F, 0xE0, 0x7F, 0xE0, 0x7F, 0xF0, 0x7F, 0xF8, 
   36           0x7F, 0xFC, 0x7F, 0xFE, 0x7F, 0xFC, 0x73, 0xF8, 
   37           0x61, 0xF0, 0x60, 0xE0, 0x40, 0x40, 0x00, 0x00, 
   38         },
   39 };
   40 
   41 int didswcursorinit;
   42 
   43 int
   44 screensize(int x, int y, int z, ulong chan)
   45 {
   46         VGAscr *scr;
   47 
   48         lock(&vgascreenlock);
   49         memimageinit();
   50         scr = &vgascreen[0];
   51 
   52         /*
   53          * BUG: need to check if any xalloc'ed memory needs to
   54          * be given back if aperture is set.
   55          */
   56         if(scr->paddr == 0){
   57                 int width = (x*z)/BI2WD;
   58 
   59                 gscreendata.bdata = xalloc(width*BY2WD*y);
   60                 if(gscreendata.bdata == 0)
   61                         error("screensize: vga soft memory");
   62 /*              memset(gscreendata.bdata, 0x72, width*BY2WD*y); /* not really black */
   63                 scr->useflush = 1;
   64                 scr->paddr = VGAMEM();
   65                 scr->vaddr = KADDR(scr->paddr);
   66                 scr->apsize = 1<<16;
   67         }
   68         else
   69                 gscreendata.bdata = scr->vaddr;
   70 
   71         if(gscreen)
   72                 freememimage(gscreen);
   73         scr->gscreen = nil;
   74 
   75         gscreen = allocmemimaged(Rect(0,0,x,y), chan, &gscreendata);
   76         vgaimageinit(chan);
   77         if(gscreen == nil){
   78                 unlock(&vgascreenlock);
   79                 return -1;
   80         }
   81 
   82         if(scr->dev && scr->dev->flush)
   83                 scr->useflush = 1;
   84 
   85         scr->palettedepth = 6;  /* default */
   86         scr->gscreendata = &gscreendata;
   87         scr->memdefont = getmemdefont();
   88         scr->gscreen = gscreen;
   89 
   90         physgscreenr = gscreen->r;
   91         unlock(&vgascreenlock);
   92 
   93         if(didswcursorinit)
   94                 swcursorinit();
   95         drawcmap();
   96         return 0;
   97 }
   98 
   99 int
  100 screenaperture(int size, int align)
  101 {
  102         VGAscr *scr;
  103 
  104         scr = &vgascreen[0];
  105 
  106         if(scr->paddr)  /* set up during enable */
  107                 return 0;
  108 
  109         if(size == 0)
  110                 return 0;
  111 
  112         if(scr->dev && scr->dev->linear){
  113                 scr->dev->linear(scr, size, align);
  114                 return 0;
  115         }
  116 
  117         /*
  118          * Need to allocate some physical address space.
  119          * The driver will tell the card to use it.
  120          */
  121         size = PGROUND(size);
  122         scr->paddr = upaalloc(size, align);
  123         if(scr->paddr == 0)
  124                 return -1;
  125         scr->vaddr = vmap(scr->paddr, size);
  126         if(scr->vaddr == nil)
  127                 return -1;
  128         scr->apsize = size;
  129 
  130         return 0;
  131 }
  132 
  133 uchar*
  134 attachscreen(Rectangle* r, ulong* chan, int* d, int* width, int *softscreen)
  135 {
  136         VGAscr *scr;
  137 
  138         scr = &vgascreen[0];
  139         if(scr->gscreen == nil || scr->gscreendata == nil)
  140                 return nil;
  141 
  142         *r = scr->gscreen->clipr;
  143         *chan = scr->gscreen->chan;
  144         *d = scr->gscreen->depth;
  145         *width = scr->gscreen->width;
  146         *softscreen = scr->useflush;
  147 
  148         return scr->gscreendata->bdata;
  149 }
  150 
  151 /*
  152  * It would be fair to say that this doesn't work for >8-bit screens.
  153  */
  154 void
  155 flushmemscreen(Rectangle r)
  156 {
  157         VGAscr *scr;
  158         uchar *sp, *disp, *sdisp, *edisp;
  159         int y, len, incs, off, page;
  160 
  161         scr = &vgascreen[0];
  162         if(scr->dev && scr->dev->flush){
  163                 scr->dev->flush(scr, r);
  164                 return;
  165         }
  166         if(scr->gscreen == nil || scr->useflush == 0)
  167                 return;
  168         if(scr->dev == nil || scr->dev->page == nil)
  169                 return;
  170 
  171         if(rectclip(&r, scr->gscreen->r) == 0)
  172                 return;
  173 
  174         incs = scr->gscreen->width * BY2WD;
  175 
  176         switch(scr->gscreen->depth){
  177         default:
  178                 len = 0;
  179                 panic("flushmemscreen: depth\n");
  180                 break;
  181         case 8:
  182                 len = Dx(r);
  183                 break;
  184         }
  185         if(len < 1)
  186                 return;
  187 
  188         off = r.min.y*scr->gscreen->width*BY2WD+(r.min.x*scr->gscreen->depth)/8;
  189         page = off/scr->apsize;
  190         off %= scr->apsize;
  191         disp = scr->vaddr;
  192         sdisp = disp+off;
  193         edisp = disp+scr->apsize;
  194 
  195         off = r.min.y*scr->gscreen->width*BY2WD+(r.min.x*scr->gscreen->depth)/8;
  196 
  197         sp = scr->gscreendata->bdata + off;
  198 
  199         scr->dev->page(scr, page);
  200         for(y = r.min.y; y < r.max.y; y++) {
  201                 if(sdisp + incs < edisp) {
  202                         memmove(sdisp, sp, len);
  203                         sp += incs;
  204                         sdisp += incs;
  205                 }
  206                 else {
  207                         off = edisp - sdisp;
  208                         page++;
  209                         if(off <= len){
  210                                 if(off > 0)
  211                                         memmove(sdisp, sp, off);
  212                                 scr->dev->page(scr, page);
  213                                 if(len - off > 0)
  214                                         memmove(disp, sp+off, len - off);
  215                         }
  216                         else {
  217                                 memmove(sdisp, sp, len);
  218                                 scr->dev->page(scr, page);
  219                         }
  220                         sp += incs;
  221                         sdisp += incs - scr->apsize;
  222                 }
  223         }
  224 }
  225 
  226 void
  227 getcolor(ulong p, ulong* pr, ulong* pg, ulong* pb)
  228 {
  229         VGAscr *scr;
  230         ulong x;
  231 
  232         scr = &vgascreen[0];
  233         if(scr->gscreen == nil)
  234                 return;
  235 
  236         switch(scr->gscreen->depth){
  237         default:
  238                 x = 0x0F;
  239                 break;
  240         case 8:
  241                 x = 0xFF;
  242                 break;
  243         }
  244         p &= x;
  245 
  246         lock(&cursor);
  247         *pr = scr->colormap[p][0];
  248         *pg = scr->colormap[p][1];
  249         *pb = scr->colormap[p][2];
  250         unlock(&cursor);
  251 }
  252 
  253 int
  254 setpalette(ulong p, ulong r, ulong g, ulong b)
  255 {
  256         VGAscr *scr;
  257         int d;
  258 
  259         scr = &vgascreen[0];
  260         d = scr->palettedepth;
  261 
  262         lock(&cursor);
  263         scr->colormap[p][0] = r;
  264         scr->colormap[p][1] = g;
  265         scr->colormap[p][2] = b;
  266         vgao(PaddrW, p);
  267         vgao(Pdata, r>>(32-d));
  268         vgao(Pdata, g>>(32-d));
  269         vgao(Pdata, b>>(32-d));
  270         unlock(&cursor);
  271 
  272         return ~0;
  273 }
  274 
  275 /*
  276  * On some video cards (e.g. Mach64), the palette is used as the 
  277  * DAC registers for >8-bit modes.  We don't want to set them when the user
  278  * is trying to set a colormap and the card is in one of these modes.
  279  */
  280 int
  281 setcolor(ulong p, ulong r, ulong g, ulong b)
  282 {
  283         VGAscr *scr;
  284         int x;
  285 
  286         scr = &vgascreen[0];
  287         if(scr->gscreen == nil)
  288                 return 0;
  289 
  290         switch(scr->gscreen->depth){
  291         case 1:
  292         case 2:
  293         case 4:
  294                 x = 0x0F;
  295                 break;
  296         case 8:
  297                 x = 0xFF;
  298                 break;
  299         default:
  300                 return 0;
  301         }
  302         p &= x;
  303 
  304         return setpalette(p, r, g, b);
  305 }
  306 
  307 int
  308 cursoron(int dolock)
  309 {
  310         VGAscr *scr;
  311         int v;
  312 
  313         scr = &vgascreen[0];
  314         if(scr->cur == nil || scr->cur->move == nil)
  315                 return 0;
  316 
  317         if(dolock)
  318                 lock(&cursor);
  319         v = scr->cur->move(scr, mousexy());
  320         if(dolock)
  321                 unlock(&cursor);
  322 
  323         return v;
  324 }
  325 
  326 void
  327 cursoroff(int)
  328 {
  329 }
  330 
  331 void
  332 setcursor(Cursor* curs)
  333 {
  334         VGAscr *scr;
  335 
  336         scr = &vgascreen[0];
  337         if(scr->cur == nil || scr->cur->load == nil)
  338                 return;
  339 
  340         scr->cur->load(scr, curs);
  341 }
  342 
  343 int hwaccel = 1;
  344 int hwblank = 0;        /* turned on by drivers that are known good */
  345 int panning = 0;
  346 
  347 int
  348 hwdraw(Memdrawparam *par)
  349 {
  350         VGAscr *scr;
  351         Memimage *dst, *src, *mask;
  352         int m;
  353 
  354         if(hwaccel == 0)
  355                 return 0;
  356 
  357         scr = &vgascreen[0];
  358         if((dst=par->dst) == nil || dst->data == nil)
  359                 return 0;
  360         if((src=par->src) == nil || src->data == nil)
  361                 return 0;
  362         if((mask=par->mask) == nil || mask->data == nil)
  363                 return 0;
  364 
  365         if(scr->cur == &swcursor){
  366                 if(dst->data->bdata == gscreendata.bdata)
  367                         swcursoravoid(par->r);
  368                 if(src->data->bdata == gscreendata.bdata)
  369                         swcursoravoid(par->sr);
  370                 if(mask->data->bdata == gscreendata.bdata)
  371                         swcursoravoid(par->mr);
  372         }
  373         
  374         if(dst->data->bdata != gscreendata.bdata)
  375                 return 0;
  376 
  377         if(scr->fill==nil && scr->scroll==nil)
  378                 return 0;
  379 
  380         /*
  381          * If we have an opaque mask and source is one opaque
  382          * pixel we can convert to the destination format and just
  383          * replicate with memset.
  384          */
  385         m = Simplesrc|Simplemask|Fullmask;
  386         if(scr->fill
  387         && (par->state&m)==m
  388         && ((par->srgba&0xFF) == 0xFF)
  389         && (par->op&S) == S)
  390                 return scr->fill(scr, par->r, par->sdval);
  391 
  392         /*
  393          * If no source alpha, an opaque mask, we can just copy the
  394          * source onto the destination.  If the channels are the same and
  395          * the source is not replicated, memmove suffices.
  396          */
  397         m = Simplemask|Fullmask;
  398         if(scr->scroll
  399         && src->data->bdata==dst->data->bdata
  400         && !(src->flags&Falpha)
  401         && (par->state&m)==m
  402         && (par->op&S) == S)
  403                 return scr->scroll(scr, par->r, par->sr);
  404 
  405         return 0;       
  406 }
  407 
  408 void
  409 blankscreen(int blank)
  410 {
  411         VGAscr *scr;
  412 
  413         scr = &vgascreen[0];
  414         if(hwblank){
  415                 if(scr->blank)
  416                         scr->blank(scr, blank);
  417                 else
  418                         vgablank(scr, blank);
  419         }
  420 }
  421 
  422 void
  423 vgalinearpciid(VGAscr *scr, int vid, int did)
  424 {
  425         Pcidev *p;
  426 
  427         p = nil;
  428         while((p = pcimatch(p, vid, 0)) != nil){
  429                 if(p->ccrb != 3)        /* video card */
  430                         continue;
  431                 if(did != 0 && p->did != did)
  432                         continue;
  433                 break;
  434         }
  435         if(p == nil)
  436                 error("pci video card not found");
  437 
  438         scr->pci = p;
  439         vgalinearpci(scr);
  440 }
  441 
  442 void
  443 vgalinearpci(VGAscr *scr)
  444 {
  445         ulong paddr;
  446         int i, size, best;
  447         Pcidev *p;
  448         
  449         p = scr->pci;
  450         if(p == nil)
  451                 return;
  452 
  453         /*
  454          * Scan for largest memory region on card.
  455          * Some S3 cards (e.g. Savage) have enormous
  456          * mmio regions (but even larger frame buffers).
  457          * Some 3dfx cards (e.g., Voodoo3) have mmio
  458          * buffers the same size as the frame buffer,
  459          * but only the frame buffer is marked as
  460          * prefetchable (bar&8).  If a card doesn't fit
  461          * into these heuristics, its driver will have to
  462          * call vgalinearaddr directly.
  463          */
  464         best = -1;
  465         for(i=0; i<nelem(p->mem); i++){
  466                 if(p->mem[i].bar&1)     /* not memory */
  467                         continue;
  468                 if(p->mem[i].size < 640*480)    /* not big enough */
  469                         continue;
  470                 if(best==-1 
  471                 || p->mem[i].size > p->mem[best].size 
  472                 || (p->mem[i].size == p->mem[best].size 
  473                   && (p->mem[i].bar&8)
  474                   && !(p->mem[best].bar&8)))
  475                         best = i;
  476         }
  477         if(best >= 0){
  478                 paddr = p->mem[best].bar & ~0x0F;
  479                 size = p->mem[best].size;
  480                 vgalinearaddr(scr, paddr, size);
  481                 return;
  482         }
  483         error("no video memory found on pci card");
  484 }
  485 
  486 void
  487 vgalinearaddr(VGAscr *scr, ulong paddr, int size)
  488 {
  489         int x, nsize;
  490         ulong npaddr;
  491 
  492         /*
  493          * new approach.  instead of trying to resize this
  494          * later, let's assume that we can just allocate the
  495          * entire window to start with.
  496          */
  497 
  498         if(scr->paddr == paddr && size <= scr->apsize)
  499                 return;
  500 
  501         if(scr->paddr){
  502                 /*
  503                  * could call vunmap and vmap,
  504                  * but worried about dangling pointers in devdraw
  505                  */
  506                 error("cannot grow vga frame buffer");
  507         }
  508         
  509         /* round to page boundary, just in case */
  510         x = paddr&(BY2PG-1);
  511         npaddr = paddr-x;
  512         nsize = PGROUND(size+x);
  513 
  514         /*
  515          * Don't bother trying to map more than 4000x4000x32 = 64MB.
  516          * We only have a 256MB window.
  517          */
  518         if(nsize > 64*MB)
  519                 nsize = 64*MB;
  520         scr->vaddr = vmap(npaddr, nsize);
  521         if(scr->vaddr == 0)
  522                 error("cannot allocate vga frame buffer");
  523         scr->vaddr = (char*)scr->vaddr+x;
  524         scr->paddr = paddr;
  525         scr->apsize = nsize;
  526 }
  527 
  528 
  529 /*
  530  * Software cursor. 
  531  */
  532 int     swvisible;      /* is the cursor visible? */
  533 int     swenabled;      /* is the cursor supposed to be on the screen? */
  534 Memimage*       swback; /* screen under cursor */
  535 Memimage*       swimg;  /* cursor image */
  536 Memimage*       swmask; /* cursor mask */
  537 Memimage*       swimg1;
  538 Memimage*       swmask1;
  539 
  540 Point   swoffset;
  541 Rectangle       swrect; /* screen rectangle in swback */
  542 Point   swpt;   /* desired cursor location */
  543 Point   swvispt;        /* actual cursor location */
  544 int     swvers; /* incremented each time cursor image changes */
  545 int     swvisvers;      /* the version on the screen */
  546 
  547 /*
  548  * called with drawlock locked for us, most of the time.
  549  * kernel prints at inopportune times might mean we don't
  550  * hold the lock, but memimagedraw is now reentrant so
  551  * that should be okay: worst case we get cursor droppings.
  552  */
  553 void
  554 swcursorhide(void)
  555 {
  556         if(swvisible == 0)
  557                 return;
  558         if(swback == nil)
  559                 return;
  560         swvisible = 0;
  561         memimagedraw(gscreen, swrect, swback, ZP, memopaque, ZP, S);
  562         flushmemscreen(swrect);
  563 }
  564 
  565 void
  566 swcursoravoid(Rectangle r)
  567 {
  568         if(swvisible && rectXrect(r, swrect))
  569                 swcursorhide();
  570 }
  571 
  572 void
  573 swcursordraw(void)
  574 {
  575         if(swvisible)
  576                 return;
  577         if(swenabled == 0)
  578                 return;
  579         if(swback == nil || swimg1 == nil || swmask1 == nil)
  580                 return;
  581         assert(!canqlock(&drawlock));
  582         swvispt = swpt;
  583         swvisvers = swvers;
  584         swrect = rectaddpt(Rect(0,0,16,16), swvispt);
  585         memimagedraw(swback, swback->r, gscreen, swpt, memopaque, ZP, S);
  586         memimagedraw(gscreen, swrect, swimg1, ZP, swmask1, ZP, SoverD);
  587         flushmemscreen(swrect);
  588         swvisible = 1;
  589 }
  590 
  591 /*
  592  * Need to lock drawlock for ourselves.
  593  */
  594 void
  595 swenable(VGAscr*)
  596 {
  597         swenabled = 1;
  598         if(canqlock(&drawlock)){
  599                 swcursordraw();
  600                 qunlock(&drawlock);
  601         }
  602 }
  603 
  604 void
  605 swdisable(VGAscr*)
  606 {
  607         swenabled = 0;
  608         if(canqlock(&drawlock)){
  609                 swcursorhide();
  610                 qunlock(&drawlock);
  611         }
  612 }
  613 
  614 void
  615 swload(VGAscr*, Cursor *curs)
  616 {
  617         uchar *ip, *mp;
  618         int i, j, set, clr;
  619 
  620         if(!swimg || !swmask || !swimg1 || !swmask1)
  621                 return;
  622         /*
  623          * Build cursor image and mask.
  624          * Image is just the usual cursor image
  625          * but mask is a transparent alpha mask.
  626          * 
  627          * The 16x16x8 memimages do not have
  628          * padding at the end of their scan lines.
  629          */
  630         ip = byteaddr(swimg, ZP);
  631         mp = byteaddr(swmask, ZP);
  632         for(i=0; i<32; i++){
  633                 set = curs->set[i];
  634                 clr = curs->clr[i];
  635                 for(j=0x80; j; j>>=1){
  636                         *ip++ = set&j ? 0x00 : 0xFF;
  637                         *mp++ = (clr|set)&j ? 0xFF : 0x00;
  638                 }
  639         }
  640         swoffset = curs->offset;
  641         swvers++;
  642         memimagedraw(swimg1, swimg1->r, swimg, ZP, memopaque, ZP, S);
  643         memimagedraw(swmask1, swmask1->r, swmask, ZP, memopaque, ZP, S);
  644 }
  645 
  646 int
  647 swmove(VGAscr*, Point p)
  648 {
  649         swpt = addpt(p, swoffset);
  650         return 0;
  651 }
  652 
  653 void
  654 swcursorclock(void)
  655 {
  656         int x;
  657 
  658         if(!swenabled)
  659                 return;
  660         if(swvisible && eqpt(swpt, swvispt) && swvers==swvisvers)
  661                 return;
  662 
  663         x = splhi();
  664         if(swenabled)
  665         if(!swvisible || !eqpt(swpt, swvispt) || swvers!=swvisvers)
  666         if(canqlock(&drawlock)){
  667                 swcursorhide();
  668                 swcursordraw();
  669                 qunlock(&drawlock);
  670         }
  671         splx(x);
  672 }
  673 
  674 void
  675 swcursorinit(void)
  676 {
  677         static int init, warned;
  678         VGAscr *scr;
  679 
  680         didswcursorinit = 1;
  681         if(!init){
  682                 init = 1;
  683                 addclock0link(swcursorclock, 10);
  684         }
  685         scr = &vgascreen[0];
  686         if(scr==nil || scr->gscreen==nil)
  687                 return;
  688 
  689         if(scr->dev == nil || scr->dev->linear == nil){
  690                 if(!warned){
  691                         print("cannot use software cursor on non-linear vga screen\n");
  692                         warned = 1;
  693                 }
  694                 return;
  695         }
  696 
  697         if(swback){
  698                 freememimage(swback);
  699                 freememimage(swmask);
  700                 freememimage(swmask1);
  701                 freememimage(swimg);
  702                 freememimage(swimg1);
  703         }
  704 
  705         swback = allocmemimage(Rect(0,0,32,32), gscreen->chan);
  706         swmask = allocmemimage(Rect(0,0,16,16), GREY8);
  707         swmask1 = allocmemimage(Rect(0,0,16,16), GREY1);
  708         swimg = allocmemimage(Rect(0,0,16,16), GREY8);
  709         swimg1 = allocmemimage(Rect(0,0,16,16), GREY1);
  710         if(swback==nil || swmask==nil || swmask1==nil || swimg==nil || swimg1 == nil){
  711                 print("software cursor: allocmemimage fails");
  712                 return;
  713         }
  714 
  715         memfillcolor(swmask, DOpaque);
  716         memfillcolor(swmask1, DOpaque);
  717         memfillcolor(swimg, DBlack);
  718         memfillcolor(swimg1, DBlack);
  719 }
  720 
  721 VGAcur swcursor =
  722 {
  723         "soft",
  724         swenable,
  725         swdisable,
  726         swload,
  727         swmove,
  728 };
  729 

Cache object: 6e517b02b2766811d8ae6cb76057cd9c


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]


This page is part of the FreeBSD/Linux Linux Kernel Cross-Reference, and was automatically generated using a modified version of the LXR engine.