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/netgraph/ng_device.c

Version: -  FREEBSD  -  FREEBSD-12-STABLE  -  FREEBSD-12-0  -  FREEBSD-11-STABLE  -  FREEBSD-11-2  -  FREEBSD-11-1  -  FREEBSD-11-0  -  FREEBSD-10-STABLE  -  FREEBSD-10-4  -  FREEBSD-10-3  -  FREEBSD-10-2  -  FREEBSD-10-1  -  FREEBSD-10-0  -  FREEBSD-9-STABLE  -  FREEBSD-9-3  -  FREEBSD-9-2  -  FREEBSD-9-1  -  FREEBSD-9-0  -  FREEBSD-8-STABLE  -  FREEBSD-8-4  -  FREEBSD-8-3  -  FREEBSD-8-2  -  FREEBSD-8-1  -  FREEBSD-8-0  -  FREEBSD-7-STABLE  -  FREEBSD-7-4  -  FREEBSD-7-3  -  FREEBSD-7-2  -  FREEBSD-7-1  -  FREEBSD-7-0  -  FREEBSD-6-STABLE  -  FREEBSD-6-4  -  FREEBSD-6-3  -  FREEBSD-6-2  -  FREEBSD-6-1  -  FREEBSD-6-0  -  FREEBSD-5-STABLE  -  FREEBSD-5-5  -  FREEBSD-5-4  -  FREEBSD-5-3  -  FREEBSD-5-2  -  FREEBSD-5-1  -  FREEBSD-5-0  -  FREEBSD-4-STABLE  -  FREEBSD-3-STABLE  -  FREEBSD22  -  linux-2.6  -  linux-2.4.22  -  MK83  -  MK84  -  PLAN9  -  DFBSD  -  NETBSD  -  NETBSD5  -  NETBSD4  -  NETBSD3  -  NETBSD20  -  OPENBSD  -  xnu-517  -  xnu-792  -  xnu-792.6.70  -  xnu-1228  -  xnu-1456.1.26  -  xnu-1699.24.8  -  xnu-2050.18.24  -  OPENSOLARIS  -  minix-3-1-1 
SearchContext: -  none  -  3  -  10 

    1 /*
    2  * Copyright (c) 2002 Mark Santcroos <marks@ripe.net>
    3  *
    4  * Redistribution and use in source and binary forms, with or without
    5  * modification, are permitted provided that the following conditions
    6  * are met:
    7  * 1. Redistributions of source code must retain the above copyright
    8  *    notice, this list of conditions and the following disclaimer.
    9  * 2. Redistributions in binary form must reproduce the above copyright
   10  *    notice, this list of conditions and the following disclaimer in the
   11  *    documentation and/or other materials provided with the distribution.
   12  * 3. The name of the author may not be used to endorse or promote products
   13  *    derived from this software without specific prior written permission.
   14  *
   15  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
   16  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
   17  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
   18  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
   19  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
   20  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
   21  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
   22  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
   23  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
   24  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
   25  *
   26  * Netgraph "device" node
   27  *
   28  * This node presents a /dev/ngd%d device that interfaces to an other 
   29  * netgraph node.
   30  *
   31  * $FreeBSD: stable/4/sys/netgraph/ng_device.c 102308 2002-08-23 07:15:44Z julian $
   32  *
   33  */
   34 
   35 #include <sys/param.h>
   36 #include <sys/systm.h>
   37 #include <sys/kernel.h>
   38 #include <sys/mbuf.h>
   39 #include <sys/uio.h>
   40 #include <sys/queue.h>
   41 #include <sys/malloc.h>
   42 #include <sys/conf.h>
   43 #include <sys/poll.h>
   44 #include <sys/ioccom.h>
   45 
   46 #include <netgraph/ng_message.h>
   47 #include <netgraph/netgraph.h>
   48 
   49 #include "ng_device.h"
   50 
   51 /* turn this on for verbose messages */
   52 #define NGD_DEBUG
   53 
   54 /* Netgraph methods */
   55 static ng_constructor_t ng_device_cons;
   56 static ng_rcvmsg_t      ng_device_rcvmsg;
   57 static ng_newhook_t     ng_device_newhook;
   58 static ng_connect_t     ng_device_connect;
   59 static ng_rcvdata_t     ng_device_rcvdata;
   60 static ng_disconnect_t  ng_device_disconnect;
   61 static int              ng_device_mod_event(module_t mod, int event, void *data);
   62 
   63 static int ng_device_init(void);
   64 static int get_free_unit(void);
   65 
   66 /* Netgraph type */
   67 static struct ng_type typestruct = {
   68         NG_VERSION,                     /* version */
   69         NG_DEVICE_NODE_TYPE,            /* name */
   70         ng_device_mod_event,            /* modevent */
   71         ng_device_cons,                 /* constructor */
   72         ng_device_rcvmsg,               /* receive msg */
   73         NULL,                           /* shutdown */
   74         ng_device_newhook,              /* newhook */
   75         NULL,                           /* findhook */
   76         ng_device_connect,              /* connect */
   77         ng_device_rcvdata,              /* receive data */
   78         ng_device_rcvdata,              /* receive queued data */
   79         ng_device_disconnect,           /* disconnect */
   80         NULL
   81 };
   82 NETGRAPH_INIT(device, &typestruct);
   83 
   84 /* per hook data */
   85 struct ngd_connection {
   86         SLIST_ENTRY(ngd_connection) links;
   87 
   88         dev_t   ngddev;
   89         struct  ng_hook *active_hook;
   90         char    *readq;
   91         int     loc;
   92         int     unit;
   93 };
   94 
   95 /* global data */
   96 struct ngd_softc {
   97         SLIST_HEAD(, ngd_connection) head;
   98 
   99         node_p node;
  100         char nodename[NG_NODELEN + 1];
  101 } ngd_softc;
  102 
  103 /* helper definition */
  104 #define         MIN(a, b)       ((a) < (b) ? (a) : (b))
  105 
  106 /* the per connection receiving queue maximum */
  107 #define NGD_QUEUE_SIZE (1024*10)
  108 
  109 /* Maximum number of NGD devices */
  110 #define MAX_NGD 25              /* should be more than enough for now */
  111 
  112 static d_close_t ngdclose;
  113 static d_open_t ngdopen;
  114 static d_read_t ngdread;
  115 static d_write_t ngdwrite;
  116 static d_ioctl_t ngdioctl;
  117 static d_poll_t ngdpoll;
  118 
  119 #define NGD_CDEV_MAJOR 20
  120 static struct cdevsw ngd_cdevsw = {
  121         /* open */      ngdopen,
  122         /* close */     ngdclose,
  123         /* read */      ngdread,
  124         /* write */     ngdwrite,
  125         /* ioctl */     ngdioctl,
  126         /* poll */      ngdpoll,
  127         /* mmap */      nommap,
  128         /* strategy */  nostrategy,
  129         /* name */      "ngd",
  130         /* maj */       NGD_CDEV_MAJOR,
  131         /* dump */      nodump,
  132         /* psize */     nopsize,
  133         /* flags */     0,
  134         /* bmaj */      -1
  135 };
  136 
  137 /* 
  138  * this holds all the stuff that should be done at load time 
  139  */
  140 static int
  141 ng_device_mod_event(module_t mod, int event, void *data)
  142 {
  143         int error = 0;
  144 
  145 #ifdef NGD_DEBUG
  146         printf("%s()\n", __func__);
  147 #endif /* NGD_DEBUG */
  148 
  149         switch (event) {
  150                 case MOD_LOAD:
  151                         ng_device_init();
  152                         break;
  153 
  154                 case MOD_UNLOAD:
  155                         /* XXX do we need to do something specific ? */
  156                         /* ng_device_breakdown */
  157                         /* cdevsw_remove(&ngd_cdevsw);*/
  158                         error = EBUSY; /* no unload! */
  159                         break;
  160 
  161                 
  162                 default:
  163                         error = EOPNOTSUPP;
  164                         break;
  165         }
  166 
  167         return(error);
  168 }
  169 
  170 
  171 static int
  172 ng_device_init()
  173 {
  174         struct ngd_softc *sc = &ngd_softc;
  175         
  176 #ifdef NGD_DEBUG
  177         printf("%s()\n", __func__);
  178 #endif /* NGD_DEBUG */ 
  179 
  180         SLIST_INIT(&sc->head);
  181 
  182         if (ng_make_node_common(&typestruct, &sc->node) != 0) {
  183                 printf("%s(): ng_make_node_common failed\n", __func__);
  184                 return(ENXIO);
  185         }
  186         sprintf(sc->nodename, "%s", NG_DEVICE_NODE_TYPE);
  187         if (ng_name_node(sc->node, sc->nodename)) {
  188                 NG_NODE_UNREF(sc->node); /* make it go away again */
  189                 printf("%s(): ng_name_node failed\n", __func__);
  190                 return(ENXIO);
  191         }
  192         NG_NODE_SET_PRIVATE(sc->node, sc);
  193         cdevsw_add(&ngd_cdevsw);
  194 
  195         return(0);
  196 }
  197 
  198 /* 
  199  * don't allow to be created, only the device can do that 
  200  */
  201 static int
  202 ng_device_cons(node_p node)
  203 {
  204 
  205 #ifdef NGD_DEBUG
  206         printf("%s()\n", __func__);
  207 #endif /* NGD_DEBUG */
  208         
  209         return(EINVAL);
  210 }
  211 
  212 /*
  213  * Receive control message. We just  free it.
  214  */
  215 static int
  216 ng_device_rcvmsg(node_p node, 
  217         struct ng_mesg *msg, const char *retaddr, struct ng_mesg **rptr)
  218 {
  219         FREE(msg, M_NETGRAPH);
  220         return(ENOTTY);
  221 }
  222 
  223 static int
  224 get_free_unit()
  225 {
  226         struct ngd_connection *tmp = NULL;
  227         struct ngd_softc *sc = &ngd_softc;
  228         int n = 0;
  229         int unit = -1;
  230 
  231 #ifdef NGD_DEBUG
  232         printf("%s()\n", __func__);
  233 #endif /* NGD_DEBUG */
  234 
  235         /* When there is no list yet, the first device unit is always 0. */
  236         if SLIST_EMPTY(&sc->head) {
  237                 unit = 0;
  238                 return(unit);
  239         }
  240 
  241         /* Just do a brute force loop to find the first free unit that is
  242          * smaller than MAX_NGD.
  243          * Set MAX_NGD to a large value, doesn't impact performance.
  244          */
  245         for(n = 0;n<MAX_NGD && unit == -1;n++) {
  246                 SLIST_FOREACH(tmp, &sc->head, links) {
  247 
  248                         if(tmp->unit == n) {
  249                                 unit = -1;
  250                                 break;
  251                         }
  252                         unit = n;
  253                 }
  254         }
  255 
  256         return(unit);
  257 }
  258 
  259 /*
  260  * incoming hook
  261  */
  262 static int
  263 ng_device_newhook(node_p node, hook_p hook, const char *name)
  264 {
  265         struct ngd_softc *sc = &ngd_softc;
  266         struct ngd_connection * new_connection = NULL;
  267 
  268 #ifdef NGD_DEBUG
  269         printf("%s()\n", __func__);
  270 #endif /* NGD_DEBUG */
  271 
  272         new_connection = malloc(sizeof(struct ngd_connection), M_DEVBUF, M_NOWAIT);
  273         if(new_connection == NULL) {
  274                 printf("%s(): ERROR: new_connection == NULL\n", __func__);
  275                 return(-1);
  276         }
  277 
  278         new_connection->unit = get_free_unit();
  279         if(new_connection->unit<0) {
  280                 printf("%s: No free unit found by get_free_unit(), "
  281                                 "increas MAX_NGD\n", __func__);
  282                 free(new_connection, M_DEVBUF);
  283                 return(-1);
  284         }
  285         new_connection->ngddev = make_dev(&ngd_cdevsw,
  286             new_connection->unit, 0, 0, 0600, "ngd%d", new_connection->unit);
  287         if(new_connection->ngddev == NULL) {
  288                 printf("%s(): make_dev failed\n", __func__);
  289                 SLIST_REMOVE(&sc->head, new_connection, ngd_connection, links);
  290                 free(new_connection, M_DEVBUF);
  291                 return(-1);
  292         }
  293 
  294         new_connection->readq =
  295             malloc(sizeof(char)*NGD_QUEUE_SIZE, M_DEVBUF, M_NOWAIT | M_ZERO);
  296         if(new_connection->readq == NULL) {
  297                 printf("%s(): readq malloc failed\n", __func__);
  298                 destroy_dev(new_connection->ngddev);
  299                 SLIST_REMOVE(&sc->head, new_connection, ngd_connection, links);
  300                 free(new_connection, M_DEVBUF);
  301                 return(-1);
  302         }
  303 
  304         /* point to begin of buffer */
  305         new_connection->loc = 0;
  306         new_connection->active_hook = hook;
  307 
  308         SLIST_INSERT_HEAD(&sc->head, new_connection, links);
  309 
  310         return(0);
  311 }
  312 
  313 /*
  314  * we gave ok to a new hook
  315  * now connect
  316  */
  317 static int
  318 ng_device_connect(hook_p hook)
  319 {
  320 
  321 #ifdef NGD_DEBUG
  322         printf("%s()\n", __func__);
  323 #endif /* NGD_DEBUG */
  324 
  325         return(0);
  326 }
  327 
  328 
  329 /*
  330  * Receive data from hook
  331  */
  332 static int
  333 ng_device_rcvdata(hook_p hook, struct mbuf *m, meta_p meta)
  334 {
  335         struct ngd_softc *sc = &ngd_softc;
  336         struct ngd_connection * connection = NULL;
  337         struct ngd_connection * tmp;
  338         char *buffer;
  339 
  340 #ifdef NGD_DEBUG
  341         printf("%s()\n", __func__);
  342 #endif /* NGD_DEBUG */
  343 
  344         SLIST_FOREACH(tmp, &sc->head, links) {
  345                 if(tmp->active_hook == hook) {
  346                         connection = tmp;
  347                 }
  348         }
  349         if(connection == NULL) {
  350                 printf("%s(): connection still NULL, no hook found\n", __func__);
  351                 return(-1);
  352         }
  353 
  354         NG_FREE_META(meta);
  355 
  356         m = m_pullup(m, m->m_len);
  357         if(m == NULL) {
  358                 printf("%s(): ERROR: m_pullup failed\n", __func__);
  359                 return(-1);
  360         }
  361 
  362         buffer = malloc(sizeof(char)*m->m_len, M_DEVBUF, M_NOWAIT | M_ZERO);
  363         if(buffer == NULL) {
  364                 printf("%s(): ERROR: buffer malloc failed\n", __func__);
  365                 return(-1);
  366         }
  367 
  368         buffer = mtod(m, char *);
  369 
  370         if( (connection->loc+m->m_len) < NGD_QUEUE_SIZE) {
  371                 memcpy(connection->readq+connection->loc, buffer, m->m_len);
  372                 connection->loc += m->m_len;
  373         } else
  374                 printf("%s(): queue full, first read out a bit\n", __func__);
  375 
  376         free(buffer, M_DEVBUF);
  377 
  378         return(0);
  379 }
  380 
  381 /*
  382  * Removal of the last link destroys the node
  383  */
  384 static int
  385 ng_device_disconnect(hook_p hook)
  386 {
  387         struct ngd_softc *sc = &ngd_softc;
  388         struct ngd_connection * connection = NULL;
  389         struct ngd_connection * tmp;
  390 
  391 #ifdef NGD_DEBUG
  392         printf("%s()\n", __func__);
  393 #endif /* NGD_DEBUG */
  394 
  395         SLIST_FOREACH(tmp, &sc->head, links) {
  396                 if(tmp->active_hook == hook) {
  397                         connection = tmp;
  398                 }
  399         }
  400         if(connection == NULL) {
  401                 printf("%s(): connection still NULL, no hook found\n",
  402                     __func__);
  403                 return(-1);
  404         }
  405 
  406         free(connection->readq, M_DEVBUF);
  407 
  408         destroy_dev(connection->ngddev);
  409 
  410         SLIST_REMOVE(&sc->head, connection, ngd_connection, links);
  411 
  412         return(0);
  413 }
  414 /*
  415  * the device is opened 
  416  */
  417 static int
  418 ngdopen(dev_t dev, int flag, int mode, struct thread *td)
  419 {
  420 
  421 #ifdef NGD_DEBUG
  422         printf("%s()\n", __func__);
  423 #endif /* NGD_DEBUG */
  424 
  425         return(0);
  426 }
  427 
  428 /*
  429  * the device is closed 
  430  */
  431 static int
  432 ngdclose(dev_t dev, int flag, int mode, struct thread *td)
  433 {
  434 
  435 #ifdef NGD_DEBUG
  436         printf("%s()\n", __func__);
  437 #endif
  438 
  439         return(0);
  440 }
  441 
  442 
  443 /*
  444  * process ioctl
  445  *
  446  * they are translated into netgraph messages and passed on
  447  * 
  448  */
  449 static int
  450 ngdioctl(dev_t dev, u_long cmd, caddr_t addr, int flag, struct thread *td)
  451 {
  452         struct ngd_softc *sc = &ngd_softc;
  453         struct ngd_connection * connection = NULL;
  454         struct ngd_connection * tmp;
  455         int error = 0;
  456         struct ng_mesg *msg;
  457         struct ngd_param_s * datap;
  458 
  459 #ifdef NGD_DEBUG
  460         printf("%s()\n", __func__);
  461 #endif /* NGD_DEBUG */
  462 
  463         SLIST_FOREACH(tmp, &sc->head, links) {
  464                 if(tmp->ngddev == dev) {
  465                         connection = tmp;
  466                 }
  467         }
  468         if(connection == NULL) {
  469                 printf("%s(): connection still NULL, no dev found\n",
  470                     __func__);
  471                 return(-1);
  472         }
  473 
  474         /* NG_MKMESSAGE(msg, cookie, cmdid, len, how) */
  475         NG_MKMESSAGE(msg, NGM_DEVICE_COOKIE, cmd, sizeof(struct ngd_param_s), 
  476                         M_NOWAIT);
  477         if (msg == NULL) {
  478                 printf("%s(): msg == NULL\n", __func__);
  479                 goto nomsg;
  480         }
  481 
  482         /* pass the ioctl data into the ->data area */
  483         datap = (struct ngd_param_s *)msg->data;
  484         datap->p = addr;
  485 
  486         /*ng_send_msg(node_p here, struct ng_mesg *msg,
  487                     const char *address, struct ng_mesg **resp); */
  488         error = ng_send_msg(sc->node, msg,
  489             NG_HOOK_NAME(connection->active_hook), NULL);
  490         if(error)
  491                 printf("%s(): ng_send_msg() error: %d\n", __func__, error);
  492 
  493 nomsg:
  494 
  495         return(0);
  496 }
  497 
  498 
  499 /*
  500  * This function is called when a read(2) is done to our device.
  501  * We pass the data available in kernelspace on into userland using
  502  * uiomove.
  503  */
  504 static int
  505 ngdread(dev_t dev, struct uio *uio, int flag)
  506 {
  507         int ret = 0, amnt;
  508         char buffer[uio->uio_resid+1];
  509         struct ngd_softc *sc = &ngd_softc;
  510         struct ngd_connection * connection = NULL;
  511         struct ngd_connection * tmp;
  512 
  513 #ifdef NGD_DEBUG
  514         printf("%s()\n", __func__);
  515 #endif /* NGD_DEBUG */
  516 
  517         SLIST_FOREACH(tmp, &sc->head, links) {
  518                 if(tmp->ngddev == dev) {
  519                         connection = tmp;
  520                 }
  521         }
  522         if(connection == NULL) {
  523                 printf("%s(): connection still NULL, no dev found\n", __func__);
  524                 return(-1);
  525         }
  526 
  527         while ( ( uio->uio_resid > 0 ) && ( connection->loc > 0 ) ) {
  528                 amnt = MIN(uio->uio_resid, connection->loc);
  529 
  530                 memcpy(buffer, connection->readq, amnt);
  531                 memcpy(connection->readq, connection->readq+amnt,
  532                                 connection->loc-amnt);
  533                 connection->loc -= amnt;
  534 
  535                 ret = uiomove((caddr_t)buffer, amnt, uio);
  536                 if(ret != 0)
  537                         goto error;
  538 
  539         }
  540         return(0);
  541 
  542 error:
  543         printf("%s(): uiomove returns error %d\n", __func__, ret);
  544         /* do error cleanup here */
  545         return(ret);
  546 }
  547 
  548 
  549 /* 
  550  * This function is called when our device is written to.
  551  * We read the data from userland into our local buffer and pass it on
  552  * into the remote hook.
  553  *
  554  */
  555 static int
  556 ngdwrite(dev_t dev, struct uio *uio, int flag)
  557 {
  558         int ret;
  559         int error = 0;
  560         struct mbuf *m;
  561         char buffer[uio->uio_resid];
  562         int len = uio->uio_resid;
  563         struct ngd_softc *sc =& ngd_softc;
  564         struct ngd_connection * connection = NULL;
  565         struct ngd_connection * tmp;
  566 
  567 #ifdef NGD_DEBUG
  568         printf("%s()\n", __func__);
  569 #endif /* NGD_DEBUG */
  570 
  571         SLIST_FOREACH(tmp, &sc->head, links) {
  572                 if(tmp->ngddev == dev) {
  573                         connection = tmp;
  574                 }
  575         }
  576 
  577         if(connection == NULL) {
  578                 printf("%s(): connection still NULL, no dev found\n", __func__);
  579                 return(-1);
  580         }
  581 
  582         if (len > 0) {
  583                 if ((ret = uiomove((caddr_t)buffer, len, uio)) != 0)
  584                         goto error;
  585         } else
  586                 printf("%s(): len <= 0 : supposed to happen?!\n", __func__);
  587 
  588         m = m_devget(buffer, len, 0, NULL, NULL);
  589 
  590         NG_SEND_DATA_ONLY(error, connection->active_hook, m);
  591 
  592         return(0);
  593 
  594 error:
  595         /* do error cleanup here */
  596         printf("%s(): uiomove returned err: %d\n", __func__, ret);
  597 
  598         return(ret);
  599 }
  600 
  601 /*
  602  * we are being polled/selected
  603  * check if there is data available for read
  604  */
  605 static int
  606 ngdpoll(dev_t dev, int events, struct thread *td)
  607 {
  608         int revents = 0;
  609         struct ngd_softc *sc = &ngd_softc;
  610         struct ngd_connection * connection = NULL;
  611         struct ngd_connection * tmp;
  612 
  613 
  614         if (events & (POLLIN | POLLRDNORM)) {
  615                 /* get the connection we have to know the loc from */
  616                 SLIST_FOREACH(tmp, &sc->head, links) {
  617                         if(tmp->ngddev == dev) {
  618                                 connection = tmp;
  619                         }
  620                 }
  621                 if(connection == NULL) {
  622                         printf("%s(): ERROR: connection still NULL, "
  623                                 "no dev found\n", __func__);
  624                         return(-1);
  625                 }
  626 
  627                 if (connection->loc > 0)
  628                         revents |= events & (POLLIN | POLLRDNORM);
  629         }
  630 
  631         return(revents);
  632 }

Cache object: 834beb292ac765924dbdb16f19756ccf


[ 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.