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/net80211/ieee80211_proto.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 /*-
    2  * Copyright (c) 2001 Atsushi Onoe
    3  * Copyright (c) 2002, 2003 Sam Leffler, Errno Consulting
    4  * All rights reserved.
    5  *
    6  * Redistribution and use in source and binary forms, with or without
    7  * modification, are permitted provided that the following conditions
    8  * are met:
    9  * 1. Redistributions of source code must retain the above copyright
   10  *    notice, this list of conditions and the following disclaimer.
   11  * 2. Redistributions in binary form must reproduce the above copyright
   12  *    notice, this list of conditions and the following disclaimer in the
   13  *    documentation and/or other materials provided with the distribution.
   14  * 3. The name of the author may not be used to endorse or promote products
   15  *    derived from this software without specific prior written permission.
   16  *
   17  * Alternatively, this software may be distributed under the terms of the
   18  * GNU General Public License ("GPL") version 2 as published by the Free
   19  * Software Foundation.
   20  *
   21  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
   22  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
   23  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
   24  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
   25  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
   26  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
   27  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
   28  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
   29  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
   30  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
   31  */
   32 
   33 #include <sys/cdefs.h>
   34 __FBSDID("$FreeBSD: releng/5.2/sys/net80211/ieee80211_proto.c 121816 2003-10-31 18:32:15Z brooks $");
   35 
   36 /*
   37  * IEEE 802.11 protocol support.
   38  */
   39 
   40 #include "opt_inet.h"
   41 
   42 #include <sys/param.h>
   43 #include <sys/systm.h> 
   44 #include <sys/mbuf.h>   
   45 #include <sys/malloc.h>
   46 #include <sys/kernel.h>
   47 #include <sys/socket.h>
   48 #include <sys/sockio.h>
   49 #include <sys/endian.h>
   50 #include <sys/errno.h>
   51 #include <sys/bus.h>
   52 #include <sys/proc.h>
   53 #include <sys/sysctl.h>
   54 
   55 #include <machine/atomic.h>
   56  
   57 #include <net/if.h>
   58 #include <net/if_dl.h>
   59 #include <net/if_media.h>
   60 #include <net/if_arp.h>
   61 #include <net/ethernet.h>
   62 #include <net/if_llc.h>
   63 
   64 #include <net80211/ieee80211_var.h>
   65 
   66 #include <net/bpf.h>
   67 
   68 #ifdef INET
   69 #include <netinet/in.h> 
   70 #include <netinet/if_ether.h>
   71 #endif
   72 
   73 #define IEEE80211_RATE2MBS(r)   (((r) & IEEE80211_RATE_VAL) / 2)
   74 
   75 const char *ieee80211_mgt_subtype_name[] = {
   76         "assoc_req",    "assoc_resp",   "reassoc_req",  "reassoc_resp",
   77         "probe_req",    "probe_resp",   "reserved#6",   "reserved#7",
   78         "beacon",       "atim",         "disassoc",     "auth",
   79         "deauth",       "reserved#13",  "reserved#14",  "reserved#15"
   80 };
   81 const char *ieee80211_state_name[IEEE80211_S_MAX] = {
   82         "INIT",         /* IEEE80211_S_INIT */
   83         "SCAN",         /* IEEE80211_S_SCAN */
   84         "AUTH",         /* IEEE80211_S_AUTH */
   85         "ASSOC",        /* IEEE80211_S_ASSOC */
   86         "RUN"           /* IEEE80211_S_RUN */
   87 };
   88 
   89 static int ieee80211_newstate(struct ieee80211com *, enum ieee80211_state, int);
   90 
   91 void
   92 ieee80211_proto_attach(struct ifnet *ifp)
   93 {
   94         struct ieee80211com *ic = (void *)ifp;
   95 
   96         ifp->if_hdrlen = sizeof(struct ieee80211_frame);
   97 
   98 #ifdef notdef
   99         ic->ic_rtsthreshold = IEEE80211_RTS_DEFAULT;
  100 #else
  101         ic->ic_rtsthreshold = IEEE80211_RTS_MAX;
  102 #endif
  103         ic->ic_fragthreshold = 2346;            /* XXX not used yet */
  104         ic->ic_fixed_rate = -1;                 /* no fixed rate */
  105 
  106         mtx_init(&ic->ic_mgtq.ifq_mtx, ifp->if_xname, "mgmt send q", MTX_DEF);
  107 
  108         /* protocol state change handler */
  109         ic->ic_newstate = ieee80211_newstate;
  110 
  111         /* initialize management frame handlers */
  112         ic->ic_recv_mgmt = ieee80211_recv_mgmt;
  113         ic->ic_send_mgmt = ieee80211_send_mgmt;
  114 }
  115 
  116 void
  117 ieee80211_proto_detach(struct ifnet *ifp)
  118 {
  119         struct ieee80211com *ic = (void *)ifp;
  120 
  121         IF_DRAIN(&ic->ic_mgtq);
  122         mtx_destroy(&ic->ic_mgtq.ifq_mtx);
  123 }
  124 
  125 void
  126 ieee80211_print_essid(u_int8_t *essid, int len)
  127 {
  128         int i;
  129         u_int8_t *p; 
  130 
  131         if (len > IEEE80211_NWID_LEN)
  132                 len = IEEE80211_NWID_LEN;
  133         /* determine printable or not */
  134         for (i = 0, p = essid; i < len; i++, p++) {
  135                 if (*p < ' ' || *p > 0x7e)
  136                         break;
  137         }
  138         if (i == len) {
  139                 printf("\"");
  140                 for (i = 0, p = essid; i < len; i++, p++)
  141                         printf("%c", *p);
  142                 printf("\"");
  143         } else {
  144                 printf("0x");
  145                 for (i = 0, p = essid; i < len; i++, p++)
  146                         printf("%02x", *p);
  147         }
  148 }
  149 
  150 void
  151 ieee80211_dump_pkt(u_int8_t *buf, int len, int rate, int rssi)
  152 {
  153         struct ieee80211_frame *wh;
  154         int i;
  155 
  156         wh = (struct ieee80211_frame *)buf;
  157         switch (wh->i_fc[1] & IEEE80211_FC1_DIR_MASK) {
  158         case IEEE80211_FC1_DIR_NODS:
  159                 printf("NODS %s", ether_sprintf(wh->i_addr2));
  160                 printf("->%s", ether_sprintf(wh->i_addr1));
  161                 printf("(%s)", ether_sprintf(wh->i_addr3));
  162                 break;
  163         case IEEE80211_FC1_DIR_TODS:
  164                 printf("TODS %s", ether_sprintf(wh->i_addr2));
  165                 printf("->%s", ether_sprintf(wh->i_addr3));
  166                 printf("(%s)", ether_sprintf(wh->i_addr1));
  167                 break;
  168         case IEEE80211_FC1_DIR_FROMDS:
  169                 printf("FRDS %s", ether_sprintf(wh->i_addr3));
  170                 printf("->%s", ether_sprintf(wh->i_addr1));
  171                 printf("(%s)", ether_sprintf(wh->i_addr2));
  172                 break;
  173         case IEEE80211_FC1_DIR_DSTODS:
  174                 printf("DSDS %s", ether_sprintf((u_int8_t *)&wh[1]));
  175                 printf("->%s", ether_sprintf(wh->i_addr3));
  176                 printf("(%s", ether_sprintf(wh->i_addr2));
  177                 printf("->%s)", ether_sprintf(wh->i_addr1));
  178                 break;
  179         }
  180         switch (wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK) {
  181         case IEEE80211_FC0_TYPE_DATA:
  182                 printf(" data");
  183                 break;
  184         case IEEE80211_FC0_TYPE_MGT:
  185                 printf(" %s", ieee80211_mgt_subtype_name[
  186                     (wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK)
  187                     >> IEEE80211_FC0_SUBTYPE_SHIFT]);
  188                 break;
  189         default:
  190                 printf(" type#%d", wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK);
  191                 break;
  192         }
  193         if (wh->i_fc[1] & IEEE80211_FC1_WEP)
  194                 printf(" WEP");
  195         if (rate >= 0)
  196                 printf(" %dM", rate / 2);
  197         if (rssi >= 0)
  198                 printf(" +%d", rssi);
  199         printf("\n");
  200         if (len > 0) {
  201                 for (i = 0; i < len; i++) {
  202                         if ((i & 1) == 0)
  203                                 printf(" ");
  204                         printf("%02x", buf[i]);
  205                 }
  206                 printf("\n");
  207         }
  208 }
  209 
  210 int
  211 ieee80211_fix_rate(struct ieee80211com *ic, struct ieee80211_node *ni, int flags)
  212 {
  213 #define RV(v)   ((v) & IEEE80211_RATE_VAL)
  214         int i, j, ignore, error;
  215         int okrate, badrate;
  216         struct ieee80211_rateset *srs, *nrs;
  217         u_int8_t r;
  218 
  219         error = 0;
  220         okrate = badrate = 0;
  221         srs = &ic->ic_sup_rates[ieee80211_chan2mode(ic, ni->ni_chan)];
  222         nrs = &ni->ni_rates;
  223         for (i = 0; i < nrs->rs_nrates; ) {
  224                 ignore = 0;
  225                 if (flags & IEEE80211_F_DOSORT) {
  226                         /*
  227                          * Sort rates.
  228                          */
  229                         for (j = i + 1; j < nrs->rs_nrates; j++) {
  230                                 if (RV(nrs->rs_rates[i]) > RV(nrs->rs_rates[j])) {
  231                                         r = nrs->rs_rates[i];
  232                                         nrs->rs_rates[i] = nrs->rs_rates[j];
  233                                         nrs->rs_rates[j] = r;
  234                                 }
  235                         }
  236                 }
  237                 r = nrs->rs_rates[i] & IEEE80211_RATE_VAL;
  238                 badrate = r;
  239                 if (flags & IEEE80211_F_DOFRATE) {
  240                         /*
  241                          * Apply fixed rate constraint.  Note that we do
  242                          * not apply the constraint to basic rates as
  243                          * otherwise we may not be able to associate if
  244                          * the rate set we submit to the AP is invalid
  245                          * (e.g. fix rate at 36Mb/s which is not a basic
  246                          * rate for 11a operation).
  247                          */
  248                         if ((nrs->rs_rates[i] & IEEE80211_RATE_BASIC) == 0 &&
  249                             ic->ic_fixed_rate >= 0 &&
  250                             r != RV(srs->rs_rates[ic->ic_fixed_rate]))
  251                                 ignore++;
  252                 }
  253                 if (flags & IEEE80211_F_DONEGO) {
  254                         /*
  255                          * Check against supported rates.
  256                          */
  257                         for (j = 0; j < srs->rs_nrates; j++) {
  258                                 if (r == RV(srs->rs_rates[j]))
  259                                         break;
  260                         }
  261                         if (j == srs->rs_nrates) {
  262                                 /*
  263                                  * A rate in the node's rate set is not
  264                                  * supported.  If this is a basic rate and we
  265                                  * are operating as an AP then this is an error.
  266                                  * Otherwise we just discard/ignore the rate.
  267                                  * Note that this is important for 11b stations
  268                                  * when they want to associate with an 11g AP.
  269                                  */
  270                                 if (ic->ic_opmode == IEEE80211_M_HOSTAP &&
  271                                     (nrs->rs_rates[i] & IEEE80211_RATE_BASIC))
  272                                         error++;
  273                                 ignore++;
  274                         }
  275                 }
  276                 if (flags & IEEE80211_F_DODEL) {
  277                         /*
  278                          * Delete unacceptable rates.
  279                          */
  280                         if (ignore) {
  281                                 nrs->rs_nrates--;
  282                                 for (j = i; j < nrs->rs_nrates; j++)
  283                                         nrs->rs_rates[j] = nrs->rs_rates[j + 1];
  284                                 nrs->rs_rates[j] = 0;
  285                                 continue;
  286                         }
  287                 }
  288                 if (!ignore)
  289                         okrate = nrs->rs_rates[i];
  290                 i++;
  291         }
  292         if (okrate == 0 || error != 0)
  293                 return badrate | IEEE80211_RATE_BASIC;
  294         else
  295                 return RV(okrate);
  296 #undef RV
  297 }
  298 
  299 static int
  300 ieee80211_newstate(struct ieee80211com *ic, enum ieee80211_state nstate, int mgt)
  301 {
  302         struct ifnet *ifp = &ic->ic_if;
  303         struct ieee80211_node *ni;
  304         enum ieee80211_state ostate;
  305 
  306         ostate = ic->ic_state;
  307         IEEE80211_DPRINTF(("%s: %s -> %s\n", __func__,
  308                 ieee80211_state_name[ostate], ieee80211_state_name[nstate]));
  309         ic->ic_state = nstate;                  /* state transition */
  310         ni = ic->ic_bss;                        /* NB: no reference held */
  311         switch (nstate) {
  312         case IEEE80211_S_INIT:
  313                 switch (ostate) {
  314                 case IEEE80211_S_INIT:
  315                         break;
  316                 case IEEE80211_S_RUN:
  317                         switch (ic->ic_opmode) {
  318                         case IEEE80211_M_STA:
  319                                 IEEE80211_SEND_MGMT(ic, ni,
  320                                     IEEE80211_FC0_SUBTYPE_DISASSOC,
  321                                     IEEE80211_REASON_ASSOC_LEAVE);
  322                                 break;
  323                         case IEEE80211_M_HOSTAP:
  324                                 IEEE80211_NODE_LOCK(ic);
  325                                 TAILQ_FOREACH(ni, &ic->ic_node, ni_list) {
  326                                         if (ni->ni_associd == 0)
  327                                                 continue;
  328                                         IEEE80211_SEND_MGMT(ic, ni,
  329                                             IEEE80211_FC0_SUBTYPE_DISASSOC,
  330                                             IEEE80211_REASON_ASSOC_LEAVE);
  331                                 }
  332                                 IEEE80211_NODE_UNLOCK(ic);
  333                                 break;
  334                         default:
  335                                 break;
  336                         }
  337                         /* FALLTHRU */
  338                 case IEEE80211_S_ASSOC:
  339                         switch (ic->ic_opmode) {
  340                         case IEEE80211_M_STA:
  341                                 IEEE80211_SEND_MGMT(ic, ni,
  342                                     IEEE80211_FC0_SUBTYPE_DEAUTH,
  343                                     IEEE80211_REASON_AUTH_LEAVE);
  344                                 break;
  345                         case IEEE80211_M_HOSTAP:
  346                                 IEEE80211_NODE_LOCK(ic);
  347                                 TAILQ_FOREACH(ni, &ic->ic_node, ni_list) {
  348                                         IEEE80211_SEND_MGMT(ic, ni,
  349                                             IEEE80211_FC0_SUBTYPE_DEAUTH,
  350                                             IEEE80211_REASON_AUTH_LEAVE);
  351                                 }
  352                                 IEEE80211_NODE_UNLOCK(ic);
  353                                 break;
  354                         default:
  355                                 break;
  356                         }
  357                         /* FALLTHRU */
  358                 case IEEE80211_S_AUTH:
  359                 case IEEE80211_S_SCAN:
  360                         ic->ic_mgt_timer = 0;
  361                         IF_DRAIN(&ic->ic_mgtq);
  362                         if (ic->ic_wep_ctx != NULL) {
  363                                 free(ic->ic_wep_ctx, M_DEVBUF);
  364                                 ic->ic_wep_ctx = NULL;
  365                         }
  366                         ieee80211_free_allnodes(ic);
  367                         break;
  368                 }
  369                 break;
  370         case IEEE80211_S_SCAN:
  371                 ic->ic_flags &= ~IEEE80211_F_SIBSS;
  372                 /* initialize bss for probe request */
  373                 IEEE80211_ADDR_COPY(ni->ni_macaddr, ifp->if_broadcastaddr);
  374                 IEEE80211_ADDR_COPY(ni->ni_bssid, ifp->if_broadcastaddr);
  375                 ni->ni_rates = ic->ic_sup_rates[
  376                         ieee80211_chan2mode(ic, ni->ni_chan)];
  377                 ni->ni_associd = 0;
  378                 ni->ni_rstamp = 0;
  379                 switch (ostate) {
  380                 case IEEE80211_S_INIT:
  381                         if (ic->ic_opmode == IEEE80211_M_HOSTAP &&
  382                             ic->ic_des_chan != IEEE80211_CHAN_ANYC) {
  383                                 /*
  384                                  * AP operation and we already have a channel;
  385                                  * bypass the scan and startup immediately.
  386                                  */
  387                                 ieee80211_create_ibss(ic, ic->ic_des_chan);
  388                         } else {
  389                                 ieee80211_begin_scan(ifp);
  390                         }
  391                         break;
  392                 case IEEE80211_S_SCAN:
  393                         /* scan next */
  394                         if (ic->ic_flags & IEEE80211_F_ASCAN) {
  395                                 IEEE80211_SEND_MGMT(ic, ni,
  396                                     IEEE80211_FC0_SUBTYPE_PROBE_REQ, 0);
  397                         }
  398                         break;
  399                 case IEEE80211_S_RUN:
  400                         /* beacon miss */
  401                         if (ifp->if_flags & IFF_DEBUG) {
  402                                 /* XXX bssid clobbered above */
  403                                 if_printf(ifp, "no recent beacons from %s;"
  404                                     " rescanning\n",
  405                                     ether_sprintf(ic->ic_bss->ni_bssid));
  406                         }
  407                         ieee80211_free_allnodes(ic);
  408                         /* FALLTHRU */
  409                 case IEEE80211_S_AUTH:
  410                 case IEEE80211_S_ASSOC:
  411                         /* timeout restart scan */
  412                         ni = ieee80211_find_node(ic, ic->ic_bss->ni_macaddr);
  413                         if (ni != NULL) {
  414                                 ni->ni_fails++;
  415                                 ieee80211_unref_node(&ni);
  416                         }
  417                         ieee80211_begin_scan(ifp);
  418                         break;
  419                 }
  420                 break;
  421         case IEEE80211_S_AUTH:
  422                 switch (ostate) {
  423                 case IEEE80211_S_INIT:
  424                         IEEE80211_DPRINTF(("%s: invalid transition\n",
  425                                 __func__));
  426                         break;
  427                 case IEEE80211_S_SCAN:
  428                         IEEE80211_SEND_MGMT(ic, ni,
  429                             IEEE80211_FC0_SUBTYPE_AUTH, 1);
  430                         break;
  431                 case IEEE80211_S_AUTH:
  432                 case IEEE80211_S_ASSOC:
  433                         switch (mgt) {
  434                         case IEEE80211_FC0_SUBTYPE_AUTH:
  435                                 /* ??? */
  436                                 IEEE80211_SEND_MGMT(ic, ni,
  437                                     IEEE80211_FC0_SUBTYPE_AUTH, 2);
  438                                 break;
  439                         case IEEE80211_FC0_SUBTYPE_DEAUTH:
  440                                 /* ignore and retry scan on timeout */
  441                                 break;
  442                         }
  443                         break;
  444                 case IEEE80211_S_RUN:
  445                         switch (mgt) {
  446                         case IEEE80211_FC0_SUBTYPE_AUTH:
  447                                 IEEE80211_SEND_MGMT(ic, ni,
  448                                     IEEE80211_FC0_SUBTYPE_AUTH, 2);
  449                                 ic->ic_state = ostate;  /* stay RUN */
  450                                 break;
  451                         case IEEE80211_FC0_SUBTYPE_DEAUTH:
  452                                 /* try to reauth */
  453                                 IEEE80211_SEND_MGMT(ic, ni,
  454                                     IEEE80211_FC0_SUBTYPE_AUTH, 1);
  455                                 break;
  456                         }
  457                         break;
  458                 }
  459                 break;
  460         case IEEE80211_S_ASSOC:
  461                 switch (ostate) {
  462                 case IEEE80211_S_INIT:
  463                 case IEEE80211_S_SCAN:
  464                 case IEEE80211_S_ASSOC:
  465                         IEEE80211_DPRINTF(("%s: invalid transition\n",
  466                                 __func__));
  467                         break;
  468                 case IEEE80211_S_AUTH:
  469                         IEEE80211_SEND_MGMT(ic, ni,
  470                             IEEE80211_FC0_SUBTYPE_ASSOC_REQ, 0);
  471                         break;
  472                 case IEEE80211_S_RUN:
  473                         IEEE80211_SEND_MGMT(ic, ni,
  474                             IEEE80211_FC0_SUBTYPE_ASSOC_REQ, 1);
  475                         break;
  476                 }
  477                 break;
  478         case IEEE80211_S_RUN:
  479                 switch (ostate) {
  480                 case IEEE80211_S_INIT:
  481                 case IEEE80211_S_AUTH:
  482                 case IEEE80211_S_RUN:
  483                         IEEE80211_DPRINTF(("%s: invalid transition\n",
  484                                 __func__));
  485                         break;
  486                 case IEEE80211_S_SCAN:          /* adhoc/hostap mode */
  487                 case IEEE80211_S_ASSOC:         /* infra mode */
  488                         KASSERT(ni->ni_txrate < ni->ni_rates.rs_nrates,
  489                                 ("%s: bogus xmit rate %u setup\n", __func__,
  490                                         ni->ni_txrate));
  491                         if (ifp->if_flags & IFF_DEBUG) {
  492                                 if_printf(ifp, " ");
  493                                 if (ic->ic_opmode == IEEE80211_M_STA)
  494                                         printf("associated ");
  495                                 else
  496                                         printf("synchronized ");
  497                                 printf("with %s ssid ",
  498                                     ether_sprintf(ni->ni_bssid));
  499                                 ieee80211_print_essid(ic->ic_bss->ni_essid,
  500                                     ni->ni_esslen);
  501                                 printf(" channel %d start %uMb\n",
  502                                         ieee80211_chan2ieee(ic, ni->ni_chan),
  503                                         IEEE80211_RATE2MBS(ni->ni_rates.rs_rates[ni->ni_txrate]));
  504                         }
  505                         ic->ic_mgt_timer = 0;
  506                         (*ifp->if_start)(ifp);
  507                         break;
  508                 }
  509                 break;
  510         }
  511         return 0;
  512 }

Cache object: 0eda0a3d977d6aa864ec11a1fd79c261


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