Logo Search packages:      
Sourcecode: lcrash version File versions  Download package

kl_pci.c

/*
 * $Id: kl_pci.c,v 1.1 2004/12/21 23:26:20 tjm Exp $
 *
 * This file is part of libhwconfig.
 * A library which provides access to Linux system kernel dumps.
 *
 * Created by Silicon Graphics, Inc.
 *
 * Copyright (C) 2003-2004 Silicon Graphics, Inc. All rights reserved.
 *
 * This code is free software; you can redistribute it and/or modify
 * it under the terms of the GNU Lesser Public License as published by
 * the Free Software Foundation; either version 2.1 of the License, or
 * (at your option) any later version. See the file COPYING for more
 * information.
 */
#include <klib.h>
#include <kl_pci.h>

int pci_bus_count = 0;
int pci_dev_count = 0;
pci_bus_t *pci_buses = (pci_bus_t *)NULL;
pci_dev_t *pci_devices = (pci_dev_t *)NULL;
int PCI_DEV_SZ, PCI_BUS_SZ;

typedef struct pci_class_name {
      uint8_t            class_id;
      char              *short_name;
} pci_class_name_t;

pci_class_name_t pci_class_name[] = {
      { 0x00, "UNCLASSIFIED_DEV" },
      { 0x01,     "MASS_STORAGE_CTLR" },
      { 0x02, "NETWORK_CTLR" },
      { 0x03, "DISPLAY_CTLR" },
      { 0x04, "MULTIMEDIA_CTLR" },
      { 0x05, "MEMORY_CTLR" },
      { 0x06, "BRIDGE" },
      { 0x07, "COMM_CTLR" },
      { 0x08, "SYSTEM_PERIPH" },
      { 0x09, "INPUT_DEV_CTLR" },
      { 0x0a, "DOCKING_STATION" },
      { 0x0b, "PROCESSOR" },
      { 0x0c, "SERIAL_BUS_CTLR" },
      { 0x0d, "WIRELESS_CTLR" },
      { 0x0e, "INTELLIGENT_CTLR" },
      { 0x0f, "SATELLITE_COMM_CTLR" },
      { 0x10, "ENCRYPTION_CTLR" },
      { 0x11, "SIG_PROCESS_CTLR" },
      { 0, 0 }
};

typedef struct pci_subclass_name {
      uint32_t     class_id;  /* CLASS|SUBCLASS/PROGIF */
      char              *short_name;
} pci_subclass_name_t;

pci_subclass_name_t pci_subclass_name[] = {

      { 0x000000, "UNCLASSIFIED_DEV" },

      { 0x010000, "SCSI_CTLR" },
      { 0x010100, "IDE_CTLR" },
      { 0x010200, "FLOPPY_CTLR" },
      { 0x010300, "IPI_CTLR" },
      { 0x010400, "RAID_CTLR" },
      { 0x018000, "MASS_STORAGE_CTLR" },

      { 0x020000, "ETHERNET_CTLR" },
      { 0x020100, "TOKEN_RING_CTLR" },
      { 0x020200, "FDDI_CTLR" },
      { 0x020300, "ATM_CTLR" },
      { 0x020400, "ISDN_CTLR" },
      { 0x028000, "NETWORK_CTLR" },

      { 0x030000, "VGA_CTLR" },
      { 0x030100, "XGA_CTLR" },
      { 0x030200, "3D_CTLR" },
      { 0x038000, "DISPLAY_CTLR" },

      { 0x040000, "VIDEO_CTLR" },
      { 0x040100, "AUDIO_CTLR" },
      { 0x040200, "TELEPHONY_DEV" },
      { 0x048000, "MULTIMEDIA_CTLR" },

      { 0x050000, "RAM_MEM_CTLR" },
      { 0x050100, "FLASH_MEM_CTLR" },
      { 0x058000, "MEMORY_CTLR" },

      { 0x060000, "HOST_BRIDGE" },
      { 0x060100, "ISA_BRIDGE" },
      { 0x060200, "EISA_BRIDGE" },
      { 0x060300, "MICROCHANNEL_BRIDGE" },
      { 0x060400, "PCI_BRIDGE" },
      { 0x060500, "PCMCIA_BRIDGE" },
      { 0x060600, "NUBUS_BRIDGE" },
      { 0x060700, "CARDBUS_BRIDGE" },
      { 0x060800, "RACEWAY_BRIDGE" },
      { 0x060900, "PCI_TO_PCI_BRIDGE" },
      { 0x060a00, "INFINIBAND_TO_PCI_BRIDGE" },
      { 0x068000, "BRIDGE" },

      { 0x070000, "SERIAL_CTLR" },
      { 0x070100, "PARALLEL_CTLR" },
      { 0x070200, "MULTIPORT_SERIAL_CTLR" },
      { 0x070300, "MODEM" },
      { 0x078000, "COMM_CTLR" },

      { 0x080000, "PIC" },
      { 0x080100, "DMA_CTLR" },
      { 0x080200, "TIMER" },
      { 0x080300, "RTC" },
      { 0x080400, "PCI_HOT_PLUG_CTLR" },
      { 0x088000, "SYS_PERIPHERAL" },

      { 0x090000, "KEYBOARD_CTLR" },
      { 0x090100, "DIGITIZER_PEN" },
      { 0x090200, "MOUSE_CTLR" },
      { 0x090300, "SCANNER_CTLR" },
      { 0x090400, "GAMEPORT_CTLR" },
      { 0x098000, "INPUT_DEV_CTLR" },

      { 0x0a0000, "DOCKING_STATION" },
      { 0x0a8000, "DOCKING_STATION" },

      { 0x0b0000, "386_PROCESSOR" },
      { 0x0b0100, "486_PROCESSOR" },
      { 0x0b0200, "PENTIUM_PROCESSOR" },
      { 0x0b1000, "ALPHA_PROCESSOR" },
      { 0x0b2000, "POWER_PC_PROCESSOR" },
      { 0x0b3000, "MIPS_PROCESSOR" },
      { 0x0b4000, "CO-PROCESSOR" },

      { 0x0c0000, "FIREWHIRE" },
      { 0x0c0100, "ACCESS_BUS" },
      { 0x0c0200, "SSA" },
      { 0x0c0300, "USB_CTLR" },
      { 0x0c0400, "FIBER_CHANNEL" },
      { 0x0c0500, "SMBUS" },
      { 0x0c0600, "INFINIBAND" },

      { 0x0d0000, "IRDA_CTLR" },
      { 0x0d0100, "CONSMER_IR_CTLR" },
      { 0x0d1000, "RF_CTLR" },
      { 0x0d8000, "WIRELESS_CTLR" },

      { 0x0e0000, "I20" },

      { 0x0f0000, "SATELLITE_TV_CTLR" },
      { 0x0f0100, "SATELLITE_AUDIO_CTLR" },
      { 0x0f0300, "SATELLITE_VOICE_CTLR" },
      { 0x0f0400, "SATELLITE_DATA_CTLR" },

      { 0x100000, "NETWORK_ENCRYPT_DEV" },
      { 0x101000, "ENTERTAINMENT_ENCRYPT_DEV" },
      { 0x108000, "ENCRYPTION_CTLR" },

      { 0x110000, "DPIO_MODULE" },
      { 0x110100, "PERFORMANCE_CNTR" },
      { 0x111000, "iCOMM_CYNCHRONIZER" },
      { 0x118000, "SIG_PROCESS_CTLR" },

      { 0, 0 }
};



/* Control structure for accessing lines in a text file. Used by pciinfo
 * code...
 *
 * XXX -- Should generalize and move to libutil
 */
typedef struct txtfile_s {
      char        *fname;  /* name of text file to read blocks from */
      int          fd;     /* file descriptor (-1 if closed) */
      char        *blkptr; /* pointer to data block */
      int          blksz;  /* total size of data block */
      int          datasz; /* size of valid data in block */
      char        *ptr;        /* current pointer in data block */
} txtfile_t;

#define BUFMAX 4096

char *pciids_file="/usr/share/pci.ids";
txtfile_t *pciinfo = (txtfile_t *)NULL;
pciinfo_vendor_t *pci_vendors = (pciinfo_vendor_t *)NULL;
pciinfo_class_t *pci_classes = (pciinfo_class_t *)NULL;

static txtfile_t *
alloc_txtfile(char *fname, int bufsz)
{
      txtfile_t *pciinfo;

      if (!(pciinfo = (txtfile_t *)malloc(sizeof(txtfile_t)))) {
            return((txtfile_t *)NULL);
      }
      if (bufsz > BUFMAX) {
            bufsz = BUFMAX;
      }
      pciinfo->fname = (char *)malloc(strlen(fname) + 1);
      strcpy(pciinfo->fname, fname);
      pciinfo->fd = -1;
      pciinfo->blkptr = (char *)malloc(bufsz);
      pciinfo->blksz = bufsz;
      pciinfo->datasz = 0;
      pciinfo->ptr = NULL;
      return(pciinfo);
}

#ifdef NOTUSED
static void
free_txtfile(txtfile_t *pciinfo)
{
      if (pciinfo->blkptr) {
            free(pciinfo->blkptr);
      }
      if (pciinfo->fname) {
            free(pciinfo->fname);
      }
      free(pciinfo);
}
#endif

static void
close_txtfile(txtfile_t *pciinfo)
{
      if (pciinfo->fd >= 0) {
            /* If the file is open, close it first and then
             * reopen it...
             */
            (void)close(pciinfo->fd);
            pciinfo->fd = -1;
      }
}

static int
open_txtfile(txtfile_t *pciinfo)
{
      int sz;

      /* Make sure and close the file if it's open...
       */
      close_txtfile(pciinfo);

        if ((pciinfo->fd = open(pciinfo->fname, 0)) == -1) {
                perror("open");
                return(1);
        }

      /* Reset to the start of the file
       */
        if (lseek(pciinfo->fd, 0, SEEK_SET) == -1) {
                perror("lseek");
                return(1);
        }

      /* Read in the first block and finish setting up the control
       * information.
       */
      if ((sz = read(pciinfo->fd, pciinfo->blkptr, pciinfo->blksz)) == -1) {
            perror("read");
            return(1);
      }
      if (sz == 0) {
            fprintf(stdout, "EMPTY FILE!\n");
            return(1);
      }
      pciinfo->datasz = sz;
      pciinfo->ptr = NULL; /* no line identified yet */
      return(0);
}

static int
next_block(txtfile_t *pciinfo)
{
      int extra = 0, datasz, sz;
      char *ptr;

      datasz = pciinfo->blksz;
      if ((ptr = pciinfo->ptr)) {
            extra = pciinfo->datasz - (int)(ptr - pciinfo->blkptr);
            memmove(pciinfo->blkptr, ptr, extra);
      }
      pciinfo->ptr = pciinfo->blkptr;
      ptr = pciinfo->ptr + extra;

      /* Read in the next block and finish setting up the block record.
       */
      if ((sz = read(pciinfo->fd, ptr, (datasz - extra))) == -1) {
            perror("read");
            return(1);
      }
      if (sz == 0) {
            return(1);
      }
      pciinfo->datasz = (sz + extra);
      return(0);
}

static char *
next_line(txtfile_t *pciinfo)
{
      size_t unprocessed;
      char *c;

      if (pciinfo->ptr == NULL) {
            /* First time for this file
             */
            pciinfo->ptr = pciinfo->blkptr;
      } else {
            pciinfo->ptr += strlen(pciinfo->ptr) + 1; 
            if (pciinfo->ptr >= (pciinfo->blkptr + pciinfo->datasz)) {
                  /* We need to get next block
                   */
                  if (next_block(pciinfo)) {
                        return((char *)NULL);
                  }
            }
      }

again:
      unprocessed = pciinfo->datasz - (int)(pciinfo->ptr - pciinfo->blkptr);
      if ((c = memchr(pciinfo->ptr, '\n', unprocessed))) {
            *c = 0;
      } else {
            /* We are near the end of the block and thre is not
             * a complete line there. We need to read in the next
             * chunk of data (minus the number of unprocessed bytes
             * still in our block).
             */
            if (next_block(pciinfo)) {
                  return((char *)NULL);
            }
            goto again;
      }
      return(pciinfo->ptr);
}

static void
get_progifs(txtfile_t *pciinfo, pciinfo_subclass_t *subclassp)
{
      pciinfo_progif_t *progifp, *lastprogifp = (pciinfo_progif_t *)NULL;
      char *line;
      char valstr[20];
      long value;

      line = pciinfo->ptr;
      do {
            if (*line == '#') {
                  /* This is a comment...
                   */
                  continue;
            }
            if ((*line != '\t') || (*(line+1) != '\t')) {
                  /* we are at the next class or subclass record, 
                   * so return...
                   */
                  return;
            }
            progifp = (pciinfo_progif_t *)malloc(sizeof(pciinfo_progif_t));

            strncpy(valstr, "0x", 2);
            valstr[2] = 0;
            strncat(valstr+2, line+2, 2);
            valstr[4] = 0;
            value = strtol(valstr, (char**)NULL, 16);
            progifp->progif_id = (uint8_t)value;

            progifp->name = (char *)malloc(strlen(line+7)+1);
            strcpy(progifp->name, (line+7));

            /* Link the new progif record in...
             */
            if (lastprogifp) {
                  lastprogifp->next = progifp;
                  progifp->prev = lastprogifp;
                  progifp->next = (pciinfo_progif_t*)NULL;
            } else {
                  subclassp->progifs = progifp;
                  progifp->next = (pciinfo_progif_t*)NULL;
                  progifp->prev = (pciinfo_progif_t*)NULL;
            }
            lastprogifp = progifp;
            progifp->subclass = subclassp;
      } while ((line = next_line(pciinfo)));
}


static void
get_subclasses(txtfile_t *pciinfo, pciinfo_class_t *classp)
{
      pciinfo_subclass_t *subclassp = (pciinfo_subclass_t *)NULL;
      pciinfo_subclass_t *lastsubclassp = (pciinfo_subclass_t *)NULL;
      char *line;
      char valstr[20];
      long value;

      /* Now go through the class section
       */
      while ((line = next_line(pciinfo))) {
again:
            if (*line == '#') {
                  /* This is a comment...
                   */
                  continue;
            }
            if (*line != '\t') {
                  /* we are at the next class, so return...
                   */
                  return;
            }
            if  (*(line+1) == '\t') {
                  if (!subclassp) {
                        /* XXX -- print error msg...
                         */
                        return;
                  }
                  get_progifs(pciinfo, subclassp);

                  /* Jump to again: because we already have the
                   * next line (from get_progifs()).
                   */
                  line = pciinfo->ptr;
                  goto again;
            } else {
                  subclassp = (pciinfo_subclass_t *)
                        malloc(sizeof(pciinfo_subclass_t));
                  strncpy(valstr, "0x", 2);
                  valstr[2] = 0;
                  strncat(valstr+2, line+1, 2);
                  valstr[4] = 0;
                  value = strtol(valstr, (char**)NULL, 16);
                  subclassp->subclass_id = (uint8_t)value;
                  subclassp->name = (char *)malloc(strlen(line+5)+1);
                  strcpy(subclassp->name, (line+5));

                  /* Link the new subclass record in...
                   */
                  if (lastsubclassp) {
                        lastsubclassp->next = subclassp;
                        subclassp->prev = lastsubclassp;
                        subclassp->next = (pciinfo_subclass_t*)NULL;
                  } else {
                        classp->sub_classes = subclassp;
                        subclassp->next = (pciinfo_subclass_t*)NULL;
                        subclassp->prev = (pciinfo_subclass_t*)NULL;
                  }
                  lastsubclassp = subclassp;
                  subclassp->class = classp;
            }
      }
}

static char *
get_class(txtfile_t *pciinfo, uint8_t classid)
{
      char *line;
      char valstr[20];

      sprintf(valstr, "C %02hhx", classid);
      
      while ((line = next_line(pciinfo))) {
            if (!strncmp(valstr, line, 4)) {
                  /* found it!
                   */
                  break;
            }
      }
      return(line);
}

pciinfo_class_t *
find_class(txtfile_t *pciinfo, uint8_t classid)
{
      char *line;
      pciinfo_class_t *classp = (pciinfo_class_t *)NULL;
      pciinfo_class_t *nclassp = (pciinfo_class_t *)NULL;

      /* Check to see if we already have info for this class. If we
       * have some ventor records, but not this one, then drop below
       * with pvenp pointing to the the record where the new vendor
       * record should be inserted. The vendor records need to be
       * linked together in order (for vendor ID).
       */
      if ((classp = pci_classes)) {
            while (classp) {
                  if (classp->class_id == classid) {
                        return(classp);
                  }
                  if (classid < classp->class_id) {
                        if (!classp->next) {
                              /* Don't fall off the end of our
                               * list.
                               */
                              break;
                        } else {
                              classp = classp->next;
                        }
                  } else {
                        break;
                  }
            }
      }

      /* We did not find a class record on our list (or we don't have
       * a list yet). Search the pci.ids file for the class record and
       * it's associated subclass and progif records.
       */
      open_txtfile(pciinfo);
      if (!(line = get_class(pciinfo, classid))) {
            close_txtfile(pciinfo);
            return((pciinfo_class_t *)NULL);
      }

      /* Create a class record and then proceed to gather sub-class
       * and prog-if info..
       */
      if (!(nclassp = (pciinfo_class_t *)malloc(sizeof(pciinfo_class_t)))) {
            close_txtfile(pciinfo);
            return((pciinfo_class_t *)NULL);
      }
      nclassp->class_id = classid;
      nclassp->name = (char *)malloc(strlen(line+3)+1);
      strcpy(nclassp->name, (line+3));
      if (classp) {
            if (classp->class_id < nclassp->class_id) {
                  /* Add the new class record after the current one.
                   * If we are not at the end of the chain, make sure
                   * the next record points back to our new record.
                   */
                  if (classp->next) {
                        nclassp->next = classp->next;
                        nclassp->next->prev = nclassp;
                  }
                  classp->next = nclassp;
                  nclassp->prev = classp;
            } else {
                  if (classp->prev) {
                        nclassp->prev = classp->prev;
                        nclassp->prev->next = nclassp;
                        nclassp->next = classp;
                  } else {
                        nclassp->next = classp;
                        classp->prev = nclassp;
                        pci_classes = nclassp;
                  }
            }
      } else {
            nclassp->prev = nclassp->next = (pciinfo_class_t *)NULL;
            pci_classes = nclassp;
      }
      get_subclasses(pciinfo, nclassp);
      close_txtfile(pciinfo);
      return(nclassp);
}

static void
get_subsystems(txtfile_t *pciinfo, pciinfo_dev_t *vdevp)
{
      pciinfo_subsys_t *vsubsys, *lastvsybsys = (pciinfo_subsys_t *)NULL;
      char *line;
      char valstr[20];
      long value;

      line = pciinfo->ptr;
      do {
            if (*line == '#') {
                  /* This is a comment...
                   */
                  continue;
            }
            if ((*line != '\t') || (*(line+1) != '\t')) {
                  /* we are at the next device or vendor record, 
                   * so return...
                   */
                  return;
            }
            vsubsys = (pciinfo_subsys_t *)malloc(sizeof(pciinfo_subsys_t));

            strncpy(valstr, "0x", 2);
            valstr[2] = 0;
            strncat(valstr+2, line+2, 4);
            valstr[6] = 0;
            value = strtol(valstr, (char**)NULL, 16);
            vsubsys->ven_id = (short)value;

            valstr[2] = 0;
            strncat(valstr+2, line+7, 4);
            valstr[6] = 0;
            value = strtol(valstr, (char**)NULL, 16);
            vsubsys->dev_id = (short)value;

            vsubsys->name = (char *)malloc(strlen(line+13)+1);
            strcpy(vsubsys->name, (line+13));

            /* Link the new sybsys record in...
             */
            if (lastvsybsys) {
                  lastvsybsys->next = vsubsys;
                  vsubsys->prev = lastvsybsys;
                  vsubsys->next = (pciinfo_subsys_t*)NULL;
            } else {
                  vdevp->subsystems = vsubsys;
                  vsubsys->next = (pciinfo_subsys_t*)NULL;
                  vsubsys->prev = (pciinfo_subsys_t*)NULL;
            }
            lastvsybsys = vsubsys;
            vsubsys->device = vdevp;
      } while ((line = next_line(pciinfo)));
}

static void
get_devices(txtfile_t *pciinfo, pciinfo_vendor_t *pvenp)
{
      pciinfo_dev_t *vdevp = (pciinfo_dev_t *)NULL;
      pciinfo_dev_t *lastvdevpi = (pciinfo_dev_t *)NULL;
      char *line;
      char valstr[20];
      long dev_id;

      while ((line = next_line(pciinfo))) {
again:
            if (*line == '#') {
                  /* This is a comment...
                   */
                  continue;
            }
            if (*line != '\t') {
                  /* we are at the next vendor, so return...
                   */
                  return;
            }
            if  (*(line+1) == '\t') {
                  if (!vdevp) {
                        /* XXX -- print error msg...
                         */
                        return;
                  }
                  get_subsystems(pciinfo, vdevp);
                  /* Jump to again: because we already have the
                   * next line (from get_subsystems()).
                   */
                  line = pciinfo->ptr;
                  goto again;
            } else {
                  vdevp = (pciinfo_dev_t *)malloc(sizeof(pciinfo_dev_t));
                  strncpy(valstr, "0x", 2);
                  valstr[2] = 0;
                  strncat(valstr+2, line+1, 4);
                  valstr[6] = 0;
                  dev_id = strtol(valstr, (char**)NULL, 16);
                  vdevp->dev_id = (short)dev_id;
                  vdevp->name = (char *)malloc(strlen(line+7)+1);
                  strcpy(vdevp->name, (line+7));

                  /* Link the new device record in...
                   */
                  if (lastvdevpi) {
                        lastvdevpi->next = vdevp;
                        vdevp->prev = lastvdevpi;
                        vdevp->next = (pciinfo_dev_t*)NULL;
                  } else {
                        pvenp->devices = vdevp;
                        vdevp->next = (pciinfo_dev_t*)NULL;
                        vdevp->prev = (pciinfo_dev_t*)NULL;
                  }
                  lastvdevpi = vdevp;
                  vdevp->vendor = pvenp;
            }
      }
}

static char *
get_vendor(txtfile_t *pciinfo, short ven_id)
{
      char *line;
      char ven_string[20];

      sprintf(ven_string, "%04hx", ven_id);
      
      while ((line = next_line(pciinfo))) {
            if (!strncmp(ven_string, line, 4)) {
                  /* found it!
                   */
                  break;
            }
      }
      return(line);
}

pciinfo_vendor_t *
find_vendor(txtfile_t *pciinfo, short ven_id)
{
      char *line;
      pciinfo_vendor_t *npvenp, *pvenp = (pciinfo_vendor_t *)NULL;

      /* Check to see if we already have info for this vendor. If we
       * have some ventor records, but not this one, then drop below
       * with pvenp pointing to the the record where the new vendor
       * record should be inserted. The vendor records need to be
       * linked together in order (for vendor ID).
       */
      if ((pvenp = pci_vendors)) {
            while (pvenp) {
                  if (pvenp->ven_id == ven_id) {
                        return(pvenp);
                  }
                  if (ven_id < pvenp->ven_id) {
                        if (!pvenp->next) {
                              /* Don't fall off the end of our
                               * list.
                               */
                              break;
                        } else {
                              pvenp = pvenp->next;
                        }
                  } else {
                        break;
                  }
            }
      }

      /* We did not find a vendor record on our list (or we don't have
       * a list yet). Search the pci.ids file for the vendor record and
       * it's associated device and subsystem records.
       */
      open_txtfile(pciinfo);
      if (!(line = get_vendor(pciinfo, ven_id))) {
            close_txtfile(pciinfo);
            return((pciinfo_vendor_t *)NULL);
      }

      /* Create a vendor record and then proceed to gather device
       * and subsystem info..
       */
      if (!(npvenp = (pciinfo_vendor_t *)malloc(sizeof(pciinfo_vendor_t)))) {
            close_txtfile(pciinfo);
            return((pciinfo_vendor_t *)NULL);
      }
      npvenp->ven_id = ven_id;
      npvenp->name = (char *)malloc(strlen(line+6)+1);
      strcpy(npvenp->name, (line+6));
      if (pvenp) {
            if (pvenp->ven_id < npvenp->ven_id) {
                  /* Add the new vendor record after the current one.
                   * If we are not at the end of the chain, make sure
                   * the next record points back to our new record.
                   */
                  if (pvenp->next) {
                        npvenp->next = pvenp->next;
                        npvenp->next->prev = npvenp;
                  }
                  pvenp->next = npvenp;
                  npvenp->prev = pvenp;
            } else {
                  if (pvenp->prev) {
                        npvenp->prev = pvenp->prev;
                        npvenp->prev->next = npvenp;
                        npvenp->next = pvenp;
                  } else {
                        npvenp->next = pvenp;
                        pvenp->prev = npvenp;
                        pci_vendors = npvenp;
                  }
            }
      } else {
            npvenp->prev = npvenp->next = (pciinfo_vendor_t*)NULL;
            pci_vendors = npvenp;
      }
      get_devices(pciinfo, npvenp);
      close_txtfile(pciinfo);
      return(npvenp);
}

pciinfo_vendor_t *
pciinfo_vendor(short ven_id)
{
      pciinfo_vendor_t *pvenp;

      pvenp = find_vendor(pciinfo, ven_id);
      return(pvenp);
}

pciinfo_dev_t *
pciinfo_device(short ven_id, short dev_id)
{
      pciinfo_vendor_t *pvenp;
      pciinfo_dev_t *pdevp;

      if (!(pvenp = pciinfo_vendor(ven_id))) {
            return((pciinfo_dev_t *)NULL);
      }
      pdevp = pvenp->devices;
      while (pdevp) {
            if (pdevp->dev_id == dev_id) {
                  break;
            }
            pdevp = pdevp->next;
      }
      return(pdevp);
}

#ifdef DEBUG
static void
print_vendor(pciinfo_vendor_t *vendor)
{
      pciinfo_dev_t *device;
        pciinfo_subsys_t *subsystem;

      fprintf(stdout, "Vendor: ID=%04hx, Name=\"%s\"\n", 
            vendor->ven_id, vendor->name);
      device = vendor->devices;
      while (device) {
            fprintf(stdout, "  Device: ID=%04hx, Name=\"%s\"\n", 
                  device->dev_id, device->name);
            subsystem = device->subsystems;
            while (subsystem) {
                  fprintf(stdout, "    Subsystem: SUBVEN_ID=%04hx, "
                        "SUBDEV_ID=%04hx, Name=\"%s\"\n", 
                        subsystem->ven_id, subsystem->dev_id, 
                        subsystem->name);
                  subsystem = subsystem->next;
            }
            device = device->next;
      }
}
#endif

static int
pciinfo_init(char *fname)
{
      if (fname) {
            pciinfo = alloc_txtfile(fname, BUFMAX);
      } else {
            pciinfo = alloc_txtfile(pciids_file, BUFMAX);
      }
      if (pciinfo) {
            return(0);
      }
      return(1);
}

pci_vendor_t _pci_vendor;
pci_device_t _pci_device;

/*
 * find_pci_vendor()
 */
pci_vendor_t *
find_pci_vendor(int VenId)
{
      pci_vendor_t *pciven = (pci_vendor_t*)NULL;
      pciinfo_vendor_t *pvenp;

      if ((pvenp = pciinfo_vendor(VenId))) {
            pciven = &_pci_vendor;
            pciven->VenId = pvenp->ven_id;
            pciven->VenShortName = NULL; /* For now */
            pciven->VenFullName = pvenp->name;
            
      }
      return (pciven);
}

/*
 * find_pci_device()
 */
pci_device_t *
find_pci_device(int VenId, int DevId)
{
      pci_device_t *pcidevp = (pci_device_t *)NULL;
      pciinfo_dev_t *pvdevp;

      if ((pvdevp = pciinfo_device(VenId, DevId))) {
            pcidevp = &_pci_device;
            pcidevp->VenId = pvdevp->vendor->ven_id;
            pcidevp->DevId = pvdevp->dev_id;
            pcidevp->DevName = NULL; /* For now */
            pcidevp->DevDesc = pvdevp->name;
            
      }
      return (pcidevp);
}

/*
 * find_pci_dev()
 */
pci_dev_t *
find_pci_dev(kaddr_t addr)
{
      pci_dev_t *pci_dev;

      if (!(pci_dev = pci_devices)) {
            return((pci_dev_t *)NULL);
      }
      do {
            if (pci_dev->addr == addr) {
                  break;
            }
            pci_dev = (pci_dev_t *)pci_dev->g_next;
      } while (pci_dev != pci_devices);
      return(pci_dev);
}

/*
 * get_pci_dev()
 */
static pci_dev_t *
get_pci_dev(kaddr_t addr, int flg)
{
      pci_dev_t *pci_dev;
      void *ptr;
      
      pci_dev = (pci_dev_t *)kl_alloc_block(sizeof(pci_dev_t), flg);
      if (!pci_dev) {
            return((pci_dev_t *)NULL);
      }
      ptr = kl_alloc_block(PCI_DEV_SZ, flg);
      GET_BLOCK(addr, PCI_DEV_SZ, ptr);
      if (KL_ERROR) {
            kl_free_block(pci_dev);
            return((pci_dev_t *)NULL);
      }
      pci_dev->addr = addr;
      pci_dev->ptr = ptr;

      return(pci_dev);
}

/*
 * get_pci_bus()
 */
static pci_bus_t *
get_pci_bus(kaddr_t addr, int flg)
{
      pci_bus_t *pci_bus;
      void *ptr;
      
      pci_bus = (pci_bus_t *)kl_alloc_block(sizeof(pci_bus_t), flg);
      if (!pci_bus) {
            return((pci_bus_t *)NULL);
      }
      ptr = kl_alloc_block(PCI_BUS_SZ, flg);
      GET_BLOCK(addr, PCI_BUS_SZ, ptr);
      if (KL_ERROR) {
            kl_free_block(pci_bus);
            return((pci_bus_t *)NULL);
      }
      pci_bus->number = KL_UINT(ptr, "pci_bus", "number");
      pci_bus->addr = addr;
      pci_bus->ptr = ptr;
      return(pci_bus);
}

/*
 * get_pci_dev_detail()
 */
static void
get_pci_dev_detail(pci_dev_t *pci_dev)
{
      void *ptr = pci_dev->ptr;
      int devfn;

      /* Fill in some of the key details for this device
       */
      pci_dev->class = (int)KL_UINT(ptr, "pci_dev", "class");
      pci_dev->vendor = (unsigned short)KL_UINT(ptr, "pci_dev", "vendor");
      pci_dev->device = (unsigned short)KL_UINT(ptr, "pci_dev", "device");
      pci_dev->bus_num = pci_dev->bus->number;
      devfn = (int)KL_UINT(ptr, "pci_dev", "devfn");
      pci_dev->slot_num = (((devfn) >> 3) & 0x1f);
      pci_dev->fun_num = ((devfn) & 0x07);
}

/*
 * kl_pci_init()
 */
int
kl_pci_init(void)
{
      syment_t *sp;
      kaddr_t addr, pci_root_buses, pci_devices_addr;
      pci_bus_t *pci_bus;
      pci_dev_t *pci_dev;

      if (pciinfo_init(NULL)) {
            return(1);
      }

      if (pci_buses) {
            /* We have already gathered up the pci info. Return with
             * no error.
             */
            return(0);
      }
      PCI_DEV_SZ = kl_struct_len("pci_dev");
      PCI_BUS_SZ = kl_struct_len("pci_bus");
      
      if (!(sp = kl_lkup_symname("pci_root_buses"))) {
            return(1);
      }
      /* Save the address of pci_root_buses. We need to use this 
       * address to determine when we have gotten to the end of the
       * list of pci_buses.
       */
      pci_root_buses = sp->s_addr;

      /* Get the first bus entry
       */
      addr = KL_VREAD_PTR(pci_root_buses);
      if (!(pci_buses = get_pci_bus(addr, K_PERM))) {
                return(1);
        }

      /* Now walk the linked list of pci_bus structs and add them to
       * our list. Since we are walking through the 'node' linked list,
       * we can just use the pointers in the list without adjusting
       * them. For list_head lists that aren't the first element of a
       * struct, we have to subtract the offset to the list_head struct
       * from the pointer.
       */
      addr = kl_kaddr(pci_buses->ptr, "list_head", "next");
      while (addr != pci_root_buses) {
            pci_bus = get_pci_bus(addr, K_PERM);
            list_add(&pci_buses->bus_list, &pci_bus->bus_list);
            pci_bus_count++;
            addr = kl_kaddr(pci_bus->ptr, "list_head", "next");
      }

      /* Now that we have all the buses loaded in, we can gather up
       * the pci device information. We'll string together all of the
       * devices and then thread them from the individual pci_bus 
       * structs.
       */
      if (!(sp = kl_lkup_symname("pci_devices"))) {
            return(1);
      }
      pci_devices_addr = sp->s_addr;

      /* Get the first bus_dev entry
       */
      addr = KL_VREAD_PTR(pci_devices_addr);
      if (!(pci_devices = get_pci_dev(addr, K_PERM))) {
                return(1);
        }

      /* Walk the global linked list of pci_dev structs (without
       * regard to which bus the device is attached to). Since we are
       * walking the global_list, which is the first element in the
       * pci_dev struct, we can just use the pointers in the list
       * without adjusting them).
       */
      addr = kl_kaddr(pci_devices->ptr, "list_head", "next");
      while (addr != pci_devices_addr) {
            pci_dev = get_pci_dev(addr, K_PERM);
            list_add(&pci_devices->global_list, &pci_dev->global_list);
            pci_dev_count++;
            addr = kl_kaddr(pci_dev->ptr, "list_head", "next");
      }

      /* Now, we need to link up each of the pci_dev structs attached 
       * to each of the buses.
       */
      pci_dev = pci_devices;
      do {
            addr = kl_kaddr(pci_dev->ptr, "pci_dev", "bus");
            pci_bus = pci_buses;
            do {
                  if (pci_bus->addr == addr) {
                        /* link to the pci_bus struct that 
                         * this device is attached to.
                         */
                        pci_dev->bus = pci_bus;
                        get_pci_dev_detail(pci_dev);
                        list_add(&pci_bus->bus_devices, 
                              &pci_dev->bus_list);
                        break;
                  }
                  pci_bus = (pci_bus_t *)pci_bus->bus_list.next;
            } while (pci_bus != pci_buses);
            pci_dev = (pci_dev_t *)pci_dev->g_next;
      } while (pci_dev != pci_devices);
      return(0);
}


Generated by  Doxygen 1.6.0   Back to index