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/dev/acpica/acpi_pci_link.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) 2002 Mitsuru IWASAKI <iwasaki@jp.freebsd.org>
    3  * All rights reserved.
    4  *
    5  * Redistribution and use in source and binary forms, with or without
    6  * modification, are permitted provided that the following conditions
    7  * are met:
    8  * 1. Redistributions of source code must retain the above copyright
    9  *    notice, this list of conditions and the following disclaimer.
   10  * 2. Redistributions in binary form must reproduce the above copyright
   11  *    notice, this list of conditions and the following disclaimer in the
   12  *    documentation and/or other materials provided with the distribution.
   13  *
   14  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
   15  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
   16  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
   17  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
   18  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
   19  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
   20  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
   21  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
   22  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
   23  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
   24  * SUCH DAMAGE.
   25  */
   26 
   27 #include <sys/cdefs.h>
   28 __FBSDID("$FreeBSD$");
   29 
   30 #include "opt_acpi.h"
   31 #include <sys/param.h>
   32 #include <sys/bus.h>
   33 #include <sys/kernel.h>
   34 #include <sys/limits.h>
   35 #include <sys/malloc.h>
   36 #include <sys/module.h>
   37 
   38 #include <contrib/dev/acpica/include/acpi.h>
   39 
   40 #include <dev/acpica/acpivar.h>
   41 #include <dev/acpica/acpi_pcibvar.h>
   42 
   43 #include <machine/pci_cfgreg.h>
   44 #include <dev/pci/pcireg.h>
   45 #include <dev/pci/pcivar.h>
   46 #include "pcib_if.h"
   47 
   48 /* Hooks for the ACPI CA debugging infrastructure. */
   49 #define _COMPONENT      ACPI_BUS
   50 ACPI_MODULE_NAME("PCI_LINK")
   51 
   52 ACPI_SERIAL_DECL(pci_link, "ACPI PCI link");
   53 
   54 #define NUM_ISA_INTERRUPTS      16
   55 #define NUM_ACPI_INTERRUPTS     256
   56 
   57 /*
   58  * An ACPI PCI link device may contain multiple links.  Each link has its
   59  * own ACPI resource.  _PRT entries specify which link is being used via
   60  * the Source Index.
   61  *
   62  * XXX: A note about Source Indices and DPFs:  Currently we assume that
   63  * the DPF start and end tags are not counted towards the index that
   64  * Source Index corresponds to.  Also, we assume that when DPFs are in use
   65  * they various sets overlap in terms of Indices.  Here's an example
   66  * resource list indicating these assumptions:
   67  *
   68  * Resource             Index
   69  * --------             -----
   70  * I/O Port             0
   71  * Start DPF            -
   72  * IRQ                  1
   73  * MemIO                2
   74  * Start DPF            -
   75  * IRQ                  1
   76  * MemIO                2
   77  * End DPF              -
   78  * DMA Channel          3
   79  *
   80  * The XXX is because I'm not sure if this is a valid assumption to make.
   81  */
   82 
   83 /* States during DPF processing. */
   84 #define DPF_OUTSIDE     0
   85 #define DPF_FIRST       1
   86 #define DPF_IGNORE      2
   87 
   88 struct link;
   89 
   90 struct acpi_pci_link_softc {
   91         int     pl_num_links;
   92         int     pl_crs_bad;
   93         struct link *pl_links;
   94         device_t pl_dev;
   95 };
   96 
   97 struct link {
   98         struct acpi_pci_link_softc *l_sc;
   99         uint8_t l_bios_irq;
  100         uint8_t l_irq;
  101         uint8_t l_initial_irq;
  102         UINT32  l_crs_type;
  103         int     l_res_index;
  104         int     l_num_irqs;
  105         int     *l_irqs;
  106         int     l_references;
  107         int     l_routed:1;
  108         int     l_isa_irq:1;
  109         ACPI_RESOURCE l_prs_template;
  110 };
  111 
  112 struct link_count_request {
  113         int     in_dpf;
  114         int     count;
  115 };
  116 
  117 struct link_res_request {
  118         struct acpi_pci_link_softc *sc;
  119         int     in_dpf;
  120         int     res_index;
  121         int     link_index;
  122 };
  123 
  124 static MALLOC_DEFINE(M_PCI_LINK, "pci_link", "ACPI PCI Link structures");
  125 
  126 static int pci_link_interrupt_weights[NUM_ACPI_INTERRUPTS];
  127 static int pci_link_bios_isa_irqs;
  128 
  129 static char *pci_link_ids[] = { "PNP0C0F", NULL };
  130 
  131 /*
  132  * Fetch the short name associated with an ACPI handle and save it in the
  133  * passed in buffer.
  134  */
  135 static ACPI_STATUS
  136 acpi_short_name(ACPI_HANDLE handle, char *buffer, size_t buflen)
  137 {
  138         ACPI_BUFFER buf;
  139 
  140         buf.Length = buflen;
  141         buf.Pointer = buffer;
  142         return (AcpiGetName(handle, ACPI_SINGLE_NAME, &buf));
  143 }
  144 
  145 static int
  146 acpi_pci_link_probe(device_t dev)
  147 {
  148         char descr[28], name[12];
  149         int rv;
  150 
  151         /*
  152          * We explicitly do not check _STA since not all systems set it to
  153          * sensible values.
  154          */
  155         if (acpi_disabled("pci_link"))
  156             return (ENXIO);
  157         rv = ACPI_ID_PROBE(device_get_parent(dev), dev, pci_link_ids, NULL);
  158         if (rv > 0)
  159           return (rv);
  160 
  161         if (ACPI_SUCCESS(acpi_short_name(acpi_get_handle(dev), name,
  162             sizeof(name)))) {
  163                 snprintf(descr, sizeof(descr), "ACPI PCI Link %s", name);
  164                 device_set_desc_copy(dev, descr);
  165         } else
  166                 device_set_desc(dev, "ACPI PCI Link");
  167         device_quiet(dev);
  168         return (rv);
  169 }
  170 
  171 static ACPI_STATUS
  172 acpi_count_irq_resources(ACPI_RESOURCE *res, void *context)
  173 {
  174         struct link_count_request *req;
  175 
  176         req = (struct link_count_request *)context;
  177         switch (res->Type) {
  178         case ACPI_RESOURCE_TYPE_START_DEPENDENT:
  179                 switch (req->in_dpf) {
  180                 case DPF_OUTSIDE:
  181                         /* We've started the first DPF. */
  182                         req->in_dpf = DPF_FIRST;
  183                         break;
  184                 case DPF_FIRST:
  185                         /* We've started the second DPF. */
  186                         req->in_dpf = DPF_IGNORE;
  187                         break;
  188                 }
  189                 break;
  190         case ACPI_RESOURCE_TYPE_END_DEPENDENT:
  191                 /* We are finished with DPF parsing. */
  192                 KASSERT(req->in_dpf != DPF_OUTSIDE,
  193                     ("%s: end dpf when not parsing a dpf", __func__));
  194                 req->in_dpf = DPF_OUTSIDE;
  195                 break;
  196         case ACPI_RESOURCE_TYPE_IRQ:
  197         case ACPI_RESOURCE_TYPE_EXTENDED_IRQ:
  198                 /*
  199                  * Don't count resources if we are in a DPF set that we are
  200                  * ignoring.
  201                  */
  202                 if (req->in_dpf != DPF_IGNORE)
  203                         req->count++;
  204         }
  205         return (AE_OK);
  206 }
  207 
  208 static ACPI_STATUS
  209 link_add_crs(ACPI_RESOURCE *res, void *context)
  210 {
  211         struct link_res_request *req;
  212         struct link *link;
  213 
  214         ACPI_SERIAL_ASSERT(pci_link);
  215         req = (struct link_res_request *)context;
  216         switch (res->Type) {
  217         case ACPI_RESOURCE_TYPE_START_DEPENDENT:
  218                 switch (req->in_dpf) {
  219                 case DPF_OUTSIDE:
  220                         /* We've started the first DPF. */
  221                         req->in_dpf = DPF_FIRST;
  222                         break;
  223                 case DPF_FIRST:
  224                         /* We've started the second DPF. */
  225                         panic(
  226                 "%s: Multiple dependent functions within a current resource",
  227                             __func__);
  228                         break;
  229                 }
  230                 break;
  231         case ACPI_RESOURCE_TYPE_END_DEPENDENT:
  232                 /* We are finished with DPF parsing. */
  233                 KASSERT(req->in_dpf != DPF_OUTSIDE,
  234                     ("%s: end dpf when not parsing a dpf", __func__));
  235                 req->in_dpf = DPF_OUTSIDE;
  236                 break;
  237         case ACPI_RESOURCE_TYPE_IRQ:
  238         case ACPI_RESOURCE_TYPE_EXTENDED_IRQ:
  239                 KASSERT(req->link_index < req->sc->pl_num_links,
  240                     ("%s: array boundary violation", __func__));
  241                 link = &req->sc->pl_links[req->link_index];
  242                 link->l_res_index = req->res_index;
  243                 link->l_crs_type = res->Type;
  244                 req->link_index++;
  245                 req->res_index++;
  246 
  247                 /*
  248                  * Only use the current value if there's one IRQ.  Some
  249                  * systems return multiple IRQs (which is nonsense for _CRS)
  250                  * when the link hasn't been programmed.
  251                  */
  252                 if (res->Type == ACPI_RESOURCE_TYPE_IRQ) {
  253                         if (res->Data.Irq.InterruptCount == 1)
  254                                 link->l_irq = res->Data.Irq.Interrupts[0];
  255                 } else if (res->Data.ExtendedIrq.InterruptCount == 1)
  256                         link->l_irq = res->Data.ExtendedIrq.Interrupts[0];
  257 
  258                 /*
  259                  * An IRQ of zero means that the link isn't routed.
  260                  */
  261                 if (link->l_irq == 0)
  262                         link->l_irq = PCI_INVALID_IRQ;
  263                 break;
  264         default:
  265                 req->res_index++;
  266         }
  267         return (AE_OK);
  268 }
  269 
  270 /*
  271  * Populate the set of possible IRQs for each device.
  272  */
  273 static ACPI_STATUS
  274 link_add_prs(ACPI_RESOURCE *res, void *context)
  275 {
  276         ACPI_RESOURCE *tmp;
  277         struct link_res_request *req;
  278         struct link *link;
  279         UINT8 *irqs = NULL;
  280         UINT32 *ext_irqs = NULL;
  281         int i, is_ext_irq = 1;
  282 
  283         ACPI_SERIAL_ASSERT(pci_link);
  284         req = (struct link_res_request *)context;
  285         switch (res->Type) {
  286         case ACPI_RESOURCE_TYPE_START_DEPENDENT:
  287                 switch (req->in_dpf) {
  288                 case DPF_OUTSIDE:
  289                         /* We've started the first DPF. */
  290                         req->in_dpf = DPF_FIRST;
  291                         break;
  292                 case DPF_FIRST:
  293                         /* We've started the second DPF. */
  294                         req->in_dpf = DPF_IGNORE;
  295                         break;
  296                 }
  297                 break;
  298         case ACPI_RESOURCE_TYPE_END_DEPENDENT:
  299                 /* We are finished with DPF parsing. */
  300                 KASSERT(req->in_dpf != DPF_OUTSIDE,
  301                     ("%s: end dpf when not parsing a dpf", __func__));
  302                 req->in_dpf = DPF_OUTSIDE;
  303                 break;
  304         case ACPI_RESOURCE_TYPE_IRQ:
  305                 is_ext_irq = 0;
  306                 /* fall through */
  307         case ACPI_RESOURCE_TYPE_EXTENDED_IRQ:
  308                 /*
  309                  * Don't parse resources if we are in a DPF set that we are
  310                  * ignoring.
  311                  */
  312                 if (req->in_dpf == DPF_IGNORE)
  313                         break;
  314 
  315                 KASSERT(req->link_index < req->sc->pl_num_links,
  316                     ("%s: array boundary violation", __func__));
  317                 link = &req->sc->pl_links[req->link_index];
  318                 if (link->l_res_index == -1) {
  319                         KASSERT(req->sc->pl_crs_bad,
  320                             ("res_index should be set"));
  321                         link->l_res_index = req->res_index;
  322                 }
  323                 req->link_index++;
  324                 req->res_index++;
  325 
  326                 /*
  327                  * Stash a copy of the resource for later use when doing
  328                  * _SRS.
  329                  */
  330                 tmp = &link->l_prs_template;
  331                 if (is_ext_irq) {
  332                         bcopy(res, tmp, ACPI_RS_SIZE(tmp->Data.ExtendedIrq));
  333 
  334                         /*
  335                          * XXX acpi_AppendBufferResource() cannot handle
  336                          * optional data.
  337                          */
  338                         bzero(&tmp->Data.ExtendedIrq.ResourceSource,
  339                             sizeof(tmp->Data.ExtendedIrq.ResourceSource));
  340                         tmp->Length = ACPI_RS_SIZE(tmp->Data.ExtendedIrq);
  341 
  342                         link->l_num_irqs =
  343                             res->Data.ExtendedIrq.InterruptCount;
  344                         ext_irqs = res->Data.ExtendedIrq.Interrupts;
  345                 } else {
  346                         bcopy(res, tmp, ACPI_RS_SIZE(tmp->Data.Irq));
  347                         link->l_num_irqs = res->Data.Irq.InterruptCount;
  348                         irqs = res->Data.Irq.Interrupts;
  349                 }
  350                 if (link->l_num_irqs == 0)
  351                         break;
  352 
  353                 /*
  354                  * Save a list of the valid IRQs.  Also, if all of the
  355                  * valid IRQs are ISA IRQs, then mark this link as
  356                  * routed via an ISA interrupt.
  357                  */
  358                 link->l_isa_irq = TRUE;
  359                 link->l_irqs = malloc(sizeof(int) * link->l_num_irqs,
  360                     M_PCI_LINK, M_WAITOK | M_ZERO);
  361                 for (i = 0; i < link->l_num_irqs; i++) {
  362                         if (is_ext_irq) {
  363                                 link->l_irqs[i] = ext_irqs[i];
  364                                 if (ext_irqs[i] >= NUM_ISA_INTERRUPTS)
  365                                         link->l_isa_irq = FALSE;
  366                         } else {
  367                                 link->l_irqs[i] = irqs[i];
  368                                 if (irqs[i] >= NUM_ISA_INTERRUPTS)
  369                                         link->l_isa_irq = FALSE;
  370                         }
  371                 }
  372 
  373                 /*
  374                  * If this is not an ISA IRQ but _CRS used a non-extended
  375                  * IRQ descriptor, don't use _CRS as a template for _SRS.
  376                  */
  377                 if (!req->sc->pl_crs_bad && !link->l_isa_irq &&
  378                     link->l_crs_type == ACPI_RESOURCE_TYPE_IRQ)
  379                         req->sc->pl_crs_bad = TRUE;
  380                 break;
  381         default:
  382                 if (req->in_dpf == DPF_IGNORE)
  383                         break;
  384                 if (req->sc->pl_crs_bad)
  385                         device_printf(req->sc->pl_dev,
  386                     "Warning: possible resource %d will be lost during _SRS\n",
  387                             req->res_index);
  388                 req->res_index++;
  389         }
  390         return (AE_OK);
  391 }
  392 
  393 static int
  394 link_valid_irq(struct link *link, int irq)
  395 {
  396         int i;
  397 
  398         ACPI_SERIAL_ASSERT(pci_link);
  399 
  400         /* Invalid interrupts are never valid. */
  401         if (!PCI_INTERRUPT_VALID(irq))
  402                 return (FALSE);
  403 
  404         /* Any interrupt in the list of possible interrupts is valid. */
  405         for (i = 0; i < link->l_num_irqs; i++)
  406                 if (link->l_irqs[i] == irq)
  407                          return (TRUE);
  408 
  409         /*
  410          * For links routed via an ISA interrupt, if the SCI is routed via
  411          * an ISA interrupt, the SCI is always treated as a valid IRQ.
  412          */
  413         if (link->l_isa_irq && AcpiGbl_FADT.SciInterrupt == irq &&
  414             irq < NUM_ISA_INTERRUPTS)
  415                 return (TRUE);
  416 
  417         /* If the interrupt wasn't found in the list it is not valid. */
  418         return (FALSE);
  419 }
  420 
  421 static void
  422 acpi_pci_link_dump(struct acpi_pci_link_softc *sc, int header, const char *tag)
  423 {
  424         struct link *link;
  425         char buf[16];
  426         int i, j;
  427 
  428         ACPI_SERIAL_ASSERT(pci_link);
  429         if (header) {
  430                 snprintf(buf, sizeof(buf), "%s:",
  431                     device_get_nameunit(sc->pl_dev));
  432                 printf("%-16.16s  Index  IRQ  Rtd  Ref  IRQs\n", buf);
  433         }
  434         for (i = 0; i < sc->pl_num_links; i++) {
  435                 link = &sc->pl_links[i];
  436                 printf("  %-14.14s  %5d  %3d   %c   %3d ", i == 0 ? tag : "", i,
  437                     link->l_irq, link->l_routed ? 'Y' : 'N',
  438                     link->l_references);
  439                 if (link->l_num_irqs == 0)
  440                         printf(" none");
  441                 else for (j = 0; j < link->l_num_irqs; j++)
  442                         printf(" %d", link->l_irqs[j]);
  443                 printf("\n");
  444         }
  445 }
  446 
  447 static int
  448 acpi_pci_link_attach(device_t dev)
  449 {
  450         struct acpi_pci_link_softc *sc;
  451         struct link_count_request creq;
  452         struct link_res_request rreq;
  453         ACPI_STATUS status;
  454         int i;
  455 
  456         sc = device_get_softc(dev);
  457         sc->pl_dev = dev;
  458         ACPI_SERIAL_BEGIN(pci_link);
  459 
  460         /*
  461          * Count the number of current resources so we know how big of
  462          * a link array to allocate.  On some systems, _CRS is broken,
  463          * so for those systems try to derive the count from _PRS instead.
  464          */
  465         creq.in_dpf = DPF_OUTSIDE;
  466         creq.count = 0;
  467         status = AcpiWalkResources(acpi_get_handle(dev), "_CRS",
  468             acpi_count_irq_resources, &creq);
  469         sc->pl_crs_bad = ACPI_FAILURE(status);
  470         if (sc->pl_crs_bad) {
  471                 creq.in_dpf = DPF_OUTSIDE;
  472                 creq.count = 0;
  473                 status = AcpiWalkResources(acpi_get_handle(dev), "_PRS",
  474                     acpi_count_irq_resources, &creq);
  475                 if (ACPI_FAILURE(status)) {
  476                         device_printf(dev,
  477                             "Unable to parse _CRS or _PRS: %s\n",
  478                             AcpiFormatException(status));
  479                         ACPI_SERIAL_END(pci_link);
  480                         return (ENXIO);
  481                 }
  482         }
  483         sc->pl_num_links = creq.count;
  484         if (creq.count == 0) {
  485                 ACPI_SERIAL_END(pci_link);
  486                 return (0);
  487         }
  488         sc->pl_links = malloc(sizeof(struct link) * sc->pl_num_links,
  489             M_PCI_LINK, M_WAITOK | M_ZERO);
  490 
  491         /* Initialize the child links. */
  492         for (i = 0; i < sc->pl_num_links; i++) {
  493                 sc->pl_links[i].l_irq = PCI_INVALID_IRQ;
  494                 sc->pl_links[i].l_bios_irq = PCI_INVALID_IRQ;
  495                 sc->pl_links[i].l_sc = sc;
  496                 sc->pl_links[i].l_isa_irq = FALSE;
  497                 sc->pl_links[i].l_res_index = -1;
  498         }
  499 
  500         /* Try to read the current settings from _CRS if it is valid. */
  501         if (!sc->pl_crs_bad) {
  502                 rreq.in_dpf = DPF_OUTSIDE;
  503                 rreq.link_index = 0;
  504                 rreq.res_index = 0;
  505                 rreq.sc = sc;
  506                 status = AcpiWalkResources(acpi_get_handle(dev), "_CRS",
  507                     link_add_crs, &rreq);
  508                 if (ACPI_FAILURE(status)) {
  509                         device_printf(dev, "Unable to parse _CRS: %s\n",
  510                             AcpiFormatException(status));
  511                         goto fail;
  512                 }
  513         }
  514 
  515         /*
  516          * Try to read the possible settings from _PRS.  Note that if the
  517          * _CRS is toast, we depend on having a working _PRS.  However, if
  518          * _CRS works, then it is ok for _PRS to be missing.
  519          */
  520         rreq.in_dpf = DPF_OUTSIDE;
  521         rreq.link_index = 0;
  522         rreq.res_index = 0;
  523         rreq.sc = sc;
  524         status = AcpiWalkResources(acpi_get_handle(dev), "_PRS",
  525             link_add_prs, &rreq);
  526         if (ACPI_FAILURE(status) &&
  527             (status != AE_NOT_FOUND || sc->pl_crs_bad)) {
  528                 device_printf(dev, "Unable to parse _PRS: %s\n",
  529                     AcpiFormatException(status));
  530                 goto fail;
  531         }
  532         if (bootverbose)
  533                 acpi_pci_link_dump(sc, 1, "Initial Probe");
  534 
  535         /* Verify initial IRQs if we have _PRS. */
  536         if (status != AE_NOT_FOUND)
  537                 for (i = 0; i < sc->pl_num_links; i++)
  538                         if (!link_valid_irq(&sc->pl_links[i],
  539                             sc->pl_links[i].l_irq))
  540                                 sc->pl_links[i].l_irq = PCI_INVALID_IRQ;
  541         if (bootverbose)
  542                 acpi_pci_link_dump(sc, 0, "Validation");
  543 
  544         /* Save initial IRQs. */
  545         for (i = 0; i < sc->pl_num_links; i++)
  546                 sc->pl_links[i].l_initial_irq = sc->pl_links[i].l_irq;
  547 
  548         /*
  549          * Try to disable this link.  If successful, set the current IRQ to
  550          * zero and flags to indicate this link is not routed.  If we can't
  551          * run _DIS (i.e., the method doesn't exist), assume the initial
  552          * IRQ was routed by the BIOS.
  553          */
  554         if (ACPI_SUCCESS(AcpiEvaluateObject(acpi_get_handle(dev), "_DIS", NULL,
  555             NULL)))
  556                 for (i = 0; i < sc->pl_num_links; i++)
  557                         sc->pl_links[i].l_irq = PCI_INVALID_IRQ;
  558         else
  559                 for (i = 0; i < sc->pl_num_links; i++)
  560                         if (PCI_INTERRUPT_VALID(sc->pl_links[i].l_irq))
  561                                 sc->pl_links[i].l_routed = TRUE;
  562         if (bootverbose)
  563                 acpi_pci_link_dump(sc, 0, "After Disable");
  564         ACPI_SERIAL_END(pci_link);
  565         return (0);
  566 fail:
  567         ACPI_SERIAL_END(pci_link);
  568         for (i = 0; i < sc->pl_num_links; i++)
  569                 if (sc->pl_links[i].l_irqs != NULL)
  570                         free(sc->pl_links[i].l_irqs, M_PCI_LINK);
  571         free(sc->pl_links, M_PCI_LINK);
  572         return (ENXIO);
  573 }
  574 
  575 /* XXX: Note that this is identical to pci_pir_search_irq(). */
  576 static uint8_t
  577 acpi_pci_link_search_irq(int bus, int device, int pin)
  578 {
  579         uint32_t value;
  580         uint8_t func, maxfunc;
  581 
  582         /* See if we have a valid device at function 0. */
  583         value = pci_cfgregread(bus, device, 0, PCIR_VENDOR, 2);
  584         if (value == PCIV_INVALID)
  585                 return (PCI_INVALID_IRQ);
  586         value = pci_cfgregread(bus, device, 0, PCIR_HDRTYPE, 1);
  587         if ((value & PCIM_HDRTYPE) > PCI_MAXHDRTYPE)
  588                 return (PCI_INVALID_IRQ);
  589         if (value & PCIM_MFDEV)
  590                 maxfunc = PCI_FUNCMAX;
  591         else
  592                 maxfunc = 0;
  593 
  594         /* Scan all possible functions at this device. */
  595         for (func = 0; func <= maxfunc; func++) {
  596                 value = pci_cfgregread(bus, device, func, PCIR_VENDOR, 2);
  597                 if (value == PCIV_INVALID)
  598                         continue;
  599                 value = pci_cfgregread(bus, device, func, PCIR_INTPIN, 1);
  600 
  601                 /*
  602                  * See if it uses the pin in question.  Note that the passed
  603                  * in pin uses 0 for A, .. 3 for D whereas the intpin
  604                  * register uses 0 for no interrupt, 1 for A, .. 4 for D.
  605                  */
  606                 if (value != pin + 1)
  607                         continue;
  608                 value = pci_cfgregread(bus, device, func, PCIR_INTLINE, 1);
  609                 if (bootverbose)
  610                         printf(
  611                 "ACPI: Found matching pin for %d.%d.INT%c at func %d: %d\n",
  612                             bus, device, pin + 'A', func, value);
  613                 if (value != PCI_INVALID_IRQ)
  614                         return (value);
  615         }
  616         return (PCI_INVALID_IRQ);
  617 }
  618 
  619 /*
  620  * Find the link structure that corresponds to the resource index passed in
  621  * via 'source_index'.
  622  */
  623 static struct link *
  624 acpi_pci_link_lookup(device_t dev, int source_index)
  625 {
  626         struct acpi_pci_link_softc *sc;
  627         int i;
  628 
  629         ACPI_SERIAL_ASSERT(pci_link);
  630         sc = device_get_softc(dev);
  631         for (i = 0; i < sc->pl_num_links; i++)
  632                 if (sc->pl_links[i].l_res_index == source_index)
  633                         return (&sc->pl_links[i]);
  634         return (NULL);
  635 }
  636 
  637 void
  638 acpi_pci_link_add_reference(device_t dev, int index, device_t pcib, int slot,
  639     int pin)
  640 {
  641         struct link *link;
  642         uint8_t bios_irq;
  643         uintptr_t bus;
  644 
  645         /*
  646          * Look up the PCI bus for the specified PCI bridge device.  Note
  647          * that the PCI bridge device might not have any children yet.
  648          * However, looking up its bus number doesn't require a valid child
  649          * device, so we just pass NULL.
  650          */
  651         if (BUS_READ_IVAR(pcib, NULL, PCIB_IVAR_BUS, &bus) != 0) {
  652                 device_printf(pcib, "Unable to read PCI bus number");
  653                 panic("PCI bridge without a bus number");
  654         }
  655                 
  656         /* Bump the reference count. */
  657         ACPI_SERIAL_BEGIN(pci_link);
  658         link = acpi_pci_link_lookup(dev, index);
  659         if (link == NULL) {
  660                 device_printf(dev, "apparently invalid index %d\n", index);
  661                 ACPI_SERIAL_END(pci_link);
  662                 return;
  663         }
  664         link->l_references++;
  665         if (link->l_routed)
  666                 pci_link_interrupt_weights[link->l_irq]++;
  667 
  668         /*
  669          * The BIOS only routes interrupts via ISA IRQs using the ATPICs
  670          * (8259As).  Thus, if this link is routed via an ISA IRQ, go
  671          * look to see if the BIOS routed an IRQ for this link at the
  672          * indicated (bus, slot, pin).  If so, we prefer that IRQ for
  673          * this link and add that IRQ to our list of known-good IRQs.
  674          * This provides a good work-around for link devices whose _CRS
  675          * method is either broken or bogus.  We only use the value
  676          * returned by _CRS if we can't find a valid IRQ via this method
  677          * in fact.
  678          *
  679          * If this link is not routed via an ISA IRQ (because we are using
  680          * APIC for example), then don't bother looking up the BIOS IRQ
  681          * as if we find one it won't be valid anyway.
  682          */
  683         if (!link->l_isa_irq) {
  684                 ACPI_SERIAL_END(pci_link);
  685                 return;
  686         }
  687 
  688         /* Try to find a BIOS IRQ setting from any matching devices. */
  689         bios_irq = acpi_pci_link_search_irq(bus, slot, pin);
  690         if (!PCI_INTERRUPT_VALID(bios_irq)) {
  691                 ACPI_SERIAL_END(pci_link);
  692                 return;
  693         }
  694 
  695         /* Validate the BIOS IRQ. */
  696         if (!link_valid_irq(link, bios_irq)) {
  697                 device_printf(dev, "BIOS IRQ %u for %d.%d.INT%c is invalid\n",
  698                     bios_irq, (int)bus, slot, pin + 'A');
  699         } else if (!PCI_INTERRUPT_VALID(link->l_bios_irq)) {
  700                 link->l_bios_irq = bios_irq;
  701                 if (bios_irq < NUM_ISA_INTERRUPTS)
  702                         pci_link_bios_isa_irqs |= (1 << bios_irq);
  703                 if (bios_irq != link->l_initial_irq &&
  704                     PCI_INTERRUPT_VALID(link->l_initial_irq))
  705                         device_printf(dev,
  706                             "BIOS IRQ %u does not match initial IRQ %u\n",
  707                             bios_irq, link->l_initial_irq);
  708         } else if (bios_irq != link->l_bios_irq)
  709                 device_printf(dev,
  710             "BIOS IRQ %u for %d.%d.INT%c does not match previous BIOS IRQ %u\n",
  711                     bios_irq, (int)bus, slot, pin + 'A',
  712                     link->l_bios_irq);
  713         ACPI_SERIAL_END(pci_link);
  714 }
  715 
  716 static ACPI_STATUS
  717 acpi_pci_link_srs_from_crs(struct acpi_pci_link_softc *sc, ACPI_BUFFER *srsbuf)
  718 {
  719         ACPI_RESOURCE *end, *res;
  720         ACPI_STATUS status;
  721         struct link *link;
  722         int i __diagused, in_dpf;
  723 
  724         /* Fetch the _CRS. */
  725         ACPI_SERIAL_ASSERT(pci_link);
  726         srsbuf->Pointer = NULL;
  727         srsbuf->Length = ACPI_ALLOCATE_BUFFER;
  728         status = AcpiGetCurrentResources(acpi_get_handle(sc->pl_dev), srsbuf);
  729         if (ACPI_SUCCESS(status) && srsbuf->Pointer == NULL)
  730                 status = AE_NO_MEMORY;
  731         if (ACPI_FAILURE(status)) {
  732                 if (bootverbose)
  733                         device_printf(sc->pl_dev,
  734                             "Unable to fetch current resources: %s\n",
  735                             AcpiFormatException(status));
  736                 return (status);
  737         }
  738 
  739         /* Fill in IRQ resources via link structures. */
  740         link = sc->pl_links;
  741         i = 0;
  742         in_dpf = DPF_OUTSIDE;
  743         res = (ACPI_RESOURCE *)srsbuf->Pointer;
  744         end = (ACPI_RESOURCE *)((char *)srsbuf->Pointer + srsbuf->Length);
  745         for (;;) {
  746                 switch (res->Type) {
  747                 case ACPI_RESOURCE_TYPE_START_DEPENDENT:
  748                         switch (in_dpf) {
  749                         case DPF_OUTSIDE:
  750                                 /* We've started the first DPF. */
  751                                 in_dpf = DPF_FIRST;
  752                                 break;
  753                         case DPF_FIRST:
  754                                 /* We've started the second DPF. */
  755                                 panic(
  756                 "%s: Multiple dependent functions within a current resource",
  757                                     __func__);
  758                                 break;
  759                         }
  760                         break;
  761                 case ACPI_RESOURCE_TYPE_END_DEPENDENT:
  762                         /* We are finished with DPF parsing. */
  763                         KASSERT(in_dpf != DPF_OUTSIDE,
  764                             ("%s: end dpf when not parsing a dpf", __func__));
  765                         in_dpf = DPF_OUTSIDE;
  766                         break;
  767                 case ACPI_RESOURCE_TYPE_IRQ:
  768                         MPASS(i < sc->pl_num_links);
  769                         res->Data.Irq.InterruptCount = 1;
  770                         if (PCI_INTERRUPT_VALID(link->l_irq)) {
  771                                 KASSERT(link->l_irq < NUM_ISA_INTERRUPTS,
  772                 ("%s: can't put non-ISA IRQ %d in legacy IRQ resource type",
  773                                     __func__, link->l_irq));
  774                                 res->Data.Irq.Interrupts[0] = link->l_irq;
  775                         } else
  776                                 res->Data.Irq.Interrupts[0] = 0;
  777                         link++;
  778                         i++;
  779                         break;
  780                 case ACPI_RESOURCE_TYPE_EXTENDED_IRQ:
  781                         MPASS(i < sc->pl_num_links);
  782                         res->Data.ExtendedIrq.InterruptCount = 1;
  783                         if (PCI_INTERRUPT_VALID(link->l_irq))
  784                                 res->Data.ExtendedIrq.Interrupts[0] =
  785                                     link->l_irq;
  786                         else
  787                                 res->Data.ExtendedIrq.Interrupts[0] = 0;
  788                         link++;
  789                         i++;
  790                         break;
  791                 }
  792                 if (res->Type == ACPI_RESOURCE_TYPE_END_TAG)
  793                         break;
  794                 res = ACPI_NEXT_RESOURCE(res);
  795                 if (res >= end)
  796                         break;
  797         }
  798         return (AE_OK);
  799 }
  800 
  801 static ACPI_STATUS
  802 acpi_pci_link_srs_from_links(struct acpi_pci_link_softc *sc,
  803     ACPI_BUFFER *srsbuf)
  804 {
  805         ACPI_RESOURCE newres;
  806         ACPI_STATUS status;
  807         struct link *link;
  808         int i;
  809 
  810         /* Start off with an empty buffer. */
  811         srsbuf->Pointer = NULL;
  812         link = sc->pl_links;
  813         for (i = 0; i < sc->pl_num_links; i++) {
  814                 /* Add a new IRQ resource from each link. */
  815                 link = &sc->pl_links[i];
  816                 if (link->l_prs_template.Type == ACPI_RESOURCE_TYPE_IRQ) {
  817                         /* Build an IRQ resource. */
  818                         bcopy(&link->l_prs_template, &newres,
  819                             ACPI_RS_SIZE(newres.Data.Irq));
  820                         newres.Data.Irq.InterruptCount = 1;
  821                         if (PCI_INTERRUPT_VALID(link->l_irq)) {
  822                                 KASSERT(link->l_irq < NUM_ISA_INTERRUPTS,
  823                 ("%s: can't put non-ISA IRQ %d in legacy IRQ resource type",
  824                                     __func__, link->l_irq));
  825                                 newres.Data.Irq.Interrupts[0] = link->l_irq;
  826                         } else
  827                                 newres.Data.Irq.Interrupts[0] = 0;
  828                 } else {
  829                         /* Build an ExtIRQ resuorce. */
  830                         bcopy(&link->l_prs_template, &newres,
  831                             ACPI_RS_SIZE(newres.Data.ExtendedIrq));
  832                         newres.Data.ExtendedIrq.InterruptCount = 1;
  833                         if (PCI_INTERRUPT_VALID(link->l_irq))
  834                                 newres.Data.ExtendedIrq.Interrupts[0] =
  835                                     link->l_irq;
  836                         else
  837                                 newres.Data.ExtendedIrq.Interrupts[0] = 0;
  838                 }
  839 
  840                 /* Add the new resource to the end of the _SRS buffer. */
  841                 status = acpi_AppendBufferResource(srsbuf, &newres);
  842                 if (ACPI_FAILURE(status)) {
  843                         device_printf(sc->pl_dev,
  844                             "Unable to build resources: %s\n",
  845                             AcpiFormatException(status));
  846                         if (srsbuf->Pointer != NULL) {
  847                                 AcpiOsFree(srsbuf->Pointer);
  848                                 srsbuf->Pointer = NULL;
  849                         }
  850                         return (status);
  851                 }
  852         }
  853         return (AE_OK);
  854 }
  855 
  856 static ACPI_STATUS
  857 acpi_pci_link_route_irqs(device_t dev)
  858 {
  859         struct acpi_pci_link_softc *sc;
  860         ACPI_RESOURCE *resource, *end;
  861         ACPI_BUFFER srsbuf;
  862         ACPI_STATUS status;
  863         struct link *link;
  864         int i __diagused;
  865 
  866         ACPI_SERIAL_ASSERT(pci_link);
  867         sc = device_get_softc(dev);
  868         if (sc->pl_crs_bad)
  869                 status = acpi_pci_link_srs_from_links(sc, &srsbuf);
  870         else
  871                 status = acpi_pci_link_srs_from_crs(sc, &srsbuf);
  872         if (ACPI_FAILURE(status))
  873                 return (status);
  874 
  875         /* Write out new resources via _SRS. */
  876         status = AcpiSetCurrentResources(acpi_get_handle(dev), &srsbuf);
  877         if (ACPI_FAILURE(status)) {
  878                 device_printf(dev, "Unable to route IRQs: %s\n",
  879                     AcpiFormatException(status));
  880                 AcpiOsFree(srsbuf.Pointer);
  881                 return (status);
  882         }
  883 
  884         /*
  885          * Perform acpi_config_intr() on each IRQ resource if it was just
  886          * routed for the first time.
  887          */
  888         link = sc->pl_links;
  889         i = 0;
  890         resource = (ACPI_RESOURCE *)srsbuf.Pointer;
  891         end = (ACPI_RESOURCE *)((char *)srsbuf.Pointer + srsbuf.Length);
  892         for (;;) {
  893                 if (resource->Type == ACPI_RESOURCE_TYPE_END_TAG)
  894                         break;
  895                 switch (resource->Type) {
  896                 case ACPI_RESOURCE_TYPE_IRQ:
  897                 case ACPI_RESOURCE_TYPE_EXTENDED_IRQ:
  898                         MPASS(i < sc->pl_num_links);
  899 
  900                         /*
  901                          * Only configure the interrupt and update the
  902                          * weights if this link has a valid IRQ and was
  903                          * previously unrouted.
  904                          */
  905                         if (!link->l_routed &&
  906                             PCI_INTERRUPT_VALID(link->l_irq)) {
  907                                 link->l_routed = TRUE;
  908                                 acpi_config_intr(dev, resource);
  909                                 pci_link_interrupt_weights[link->l_irq] +=
  910                                     link->l_references;
  911                         }
  912                         link++;
  913                         i++;
  914                         break;
  915                 }
  916                 resource = ACPI_NEXT_RESOURCE(resource);
  917                 if (resource >= end)
  918                         break;
  919         }
  920         AcpiOsFree(srsbuf.Pointer);
  921         return (AE_OK);
  922 }
  923 
  924 static int
  925 acpi_pci_link_resume(device_t dev)
  926 {
  927         struct acpi_pci_link_softc *sc;
  928         ACPI_STATUS status;
  929         int i, routed;
  930 
  931         /*
  932          * If all of our links are routed, then restore the link via _SRS,
  933          * otherwise, disable the link via _DIS.
  934          */
  935         ACPI_SERIAL_BEGIN(pci_link);
  936         sc = device_get_softc(dev);
  937         routed = 0;
  938         for (i = 0; i < sc->pl_num_links; i++)
  939                 if (sc->pl_links[i].l_routed)
  940                         routed++;
  941         if (routed == sc->pl_num_links)
  942                 status = acpi_pci_link_route_irqs(dev);
  943         else {
  944                 AcpiEvaluateObject(acpi_get_handle(dev), "_DIS", NULL, NULL);
  945                 status = AE_OK;
  946         }
  947         ACPI_SERIAL_END(pci_link);
  948         if (ACPI_FAILURE(status))
  949                 return (ENXIO);
  950         else
  951                 return (0);
  952 }
  953 
  954 /*
  955  * Pick an IRQ to use for this unrouted link.
  956  */
  957 static uint8_t
  958 acpi_pci_link_choose_irq(device_t dev, struct link *link)
  959 {
  960         char tunable_buffer[64], link_name[5];
  961         u_int8_t best_irq, pos_irq;
  962         int best_weight, pos_weight, i;
  963 
  964         KASSERT(!link->l_routed, ("%s: link already routed", __func__));
  965         KASSERT(!PCI_INTERRUPT_VALID(link->l_irq),
  966             ("%s: link already has an IRQ", __func__));
  967 
  968         /* Check for a tunable override. */
  969         if (ACPI_SUCCESS(acpi_short_name(acpi_get_handle(dev), link_name,
  970             sizeof(link_name)))) {
  971                 snprintf(tunable_buffer, sizeof(tunable_buffer),
  972                     "hw.pci.link.%s.%d.irq", link_name, link->l_res_index);
  973                 if (getenv_int(tunable_buffer, &i) && PCI_INTERRUPT_VALID(i)) {
  974                         if (!link_valid_irq(link, i))
  975                                 device_printf(dev,
  976                                     "Warning, IRQ %d is not listed as valid\n",
  977                                     i);
  978                         return (i);
  979                 }
  980                 snprintf(tunable_buffer, sizeof(tunable_buffer),
  981                     "hw.pci.link.%s.irq", link_name);
  982                 if (getenv_int(tunable_buffer, &i) && PCI_INTERRUPT_VALID(i)) {
  983                         if (!link_valid_irq(link, i))
  984                                 device_printf(dev,
  985                                     "Warning, IRQ %d is not listed as valid\n",
  986                                     i);
  987                         return (i);
  988                 }
  989         }
  990 
  991         /*
  992          * If we have a valid BIOS IRQ, use that.  We trust what the BIOS
  993          * says it routed over what _CRS says the link thinks is routed.
  994          */
  995         if (PCI_INTERRUPT_VALID(link->l_bios_irq))
  996                 return (link->l_bios_irq);
  997 
  998         /*
  999          * If we don't have a BIOS IRQ but do have a valid IRQ from _CRS,
 1000          * then use that.
 1001          */
 1002         if (PCI_INTERRUPT_VALID(link->l_initial_irq))
 1003                 return (link->l_initial_irq);
 1004 
 1005         /*
 1006          * Ok, we have no useful hints, so we have to pick from the
 1007          * possible IRQs.  For ISA IRQs we only use interrupts that
 1008          * have already been used by the BIOS.
 1009          */
 1010         best_irq = PCI_INVALID_IRQ;
 1011         best_weight = INT_MAX;
 1012         for (i = 0; i < link->l_num_irqs; i++) {
 1013                 pos_irq = link->l_irqs[i];
 1014                 if (pos_irq < NUM_ISA_INTERRUPTS &&
 1015                     (pci_link_bios_isa_irqs & 1 << pos_irq) == 0)
 1016                         continue;
 1017                 pos_weight = pci_link_interrupt_weights[pos_irq];
 1018                 if (pos_weight < best_weight) {
 1019                         best_weight = pos_weight;
 1020                         best_irq = pos_irq;
 1021                 }
 1022         }
 1023 
 1024         /*
 1025          * If this is an ISA IRQ, try using the SCI if it is also an ISA
 1026          * interrupt as a fallback.
 1027          */
 1028         if (link->l_isa_irq) {
 1029                 pos_irq = AcpiGbl_FADT.SciInterrupt;
 1030                 pos_weight = pci_link_interrupt_weights[pos_irq];
 1031                 if (pos_weight < best_weight) {
 1032                         best_weight = pos_weight;
 1033                         best_irq = pos_irq;
 1034                 }
 1035         }
 1036 
 1037         if (PCI_INTERRUPT_VALID(best_irq)) {
 1038                 if (bootverbose)
 1039                         device_printf(dev, "Picked IRQ %u with weight %d\n",
 1040                             best_irq, best_weight);
 1041         } else
 1042                 device_printf(dev, "Unable to choose an IRQ\n");
 1043         return (best_irq);
 1044 }
 1045 
 1046 int
 1047 acpi_pci_link_route_interrupt(device_t dev, int index)
 1048 {
 1049         struct link *link;
 1050 
 1051         if (acpi_disabled("pci_link"))
 1052                 return (PCI_INVALID_IRQ);
 1053 
 1054         ACPI_SERIAL_BEGIN(pci_link);
 1055         link = acpi_pci_link_lookup(dev, index);
 1056         if (link == NULL)
 1057                 panic("%s: apparently invalid index %d", __func__, index);
 1058 
 1059         /*
 1060          * If this link device is already routed to an interrupt, just return
 1061          * the interrupt it is routed to.
 1062          */
 1063         if (link->l_routed) {
 1064                 KASSERT(PCI_INTERRUPT_VALID(link->l_irq),
 1065                     ("%s: link is routed but has an invalid IRQ", __func__));
 1066                 ACPI_SERIAL_END(pci_link);
 1067                 return (link->l_irq);
 1068         }
 1069 
 1070         /* Choose an IRQ if we need one. */
 1071         if (!PCI_INTERRUPT_VALID(link->l_irq)) {
 1072                 link->l_irq = acpi_pci_link_choose_irq(dev, link);
 1073 
 1074                 /*
 1075                  * Try to route the interrupt we picked.  If it fails, then
 1076                  * assume the interrupt is not routed.
 1077                  */
 1078                 if (PCI_INTERRUPT_VALID(link->l_irq)) {
 1079                         acpi_pci_link_route_irqs(dev);
 1080                         if (!link->l_routed)
 1081                                 link->l_irq = PCI_INVALID_IRQ;
 1082                 }
 1083         }
 1084         ACPI_SERIAL_END(pci_link);
 1085 
 1086         return (link->l_irq);
 1087 }
 1088 
 1089 /*
 1090  * This is gross, but we abuse the identify routine to perform one-time
 1091  * SYSINIT() style initialization for the driver.
 1092  */
 1093 static void
 1094 acpi_pci_link_identify(driver_t *driver, device_t parent)
 1095 {
 1096 
 1097         /*
 1098          * If the SCI is an ISA IRQ, add it to the bitmask of known good
 1099          * ISA IRQs.
 1100          *
 1101          * XXX: If we are using the APIC, the SCI might have been
 1102          * rerouted to an APIC pin in which case this is invalid.  However,
 1103          * if we are using the APIC, we also shouldn't be having any PCI
 1104          * interrupts routed via ISA IRQs, so this is probably ok.
 1105          */
 1106         if (AcpiGbl_FADT.SciInterrupt < NUM_ISA_INTERRUPTS)
 1107                 pci_link_bios_isa_irqs |= (1 << AcpiGbl_FADT.SciInterrupt);
 1108 }
 1109 
 1110 static device_method_t acpi_pci_link_methods[] = {
 1111         /* Device interface */
 1112         DEVMETHOD(device_identify,      acpi_pci_link_identify),
 1113         DEVMETHOD(device_probe,         acpi_pci_link_probe),
 1114         DEVMETHOD(device_attach,        acpi_pci_link_attach),
 1115         DEVMETHOD(device_resume,        acpi_pci_link_resume),
 1116 
 1117         DEVMETHOD_END
 1118 };
 1119 
 1120 static driver_t acpi_pci_link_driver = {
 1121         "pci_link",
 1122         acpi_pci_link_methods,
 1123         sizeof(struct acpi_pci_link_softc),
 1124 };
 1125 
 1126 DRIVER_MODULE(acpi_pci_link, acpi, acpi_pci_link_driver, 0, 0);
 1127 MODULE_DEPEND(acpi_pci_link, acpi, 1, 1, 1);

Cache object: 4f6f3608835dc3e53125138203e3b16c


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