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: releng/5.3/sys/dev/acpica/acpi_pci_link.c 135500 2004-09-20 05:52:19Z njl $");
   29 
   30 #include "opt_acpi.h"
   31 #include <sys/param.h>
   32 #include <sys/kernel.h>
   33 #include <sys/bus.h>
   34 
   35 #include "acpi.h"
   36 #include <dev/acpica/acpivar.h>
   37 #include <dev/acpica/acpi_pcibvar.h>
   38 
   39 #include <dev/pci/pcivar.h>
   40 #include "pcib_if.h"
   41 
   42 /* Hooks for the ACPI CA debugging infrastructure. */
   43 #define _COMPONENT      ACPI_BUS
   44 ACPI_MODULE_NAME("PCI_LINK")
   45 
   46 TAILQ_HEAD(acpi_pci_link_entries, acpi_pci_link_entry);
   47 static struct acpi_pci_link_entries acpi_pci_link_entries;
   48 ACPI_SERIAL_DECL(pci_link, "ACPI PCI link");
   49 
   50 TAILQ_HEAD(acpi_prt_entries, acpi_prt_entry);
   51 static struct acpi_prt_entries acpi_prt_entries;
   52 
   53 static int      irq_penalty[MAX_ACPI_INTERRUPTS];
   54 
   55 static int      acpi_pci_link_is_valid_irq(struct acpi_pci_link_entry *link,
   56                     UINT8 irq);
   57 static void     acpi_pci_link_update_irq_penalty(device_t dev, int busno);
   58 static void     acpi_pci_link_set_bootdisabled_priority(void);
   59 static void     acpi_pci_link_fixup_bootdisabled_link(void);
   60 
   61 /*
   62  * PCI link object management
   63  */
   64 
   65 static void
   66 acpi_pci_link_dump_polarity(UINT32 ActiveHighLow)
   67 {
   68 
   69         switch (ActiveHighLow) {
   70         case ACPI_ACTIVE_HIGH:
   71                 printf("high,");
   72                 break;
   73         case ACPI_ACTIVE_LOW:
   74                 printf("low,");
   75                 break;
   76         default:
   77                 printf("unknown,");
   78                 break;
   79         }
   80 }
   81 
   82 static void
   83 acpi_pci_link_dump_trigger(UINT32 EdgeLevel)
   84 {
   85 
   86         switch (EdgeLevel) {
   87         case ACPI_EDGE_SENSITIVE:
   88                 printf("edge,");
   89                 break;
   90         case ACPI_LEVEL_SENSITIVE:
   91                 printf("level,");
   92                 break;
   93         default:
   94                 printf("unknown,");
   95                 break;
   96         }
   97 }
   98 
   99 static void
  100 acpi_pci_link_dump_sharemode(UINT32 SharedExclusive)
  101 {
  102 
  103         switch (SharedExclusive) {
  104         case ACPI_EXCLUSIVE:
  105                 printf("exclusive");
  106                 break;
  107         case ACPI_SHARED:
  108                 printf("sharable");
  109                 break;
  110         default:
  111                 printf("unknown");
  112                 break;
  113         }
  114 }
  115 
  116 static void
  117 acpi_pci_link_entry_dump(struct acpi_prt_entry *entry)
  118 {
  119         UINT8                   i;
  120         ACPI_RESOURCE_IRQ       *Irq;
  121         ACPI_RESOURCE_EXT_IRQ   *ExtIrq;
  122         struct acpi_pci_link_entry *link;
  123 
  124         if (entry == NULL || entry->pci_link == NULL)
  125                 return;
  126         link = entry->pci_link;
  127 
  128         printf("%s irq%c%2d: ", acpi_name(link->handle),
  129             (link->flags & ACPI_LINK_ROUTED) ? '*' : ' ', link->current_irq);
  130 
  131         printf("[");
  132         if (link->number_of_interrupts)
  133                 printf("%2d", link->interrupts[0]);
  134         for (i = 1; i < link->number_of_interrupts; i++)
  135                 printf("%3d", link->interrupts[i]);
  136         printf("] %2d+ ", link->initial_irq);
  137 
  138         switch (link->possible_resources.Id) {
  139         case ACPI_RSTYPE_IRQ:
  140                 Irq = &link->possible_resources.Data.Irq;
  141                 acpi_pci_link_dump_polarity(Irq->ActiveHighLow);
  142                 acpi_pci_link_dump_trigger(Irq->EdgeLevel);
  143                 acpi_pci_link_dump_sharemode(Irq->SharedExclusive);
  144                 break;
  145         case ACPI_RSTYPE_EXT_IRQ:
  146                 ExtIrq = &link->possible_resources.Data.ExtendedIrq;
  147                 acpi_pci_link_dump_polarity(ExtIrq->ActiveHighLow);
  148                 acpi_pci_link_dump_trigger(ExtIrq->EdgeLevel);
  149                 acpi_pci_link_dump_sharemode(ExtIrq->SharedExclusive);
  150                 break;
  151         }
  152 
  153         printf(" %d.%d.%d\n", entry->busno,
  154             (int)((entry->prt.Address & 0xffff0000) >> 16),
  155             (int)entry->prt.Pin);
  156 }
  157 
  158 static ACPI_STATUS
  159 acpi_pci_link_get_object_status(ACPI_HANDLE handle, UINT32 *sta)
  160 {
  161         ACPI_DEVICE_INFO        *devinfo;
  162         ACPI_BUFFER             buf;
  163         ACPI_STATUS             error;
  164 
  165         ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
  166 
  167         if (handle == NULL || sta == NULL) {
  168                 ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "invalid argument\n"));
  169                 return_ACPI_STATUS (AE_BAD_PARAMETER);
  170         }
  171 
  172         buf.Pointer = NULL;
  173         buf.Length = ACPI_ALLOCATE_BUFFER;
  174         error = AcpiGetObjectInfo(handle, &buf);
  175         if (ACPI_FAILURE(error)) {
  176                 ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
  177                     "couldn't get object info %s - %s\n",
  178                     acpi_name(handle), AcpiFormatException(error)));
  179                 return_ACPI_STATUS (error);
  180         }
  181 
  182         devinfo = (ACPI_DEVICE_INFO *)buf.Pointer;
  183         if ((devinfo->Valid & ACPI_VALID_HID) == 0 ||
  184             strcmp(devinfo->HardwareId.Value, "PNP0C0F") != 0) {
  185                 ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "invalid hardware ID - %s\n",
  186                     acpi_name(handle)));
  187                 AcpiOsFree(buf.Pointer);
  188                 return_ACPI_STATUS (AE_TYPE);
  189         }
  190 
  191         if ((devinfo->Valid & ACPI_VALID_STA) != 0) {
  192                 *sta = devinfo->CurrentStatus;
  193         } else {
  194                 ACPI_DEBUG_PRINT((ACPI_DB_WARN, "invalid status - %s\n",
  195                     acpi_name(handle)));
  196                 *sta = 0;
  197         }
  198 
  199         AcpiOsFree(buf.Pointer);
  200         return_ACPI_STATUS (AE_OK);
  201 }
  202 
  203 static ACPI_STATUS
  204 acpi_pci_link_get_irq_resources(ACPI_RESOURCE *resources,
  205     UINT8 *number_of_interrupts, UINT8 interrupts[])
  206 {
  207         UINT8                   count;
  208         UINT8                   i;
  209         UINT32                  NumberOfInterrupts;
  210         UINT32                  *Interrupts;
  211 
  212         ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
  213 
  214         if (resources == NULL || number_of_interrupts == NULL) {
  215                 ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "invalid argument\n"));
  216                 return_ACPI_STATUS (AE_BAD_PARAMETER);
  217         }
  218 
  219         *number_of_interrupts = 0;
  220         NumberOfInterrupts = 0;
  221         Interrupts = NULL;
  222 
  223         if (resources->Id == ACPI_RSTYPE_START_DPF)
  224                 resources = ACPI_NEXT_RESOURCE(resources);
  225 
  226         if (resources->Id != ACPI_RSTYPE_IRQ &&
  227             resources->Id != ACPI_RSTYPE_EXT_IRQ) {
  228                 printf("acpi link get: resource %d is not an IRQ\n",
  229                     resources->Id);
  230                 return_ACPI_STATUS (AE_TYPE);
  231         }
  232 
  233         switch (resources->Id) {
  234         case ACPI_RSTYPE_IRQ:
  235                 NumberOfInterrupts = resources->Data.Irq.NumberOfInterrupts;
  236                 Interrupts = resources->Data.Irq.Interrupts;
  237                 break;
  238         case ACPI_RSTYPE_EXT_IRQ:
  239                 NumberOfInterrupts =
  240                     resources->Data.ExtendedIrq.NumberOfInterrupts;
  241                 Interrupts = resources->Data.ExtendedIrq.Interrupts;
  242                 break;
  243         }
  244 
  245         if (NumberOfInterrupts == 0)
  246                 return_ACPI_STATUS (AE_NULL_ENTRY);
  247 
  248         count = 0;
  249         for (i = 0; i < NumberOfInterrupts; i++) {
  250                 if (i >= MAX_POSSIBLE_INTERRUPTS) {
  251                         ACPI_DEBUG_PRINT((ACPI_DB_WARN, "too many IRQs (%d)\n",
  252                             i));
  253                         break;
  254                 }
  255                 if (Interrupts[i] == 0) {
  256                         ACPI_DEBUG_PRINT((ACPI_DB_WARN, "invalid IRQ %d\n",
  257                             Interrupts[i]));
  258                         continue;
  259                 }
  260                 interrupts[count] = Interrupts[i];
  261                 count++;
  262         }
  263         *number_of_interrupts = count;
  264 
  265         return_ACPI_STATUS (AE_OK);
  266 }
  267 
  268 static ACPI_STATUS
  269 acpi_pci_link_get_current_irq(struct acpi_pci_link_entry *link, UINT8 *irq)
  270 {
  271         ACPI_STATUS             error;
  272         ACPI_BUFFER             buf;
  273         ACPI_RESOURCE           *resources;
  274         UINT8                   number_of_interrupts;
  275         UINT8                   interrupts[MAX_POSSIBLE_INTERRUPTS];;
  276 
  277         ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
  278 
  279         if (link == NULL || irq == NULL) {
  280                 ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "invalid argument\n"));
  281                 return_ACPI_STATUS (AE_BAD_PARAMETER);
  282         }
  283 
  284         *irq = 0;
  285         buf.Pointer = NULL;
  286         buf.Length = ACPI_ALLOCATE_BUFFER;
  287         error = AcpiGetCurrentResources(link->handle, &buf);
  288         if (ACPI_FAILURE(error)) {
  289                 ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
  290                     "couldn't get PCI interrupt link device _CRS %s - %s\n",
  291                     acpi_name(link->handle), AcpiFormatException(error)));
  292                 return_ACPI_STATUS (error);
  293         }
  294         if (buf.Pointer == NULL) {
  295                 ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
  296                     "couldn't allocate memory - %s\n",
  297                     acpi_name(link->handle)));
  298                 return_ACPI_STATUS (AE_NO_MEMORY);
  299         }
  300 
  301         resources = (ACPI_RESOURCE *) buf.Pointer;
  302         number_of_interrupts = 0;
  303         bzero(interrupts, sizeof(interrupts));
  304         error = acpi_pci_link_get_irq_resources(resources,
  305                     &number_of_interrupts, interrupts);
  306         AcpiOsFree(buf.Pointer);
  307 
  308         if (ACPI_FAILURE(error)) {
  309                 ACPI_DEBUG_PRINT((ACPI_DB_WARN,
  310                     "couldn't get current IRQ from interrupt link %s - %s\n",
  311                     acpi_name(link->handle), AcpiFormatException(error)));
  312                 return_ACPI_STATUS (error);
  313         }
  314 
  315         if (number_of_interrupts == 0) {
  316                 ACPI_DEBUG_PRINT((ACPI_DB_WARN,
  317                     "PCI interrupt link device _CRS data is corrupted - %s\n",
  318                     acpi_name(link->handle)));
  319                 return_ACPI_STATUS (AE_NULL_ENTRY);
  320         }
  321 
  322         *irq = interrupts[0];
  323 
  324         return_ACPI_STATUS (AE_OK);
  325 }
  326 
  327 static ACPI_STATUS
  328 acpi_pci_link_add_link(ACPI_HANDLE handle, struct acpi_prt_entry *entry)
  329 {
  330         ACPI_STATUS             error;
  331         ACPI_BUFFER             buf;
  332         ACPI_RESOURCE           *resources;
  333         struct acpi_pci_link_entry *link;
  334 
  335         ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
  336         ACPI_SERIAL_ASSERT(pci_link);
  337 
  338         entry->pci_link = NULL;
  339         TAILQ_FOREACH(link, &acpi_pci_link_entries, links) {
  340                 if (link->handle == handle) {
  341                         entry->pci_link = link;
  342                         link->references++;
  343                         return_ACPI_STATUS (AE_OK);
  344                 }
  345         }
  346 
  347         link = AcpiOsAllocate(sizeof(struct acpi_pci_link_entry));
  348         if (link == NULL) {
  349                 ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
  350                     "couldn't allocate memory - %s\n", acpi_name(handle)));
  351                 return_ACPI_STATUS (AE_NO_MEMORY);
  352         }
  353 
  354         buf.Pointer = NULL;
  355         buf.Length = ACPI_ALLOCATE_BUFFER;
  356 
  357         bzero(link, sizeof(struct acpi_pci_link_entry));
  358         link->handle = handle;
  359 
  360         /*
  361          * Get the IRQ configured at boot-time.  If successful, set this
  362          * as the initial IRQ.
  363          */
  364         error = acpi_pci_link_get_current_irq(link, &link->current_irq);
  365         if (ACPI_SUCCESS(error)) {
  366                 link->initial_irq = link->current_irq;
  367         } else {
  368                 ACPI_DEBUG_PRINT((ACPI_DB_WARN,
  369                     "couldn't get current IRQ from interrupt link %s - %s\n",
  370                     acpi_name(handle), AcpiFormatException(error)));
  371                 link->initial_irq = 0;
  372         }
  373 
  374         error = AcpiGetPossibleResources(handle, &buf);
  375         if (ACPI_FAILURE(error)) {
  376                 ACPI_DEBUG_PRINT((ACPI_DB_WARN,
  377                     "couldn't get interrupt link device _PRS data %s - %s\n",
  378                     acpi_name(handle), AcpiFormatException(error)));
  379                 goto out;
  380         }
  381         if (buf.Pointer == NULL) {
  382                 ACPI_DEBUG_PRINT((ACPI_DB_WARN,
  383                     "_PRS buffer is empty - %s\n", acpi_name(handle)));
  384                 error = AE_NO_MEMORY;
  385                 goto out;
  386         }
  387 
  388         /* Skip any DPF descriptors.  XXX We should centralize this code. */
  389         resources = (ACPI_RESOURCE *) buf.Pointer;
  390         if (resources->Id == ACPI_RSTYPE_START_DPF)
  391                 resources = ACPI_NEXT_RESOURCE(resources);
  392 
  393         /* XXX This only handles one resource, ignoring SourceIndex. */
  394         bcopy(resources, &link->possible_resources,
  395             sizeof(link->possible_resources));
  396 
  397         error = acpi_pci_link_get_irq_resources(resources,
  398             &link->number_of_interrupts, link->interrupts);
  399         if (ACPI_FAILURE(error)) {
  400                 ACPI_DEBUG_PRINT((ACPI_DB_WARN,
  401                     "couldn't get possible IRQs from interrupt link %s - %s\n",
  402                     acpi_name(handle), AcpiFormatException(error)));
  403                 goto out;
  404         }
  405 
  406         if (link->number_of_interrupts == 0) {
  407                 ACPI_DEBUG_PRINT((ACPI_DB_WARN,
  408                     "interrupt link device _PRS data is corrupted - %s\n",
  409                     acpi_name(handle)));
  410                 error = AE_NULL_ENTRY;
  411                 goto out;
  412         }
  413 
  414         /*
  415          * Try to disable this link.  If successful, set the current IRQ to
  416          * zero and flags to indicate this link is not routed.  If we can't
  417          * run _DIS (i.e., the method doesn't exist), assume the initial
  418          * IRQ was routed by the BIOS.
  419          */
  420         if (ACPI_SUCCESS(AcpiEvaluateObject(handle, "_DIS", NULL, NULL))) {
  421                 link->current_irq = 0;
  422                 link->flags = ACPI_LINK_NONE;
  423         } else
  424                 link->flags = ACPI_LINK_ROUTED;
  425 
  426         /*
  427          * If the initial IRQ is invalid (not in _PRS), set it to 0 and
  428          * mark this link as not routed.  We won't use it as the preferred
  429          * interrupt later when we route.
  430          */
  431         if (!acpi_pci_link_is_valid_irq(link, link->initial_irq) &&
  432             link->initial_irq != 0) {
  433                 printf("ACPI link %s has invalid initial irq %d, ignoring\n",
  434                     acpi_name(handle), link->initial_irq);
  435                 link->initial_irq = 0;
  436                 link->flags = ACPI_LINK_NONE;
  437         }
  438 
  439         link->references++;
  440 
  441         TAILQ_INSERT_TAIL(&acpi_pci_link_entries, link, links);
  442         entry->pci_link = link;
  443 
  444         error = AE_OK;
  445 out:
  446         if (buf.Pointer != NULL)
  447                 AcpiOsFree(buf.Pointer);
  448         if (error != AE_OK && link != NULL)
  449                 AcpiOsFree(link);
  450 
  451         return_ACPI_STATUS (error);
  452 }
  453 
  454 static ACPI_STATUS
  455 acpi_pci_link_add_prt(device_t pcidev, ACPI_PCI_ROUTING_TABLE *prt, int busno)
  456 {
  457         ACPI_HANDLE             handle;
  458         ACPI_STATUS             error;
  459         UINT32                  sta;
  460         struct acpi_prt_entry   *entry;
  461 
  462         ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
  463         ACPI_SERIAL_ASSERT(pci_link);
  464 
  465         if (prt == NULL) {
  466                 device_printf(pcidev, "NULL PRT entry\n");
  467                 return_ACPI_STATUS (AE_BAD_PARAMETER);
  468         }
  469 
  470         /* Bail out if attempting to add a duplicate PRT entry. */
  471         TAILQ_FOREACH(entry, &acpi_prt_entries, links) {
  472                 if (entry->busno == busno &&
  473                     entry->prt.Address == prt->Address &&
  474                     entry->prt.Pin == prt->Pin) {
  475                         ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
  476                             "PRT entry already exists\n"));
  477                         return_ACPI_STATUS (AE_ALREADY_EXISTS);
  478                 }
  479         }
  480 
  481         /* Allocate and initialize our new PRT entry. */
  482         entry = AcpiOsAllocate(sizeof(struct acpi_prt_entry));
  483         if (entry == NULL) {
  484                 ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "can't allocate memory\n"));
  485                 return_ACPI_STATUS (AE_NO_MEMORY);
  486         }
  487         bzero(entry, sizeof(struct acpi_prt_entry));
  488 
  489         /*
  490          * If the source link is NULL, then this IRQ is hardwired so skip
  491          * initializing the link but still add it to the list.
  492          */
  493         if (prt->Source[0] != '\0') {
  494                 /* Get a handle for the link source. */
  495                 error = AcpiGetHandle(acpi_get_handle(pcidev), prt->Source,
  496                     &handle);
  497                 if (ACPI_FAILURE(error)) {
  498                         device_printf(pcidev, "get handle for %s - %s\n",
  499                             prt->Source, AcpiFormatException(error));
  500                         goto out;
  501                 }
  502 
  503                 error = acpi_pci_link_get_object_status(handle, &sta);
  504                 if (ACPI_FAILURE(error)) {
  505                         device_printf(pcidev, "can't get status for %s - %s\n",
  506                             acpi_name(handle), AcpiFormatException(error));
  507                         goto out;
  508                 }
  509 
  510                 /* Probe/initialize the link. */
  511                 error = acpi_pci_link_add_link(handle, entry);
  512                 if (ACPI_FAILURE(error)) {
  513                         ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
  514                             "couldn't add _PRT entry to link %s - %s\n",
  515                             acpi_name(handle), AcpiFormatException(error)));
  516                         goto out;
  517                 }
  518         }
  519 
  520         entry->pcidev = pcidev;
  521         entry->busno = busno;
  522         bcopy(prt, &entry->prt, sizeof(entry->prt));
  523 
  524         /*
  525          * Make sure the Source value is null-terminated.  It is really a
  526          * variable-length string (with a fixed size in the struct) so when
  527          * we copy the entire struct, we truncate the string.  Instead of
  528          * trying to make a variable-sized PRT object to handle the string,
  529          * we store its handle in prt_source.  Callers should use that to
  530          * look up the link object.
  531          */
  532         entry->prt.Source[sizeof(prt->Source) - 1] = '\0';
  533         entry->prt_source = handle;
  534 
  535         TAILQ_INSERT_TAIL(&acpi_prt_entries, entry, links);
  536         error = AE_OK;
  537 
  538 out:
  539         if (error != AE_OK && entry != NULL)
  540                 AcpiOsFree(entry);
  541 
  542         return_ACPI_STATUS (error);
  543 }
  544 
  545 /*
  546  * Look up the given interrupt in the list of possible settings for
  547  * this link.  We don't special-case the initial link setting.  Some
  548  * systems return current settings that are outside the list of valid
  549  * settings so only allow choices explicitly specified in _PRS.
  550  */
  551 static int
  552 acpi_pci_link_is_valid_irq(struct acpi_pci_link_entry *link, UINT8 irq)
  553 {
  554         UINT8                   i;
  555 
  556         if (irq == 0)
  557                 return (FALSE);
  558 
  559         for (i = 0; i < link->number_of_interrupts; i++) {
  560                 if (link->interrupts[i] == irq)
  561                         return (TRUE);
  562         }
  563 
  564         return (FALSE);
  565 }
  566 
  567 static ACPI_STATUS
  568 acpi_pci_link_set_irq(struct acpi_pci_link_entry *link, UINT8 irq)
  569 {
  570         ACPI_STATUS             error;
  571         ACPI_RESOURCE           resbuf;
  572         ACPI_BUFFER             crsbuf;
  573 
  574         ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
  575         ACPI_SERIAL_ASSERT(pci_link);
  576 
  577         /* Make sure the new IRQ is valid before routing. */
  578         if (!acpi_pci_link_is_valid_irq(link, irq)) {
  579                 printf("acpi link set: invalid IRQ %d on %s\n",
  580                     irq, acpi_name(link->handle));
  581                 return_ACPI_STATUS (AE_BAD_PARAMETER);
  582         }
  583 
  584         /* If this this link has already been routed, just return. */
  585         if (link->flags & ACPI_LINK_ROUTED) {
  586                 printf("acpi link set: %s already routed to %d\n",
  587                     acpi_name(link->handle), link->current_irq);
  588                 return_ACPI_STATUS (AE_OK);
  589         }
  590 
  591         /* Set up the IRQ resource for _SRS. */
  592         bzero(&resbuf, sizeof(resbuf));
  593         crsbuf.Pointer = NULL;
  594 
  595         switch (link->possible_resources.Id) {
  596         case ACPI_RSTYPE_IRQ:
  597                 resbuf.Id = ACPI_RSTYPE_IRQ;
  598                 resbuf.Length = ACPI_SIZEOF_RESOURCE(ACPI_RESOURCE_IRQ);
  599 
  600                 /* structure copy other fields */
  601                 resbuf.Data.Irq = link->possible_resources.Data.Irq;
  602                 resbuf.Data.Irq.NumberOfInterrupts = 1;
  603                 resbuf.Data.Irq.Interrupts[0] = irq;
  604                 break;
  605         case ACPI_RSTYPE_EXT_IRQ:
  606                 resbuf.Id = ACPI_RSTYPE_EXT_IRQ;
  607                 resbuf.Length = ACPI_SIZEOF_RESOURCE(ACPI_RESOURCE_EXT_IRQ);
  608 
  609                 /* structure copy other fields */
  610                 resbuf.Data.ExtendedIrq =
  611                     link->possible_resources.Data.ExtendedIrq;
  612                 resbuf.Data.ExtendedIrq.NumberOfInterrupts = 1;
  613                 resbuf.Data.ExtendedIrq.Interrupts[0] = irq;
  614                 break;
  615         default:
  616                 printf("acpi link set: %s resource is not an IRQ (%d)\n",
  617                     acpi_name(link->handle), link->possible_resources.Id);
  618                 return_ACPI_STATUS (AE_TYPE);
  619         }
  620 
  621         error = acpi_AppendBufferResource(&crsbuf, &resbuf);
  622         if (ACPI_FAILURE(error)) {
  623                 printf("acpi link set: AppendBuffer failed for %s\n",
  624                     acpi_name(link->handle));
  625                 return_ACPI_STATUS (error);
  626         }
  627         if (crsbuf.Pointer == NULL) {
  628                 printf("acpi link set: AppendBuffer returned empty for %s\n",
  629                     acpi_name(link->handle));
  630                 return_ACPI_STATUS (AE_NO_MEMORY);
  631         }
  632 
  633         /* Make the new IRQ active via the link's _SRS method. */
  634         error = AcpiSetCurrentResources(link->handle, &crsbuf);
  635         if (ACPI_FAILURE(error)) {
  636                 printf("acpi link set: _SRS failed for link %s - %s\n",
  637                     acpi_name(link->handle), AcpiFormatException(error));
  638                 goto out;
  639         }
  640         link->flags |= ACPI_LINK_ROUTED;
  641         link->current_irq = 0;
  642 
  643         /*
  644          * Many systems always return invalid values for current settings
  645          * (_CRS).  Since we can't trust the value returned, we have to
  646          * assume we were successful.
  647          */
  648         error = acpi_pci_link_get_current_irq(link, &link->current_irq);
  649         if (ACPI_FAILURE(error)) {
  650                 if (bootverbose)
  651                         printf("acpi link set: _CRS failed for link %s - %s\n",
  652                             acpi_name(link->handle),
  653                             AcpiFormatException(error));
  654                 error = AE_OK;
  655         }
  656         if (link->current_irq != irq) {
  657                 if (bootverbose)
  658                         printf("acpi link set: curr irq %d != %d for %s\n",
  659                             link->current_irq, irq, acpi_name(link->handle));
  660                 link->current_irq = irq;
  661         }
  662 
  663 out:
  664         if (crsbuf.Pointer)
  665                 AcpiOsFree(crsbuf.Pointer);
  666         return_ACPI_STATUS (error);
  667 }
  668 
  669 /*
  670  * Auto arbitration for boot-disabled devices
  671  */
  672 
  673 static void
  674 acpi_pci_link_bootdisabled_dump(void)
  675 {
  676         int                     i;
  677         int                     irq;
  678         struct acpi_pci_link_entry *link;
  679 
  680         ACPI_SERIAL_ASSERT(pci_link);
  681         TAILQ_FOREACH(link, &acpi_pci_link_entries, links) {
  682                 /* boot-disabled link only. */
  683                 if (link->current_irq != 0)
  684                         continue;
  685 
  686                 printf("%s (references %d, priority %d):\n",
  687                     acpi_name(link->handle), link->references, link->priority);
  688                 printf("\tinterrupts:\t");
  689                 for (i = 0; i < link->number_of_interrupts; i++) {
  690                         irq = link->sorted_irq[i];
  691                         printf("%6d", irq);
  692                 }
  693                 printf("\n");
  694                 printf("\tpenalty:\t");
  695                 for (i = 0; i < link->number_of_interrupts; i++) {
  696                         irq = link->sorted_irq[i];
  697                         printf("%6d", irq_penalty[irq]);
  698                 }
  699                 printf("\n");
  700         }
  701 }
  702 
  703 /*
  704  * Heuristics for choosing IRQs.  We start with some static penalties,
  705  * update them based on what IRQs are currently in use, then sort the
  706  * result.  This works ok but is not perfect.
  707  *
  708  * The PCI BIOS $PIR table offers "preferred PCI interrupts", but ACPI
  709  * doesn't seem to offer a similar mechanism, so picking a good
  710  * interrupt here is a difficult task.
  711  */
  712 static void
  713 acpi_pci_link_init_irq_penalty(void)
  714 {
  715 
  716         bzero(irq_penalty, sizeof(irq_penalty));
  717 
  718         /* 0, 1, 2, 8:  timer, keyboard, cascade, RTC */
  719         irq_penalty[0] = 100000;
  720         irq_penalty[1] = 100000;
  721         irq_penalty[2] = 100000;
  722         irq_penalty[8] = 100000;
  723 
  724         /* 13, 14, 15:  npx, ATA controllers */
  725         irq_penalty[13] = 50000;
  726         irq_penalty[14] = 50000;
  727         irq_penalty[15] = 50000;
  728 
  729         /* 3, 4, 6, 7, 12:  typically used by legacy hardware */
  730         irq_penalty[3] =   5000;
  731         irq_penalty[4] =   5000;
  732         irq_penalty[6] =   5000;
  733         irq_penalty[7] =   5000;
  734         irq_penalty[12] =  5000;
  735 
  736         /* 5:  sometimes legacy sound cards */
  737         irq_penalty[5] =     50;
  738 }
  739 
  740 static int
  741 link_exclusive(ACPI_RESOURCE *res)
  742 {
  743 
  744         if (res == NULL ||
  745             (res->Id != ACPI_RSTYPE_IRQ &&
  746             res->Id != ACPI_RSTYPE_EXT_IRQ))
  747                 return (FALSE);
  748 
  749         if ((res->Id == ACPI_RSTYPE_IRQ &&
  750             res->Data.Irq.SharedExclusive == ACPI_EXCLUSIVE) ||
  751             (res->Id == ACPI_RSTYPE_EXT_IRQ &&
  752             res->Data.ExtendedIrq.SharedExclusive == ACPI_EXCLUSIVE))
  753                 return (TRUE);
  754 
  755         return (FALSE);
  756 }
  757 
  758 static void
  759 acpi_pci_link_update_irq_penalty(device_t dev, int busno)
  760 {
  761         int                     i;
  762         int                     irq;
  763         int                     rid;
  764         struct resource         *res;
  765         struct acpi_prt_entry   *entry;
  766         struct acpi_pci_link_entry *link;
  767 
  768         ACPI_SERIAL_ASSERT(pci_link);
  769         TAILQ_FOREACH(entry, &acpi_prt_entries, links) {
  770                 if (entry->busno != busno)
  771                         continue;
  772 
  773                 /* Impossible? */
  774                 link = entry->pci_link;
  775                 if (link == NULL)
  776                         continue;
  777 
  778                 /* Update penalties for all possible settings of this link. */
  779                 for (i = 0; i < link->number_of_interrupts; i++) {
  780                         /* give 10 for each possible IRQs. */
  781                         irq = link->interrupts[i];
  782                         irq_penalty[irq] += 10;
  783 
  784                         /* higher penalty if exclusive. */
  785                         if (link_exclusive(&link->possible_resources))
  786                                 irq_penalty[irq] += 100;
  787 
  788                         /* XXX try to get this IRQ in non-sharable mode. */
  789                         rid = 0;
  790                         res = bus_alloc_resource(dev, SYS_RES_IRQ,
  791                                                  &rid, irq, irq, 1, 0);
  792                         if (res != NULL) {
  793                                 bus_release_resource(dev, SYS_RES_IRQ,
  794                                     rid, res);
  795                         } else {
  796                                 /* this is in use, give 10. */
  797                                 irq_penalty[irq] += 10;
  798                         }
  799                 }
  800 
  801                 /* initialize `sorted' possible IRQs. */
  802                 bcopy(link->interrupts, link->sorted_irq,
  803                     sizeof(link->sorted_irq));
  804         }
  805 }
  806 
  807 static void
  808 acpi_pci_link_set_bootdisabled_priority(void)
  809 {
  810         int                     sum_penalty;
  811         int                     i;
  812         int                     irq;
  813         struct acpi_pci_link_entry *link, *link_pri;
  814         TAILQ_HEAD(, acpi_pci_link_entry) sorted_list;
  815 
  816         ACPI_SERIAL_ASSERT(pci_link);
  817 
  818         /* reset priority for all links. */
  819         TAILQ_FOREACH(link, &acpi_pci_link_entries, links)
  820                 link->priority = 0;
  821 
  822         TAILQ_FOREACH(link, &acpi_pci_link_entries, links) {
  823                 /* If already routed, don't include in arbitration. */
  824                 if (link->flags & ACPI_LINK_ROUTED) {
  825                         link->priority = 0;
  826                         continue;
  827                 }
  828 
  829                 /*
  830                  * Calculate the priority for each boot-disabled links.
  831                  * o IRQ penalty indicates difficulty to use. 
  832                  * o #references for devices indicates importance of the link.
  833                  * o #interrupts indicates flexibility of the link.
  834                  */
  835                 sum_penalty = 0;
  836                 for (i = 0; i < link->number_of_interrupts; i++) {
  837                         irq = link->interrupts[i];
  838                         sum_penalty += irq_penalty[irq];
  839                 }
  840 
  841                 link->priority = (sum_penalty * link->references) /
  842                     link->number_of_interrupts;
  843         }
  844 
  845         /*
  846          * Sort PCI links based on the priority.
  847          * XXX Any other better ways rather than using work list?
  848          */
  849         TAILQ_INIT(&sorted_list);
  850         while (!TAILQ_EMPTY(&acpi_pci_link_entries)) {
  851                 link = TAILQ_FIRST(&acpi_pci_link_entries);
  852                 /* find an entry which has the highest priority. */
  853                 TAILQ_FOREACH(link_pri, &acpi_pci_link_entries, links)
  854                         if (link->priority < link_pri->priority)
  855                                 link = link_pri;
  856 
  857                 /* move to work list. */
  858                 TAILQ_REMOVE(&acpi_pci_link_entries, link, links);
  859                 TAILQ_INSERT_TAIL(&sorted_list, link, links);
  860         }
  861 
  862         while (!TAILQ_EMPTY(&sorted_list)) {
  863                 /* move them back to the list, one by one... */
  864                 link = TAILQ_FIRST(&sorted_list);
  865                 TAILQ_REMOVE(&sorted_list, link, links);
  866                 TAILQ_INSERT_TAIL(&acpi_pci_link_entries, link, links);
  867         }
  868 }
  869 
  870 static void
  871 acpi_pci_link_fixup_bootdisabled_link(void)
  872 {
  873         int                     i, j;
  874         int                     irq1, irq2;
  875         struct acpi_pci_link_entry *link;
  876 
  877         ACPI_SERIAL_ASSERT(pci_link);
  878 
  879         TAILQ_FOREACH(link, &acpi_pci_link_entries, links) {
  880                 /* Ignore links that have been routed already. */
  881                 if (link->flags & ACPI_LINK_ROUTED)
  882                         continue;
  883 
  884                 /* sort IRQs based on their penalty descending. */
  885                 for (i = 0; i < link->number_of_interrupts; i++) {
  886                         irq1 = link->sorted_irq[i];
  887                         for (j = i + 1; j < link->number_of_interrupts; j++) {
  888                                 irq2 = link->sorted_irq[j];
  889                                 if (irq_penalty[irq1] < irq_penalty[irq2]) {
  890                                         continue;
  891                                 }
  892                                 link->sorted_irq[i] = irq2;
  893                                 link->sorted_irq[j] = irq1;
  894                                 irq1 = irq2;
  895                         }
  896                 }
  897         }
  898 
  899         if (bootverbose) {
  900                 printf("ACPI PCI link arbitrated settings:\n");
  901                 acpi_pci_link_bootdisabled_dump();
  902         }
  903 }
  904 
  905 /*
  906  * Public interface
  907  */
  908 
  909 int
  910 acpi_pci_link_config(device_t dev, ACPI_BUFFER *prtbuf, int busno)
  911 {
  912         struct acpi_prt_entry   *entry;
  913         ACPI_PCI_ROUTING_TABLE  *prt;
  914         u_int8_t                *prtp;
  915         ACPI_STATUS             error;
  916         int                     ret;
  917         static int              first_time = 1;
  918 
  919         ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
  920 
  921         if (acpi_disabled("pci_link"))
  922                 return (0);
  923 
  924         ret = -1;
  925         ACPI_SERIAL_BEGIN(pci_link);
  926         if (first_time) {
  927                 TAILQ_INIT(&acpi_prt_entries);
  928                 TAILQ_INIT(&acpi_pci_link_entries);
  929                 acpi_pci_link_init_irq_penalty();
  930                 first_time = 0;
  931         }
  932 
  933         if (prtbuf == NULL)
  934                 goto out;
  935 
  936         prtp = prtbuf->Pointer;
  937         if (prtp == NULL)               /* didn't get routing table */
  938                 goto out;
  939 
  940         /* scan the PCI Routing Table */
  941         for (;;) {
  942                 prt = (ACPI_PCI_ROUTING_TABLE *)prtp;
  943 
  944                 if (prt->Length == 0)   /* end of table */
  945                     break;
  946 
  947                 error = acpi_pci_link_add_prt(dev, prt, busno);
  948                 if (ACPI_FAILURE(error)) {
  949                         ACPI_DEBUG_PRINT((ACPI_DB_WARN,
  950                             "couldn't add PCI interrupt link entry - %s\n",
  951                             AcpiFormatException(error)));
  952                 }
  953 
  954                 /* skip to next entry */
  955                 prtp += prt->Length;
  956         }
  957 
  958         if (bootverbose) {
  959                 printf("ACPI PCI link initial configuration:\n");
  960                 TAILQ_FOREACH(entry, &acpi_prt_entries, links) {
  961                         if (entry->busno != busno)
  962                                 continue;
  963                         acpi_pci_link_entry_dump(entry);
  964                 }
  965         }
  966 
  967         /* manual configuration. */
  968         TAILQ_FOREACH(entry, &acpi_prt_entries, links) {
  969                 int                     irq;
  970                 char                    prthint[32];
  971 
  972                 if (entry->busno != busno)
  973                         continue;
  974 
  975                 snprintf(prthint, sizeof(prthint),
  976                     "hw.acpi.pci.link.%d.%d.%d.irq", entry->busno,
  977                     (int)((entry->prt.Address & 0xffff0000) >> 16),
  978                     (int)entry->prt.Pin);
  979 
  980                 if (getenv_int(prthint, &irq) == 0)
  981                         continue;
  982 
  983                 if (acpi_pci_link_is_valid_irq(entry->pci_link, irq)) {
  984                         error = acpi_pci_link_set_irq(entry->pci_link, irq);
  985                         if (ACPI_FAILURE(error)) {
  986                                 ACPI_DEBUG_PRINT((ACPI_DB_WARN,
  987                                     "couldn't set IRQ to link entry %s - %s\n",
  988                                     acpi_name(entry->pci_link->handle),
  989                                     AcpiFormatException(error)));
  990                         }
  991                         continue;
  992                 }
  993 
  994                 /*
  995                  * Do auto arbitration for this device's PCI link
  996                  * if hint value 0 is specified.
  997                  */
  998                 if (irq == 0)
  999                         entry->pci_link->current_irq = 0;
 1000         }
 1001         ret = 0;
 1002 
 1003 out:
 1004         ACPI_SERIAL_END(pci_link);
 1005         return (ret);
 1006 }
 1007 
 1008 int
 1009 acpi_pci_link_resume(device_t dev)
 1010 {
 1011         struct acpi_prt_entry   *entry;
 1012         struct acpi_pci_link_entry *link;
 1013         ACPI_STATUS             error;
 1014 
 1015         ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
 1016 
 1017         if (acpi_disabled("pci_link"))
 1018                 return (0);
 1019 
 1020         /* Walk through all PRT entries for this PCI bridge. */
 1021         ACPI_SERIAL_BEGIN(pci_link);
 1022         TAILQ_FOREACH(entry, &acpi_prt_entries, links) {
 1023                 if (entry->pcidev != dev || entry->pci_link == NULL)
 1024                         continue;
 1025                 link = entry->pci_link;
 1026 
 1027                 /* If it's not routed, skip re-programming. */
 1028                 if ((link->flags & ACPI_LINK_ROUTED) == 0)
 1029                         continue;
 1030                 link->flags &= ~ACPI_LINK_ROUTED;
 1031 
 1032                 /* Program it to the same setting as before suspend. */
 1033                 error = acpi_pci_link_set_irq(link, link->current_irq);
 1034                 if (ACPI_FAILURE(error)) {
 1035                         ACPI_DEBUG_PRINT((ACPI_DB_WARN,
 1036                             "couldn't set IRQ to link entry %s - %s\n",
 1037                             acpi_name(link->handle),
 1038                             AcpiFormatException(error)));
 1039                 }
 1040         }
 1041         ACPI_SERIAL_END(pci_link);
 1042 
 1043         return (0);
 1044 }
 1045 
 1046 /*
 1047  * Look up a PRT entry for the given device.  We match based on the slot
 1048  * number (high word of Address) and pin number (note that ACPI uses 0
 1049  * for INTA).
 1050  *
 1051  * Note that the low word of the Address field (function number) is
 1052  * required by the specification to be 0xffff.  We don't risk checking
 1053  * it here.
 1054  */
 1055 struct acpi_prt_entry *
 1056 acpi_pci_find_prt(device_t pcibdev, device_t dev, int pin)
 1057 {
 1058         struct acpi_prt_entry *entry;
 1059         ACPI_PCI_ROUTING_TABLE *prt;
 1060 
 1061         ACPI_SERIAL_BEGIN(pci_link);
 1062         TAILQ_FOREACH(entry, &acpi_prt_entries, links) {
 1063                 prt = &entry->prt;
 1064                 if (entry->busno == pci_get_bus(dev) &&
 1065                     (prt->Address & 0xffff0000) >> 16 == pci_get_slot(dev) &&
 1066                     prt->Pin == pin)
 1067                         break;
 1068         }
 1069         ACPI_SERIAL_END(pci_link);
 1070         return (entry);
 1071 }
 1072 
 1073 /*
 1074  * Perform the actual programming for this link.  We attempt to route an
 1075  * IRQ, first the one set by the BIOS, and then a priority-sorted list.
 1076  * Only do the programming once per link.
 1077  */
 1078 int
 1079 acpi_pci_link_route(device_t dev, struct acpi_prt_entry *prt)
 1080 {
 1081         struct acpi_pci_link_entry *link;
 1082         int busno, i, irq;
 1083         ACPI_RESOURCE crsres;
 1084         ACPI_STATUS status;
 1085 
 1086         busno = pci_get_bus(dev);
 1087         link = prt->pci_link;
 1088         irq = PCI_INVALID_IRQ;
 1089         ACPI_SERIAL_BEGIN(pci_link);
 1090         if (link == NULL || link->number_of_interrupts == 0)
 1091                 goto out;
 1092 
 1093         /* If already routed, just return the current setting. */
 1094         if (link->flags & ACPI_LINK_ROUTED) {
 1095                 irq = link->current_irq;
 1096                 goto out;
 1097         }
 1098 
 1099         /* Update all IRQ weights to determine our priority list. */
 1100         acpi_pci_link_update_irq_penalty(prt->pcidev, busno);
 1101         acpi_pci_link_set_bootdisabled_priority();
 1102         acpi_pci_link_fixup_bootdisabled_link();
 1103 
 1104         /*
 1105          * First, attempt to route the initial IRQ, if valid, since it was
 1106          * the one set up by the BIOS.  If this fails, route according to
 1107          * our priority-sorted list of IRQs.
 1108          */
 1109         status = AE_NOT_FOUND;
 1110         irq = link->initial_irq;
 1111         if (irq)
 1112                 status = acpi_pci_link_set_irq(link, irq);
 1113         for (i = 0; ACPI_FAILURE(status) && i < link->number_of_interrupts;
 1114             i++) {
 1115                 irq = link->sorted_irq[i];
 1116                 status = acpi_pci_link_set_irq(link, irq);
 1117                 if (ACPI_FAILURE(status)) {
 1118                         device_printf(dev, "_SRS failed, irq %d via %s\n",
 1119                             irq, acpi_name(link->handle));
 1120                 }
 1121         }
 1122         if (ACPI_FAILURE(status)) {
 1123                 irq = PCI_INVALID_IRQ;
 1124                 goto out;
 1125         }
 1126 
 1127         /* Update the penalty now that there's another user for this IRQ. */
 1128         irq_penalty[irq] += 10 * link->references;
 1129 
 1130         /* Configure trigger/polarity for the new IRQ. */
 1131         bcopy(&link->possible_resources, &crsres, sizeof(crsres));
 1132         if (crsres.Id == ACPI_RSTYPE_IRQ) {
 1133                 crsres.Data.Irq.NumberOfInterrupts = 1;
 1134                 crsres.Data.Irq.Interrupts[0] = irq;
 1135         } else {
 1136                 crsres.Data.ExtendedIrq.NumberOfInterrupts = 1;
 1137                 crsres.Data.ExtendedIrq.Interrupts[0] = irq;
 1138         }
 1139         acpi_config_intr(dev, &crsres);
 1140 
 1141 out:
 1142         ACPI_SERIAL_END(pci_link);
 1143         return (irq);
 1144 }

Cache object: 7a37dc83835b1069e8072b9eef5b6f58


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