]> mj.ucw.cz Git - pciutils.git/commitdiff
Added library functions for accessing PCI capabilities.
authorMartin Mares <mj@ucw.cz>
Mon, 10 Nov 2008 23:03:01 +0000 (00:03 +0100)
committerMartin Mares <mj@ucw.cz>
Mon, 10 Nov 2008 23:03:01 +0000 (00:03 +0100)
lib/Makefile
lib/access.c
lib/caps.c [new file with mode: 0644]
lib/generic.c
lib/internal.h
lib/pci.h

index 31ca1c106ddc78149edd3ee2041846af4e5508a7..a19aa3399118765797b2a29df70be5b34de79964 100644 (file)
@@ -3,7 +3,7 @@
 
 # Expects to be invoked from the top-level Makefile and uses lots of its variables.
 
-OBJS=init access generic dump names filter names-hash names-parse names-net names-cache params
+OBJS=init access generic dump names filter names-hash names-parse names-net names-cache params caps
 INCL=internal.h pci.h config.h header.h sysdep.h types.h
 
 ifdef PCI_HAVE_PM_LINUX_SYSFS
index d9032a7d2914f4eafbd85286f07476bc06d4fac9..39be330851538d7b67c753aa659cbdf66429e3d8 100644 (file)
@@ -58,6 +58,7 @@ void pci_free_dev(struct pci_dev *d)
 {
   if (d->methods->cleanup_dev)
     d->methods->cleanup_dev(d);
+  pci_free_caps(d);
   pci_mfree(d);
 }
 
@@ -150,6 +151,7 @@ pci_fill_info(struct pci_dev *d, int flags)
     {
       flags &= ~PCI_FILL_RESCAN;
       d->known_fields = 0;
+      pci_free_caps(d);
     }
   if (flags & ~d->known_fields)
     d->known_fields |= d->methods->fill_info(d, flags & ~d->known_fields);
diff --git a/lib/caps.c b/lib/caps.c
new file mode 100644 (file)
index 0000000..13e3956
--- /dev/null
@@ -0,0 +1,114 @@
+/*
+ *     The PCI Library -- Capabilities
+ *
+ *     Copyright (c) 2008 Martin Mares <mj@ucw.cz>
+ *
+ *     Can be freely distributed and used under the terms of the GNU GPL.
+ */
+
+#include <string.h>
+
+#include "internal.h"
+
+static void
+pci_add_cap(struct pci_dev *d, unsigned int addr, unsigned int id, unsigned int type)
+{
+  struct pci_cap *cap = pci_malloc(d->access, sizeof(*cap));
+
+  cap->next = d->first_cap;
+  d->first_cap = cap;
+  cap->addr = addr;
+  cap->id = id;
+  cap->type = type;
+  d->access->debug("%04x:%02x:%02x.%d: Found capability %04x of type %d at %04x\n",
+    d->domain, d->bus, d->dev, d->func, id, type, addr);
+}
+
+static void
+pci_scan_trad_caps(struct pci_dev *d)
+{
+  word status = pci_read_word(d, PCI_STATUS);
+  byte been_there[256];
+  int where;
+
+  if (!(status & PCI_STATUS_CAP_LIST))
+    return;
+
+  memset(been_there, 0, 256);
+  where = pci_read_byte(d, PCI_CAPABILITY_LIST) & ~3;
+  while (where)
+    {
+      byte id = pci_read_byte(d, where + PCI_CAP_LIST_ID);
+      byte next = pci_read_byte(d, where + PCI_CAP_LIST_NEXT) & ~3;
+      if (been_there[where]++)
+       break;
+      if (id == 0xff)
+       break;
+      pci_add_cap(d, where, id, PCI_CAP_NORMAL);
+      where = next;
+    }
+}
+
+static void
+pci_scan_ext_caps(struct pci_dev *d)
+{
+  byte been_there[0x1000];
+  int where = 0x100;
+
+  if (!pci_find_cap(d, PCI_CAP_ID_EXP, PCI_CAP_NORMAL))
+    return;
+
+  memset(been_there, 0, 0x1000);
+  do
+    {
+      u32 header;
+      int id;
+
+      header = pci_read_long(d, where);
+      if (!header || header == 0xffffffff)
+       break;
+      id = header & 0xffff;
+      if (been_there[where]++)
+       break;
+      pci_add_cap(d, where, id, PCI_CAP_EXTENDED);
+      where = header >> 20;
+    }
+  while (where);
+}
+
+unsigned int
+pci_scan_caps(struct pci_dev *d, unsigned int want_fields)
+{
+  if ((want_fields & PCI_FILL_EXT_CAPS) && !(d->known_fields & PCI_FILL_CAPS))
+    want_fields |= PCI_FILL_CAPS;
+
+  if (want_fields & PCI_FILL_CAPS)
+    pci_scan_trad_caps(d);
+  if (want_fields & PCI_FILL_EXT_CAPS)
+    pci_scan_ext_caps(d);
+  return want_fields;
+}
+
+void
+pci_free_caps(struct pci_dev *d)
+{
+  struct pci_cap *cap;
+
+  while (cap = d->first_cap)
+    {
+      d->first_cap = cap;
+      pci_mfree(d);
+    }
+}
+
+struct pci_cap *
+pci_find_cap(struct pci_dev *d, unsigned int id, unsigned int type)
+{
+  struct pci_cap *c;
+
+  pci_fill_info(d, ((type == PCI_CAP_NORMAL) ? PCI_FILL_CAPS : PCI_FILL_EXT_CAPS));
+  for (c=d->first_cap; c; c=c->next)
+    if (c->type == type && c->id == id)
+      return c;
+  return NULL;
+}
index cc902dc42b4263c8ce55dbf4c779e25b39a35a7a..c2195924b03dfe86ded37ca990dff17c988532e9 100644 (file)
@@ -157,6 +157,8 @@ pci_generic_fill_info(struct pci_dev *d, int flags)
            d->rom_base_addr = u;
        }
     }
+  if (flags & (PCI_FILL_CAPS | PCI_FILL_EXT_CAPS))
+    flags |= pci_scan_caps(d, flags);
   return flags & ~PCI_FILL_SIZES;
 }
 
index 62c8f3337ca632c862748bd5014c00c1bb642cf0..deeaaafa64a270b2ef3c7728b33d299716c8dbe3 100644 (file)
@@ -30,23 +30,31 @@ struct pci_methods {
   void (*cleanup_dev)(struct pci_dev *);
 };
 
+/* generic.c */
 void pci_generic_scan_bus(struct pci_access *, byte *busmap, int bus);
 void pci_generic_scan(struct pci_access *);
 int pci_generic_fill_info(struct pci_dev *, int flags);
 int pci_generic_block_read(struct pci_dev *, int pos, byte *buf, int len);
 int pci_generic_block_write(struct pci_dev *, int pos, byte *buf, int len);
 
+/* init.c */
 void *pci_malloc(struct pci_access *, int);
 void pci_mfree(void *);
 char *pci_strdup(struct pci_access *a, char *s);
 
+/* access.c */
 struct pci_dev *pci_alloc_dev(struct pci_access *);
 int pci_link_dev(struct pci_access *, struct pci_dev *);
 
+/* params.c */
 void pci_define_param(struct pci_access *acc, char *param, char *val, char *help);
 int pci_set_param_internal(struct pci_access *acc, char *param, char *val, int copy);
 void pci_free_params(struct pci_access *acc);
 
+/* caps.c */
+unsigned int pci_scan_caps(struct pci_dev *, unsigned int want_fields);
+void pci_free_caps(struct pci_dev *);
+
 extern struct pci_methods pm_intel_conf1, pm_intel_conf2, pm_linux_proc,
        pm_fbsd_device, pm_aix_device, pm_nbsd_libpci, pm_obsd_device,
        pm_dump, pm_linux_sysfs;
index 8f4773fbeb429cb4e8955b5e0a5c48926b926ade..7d79424d362a931282526c1d65afd089ca79c4a2 100644 (file)
--- a/lib/pci.h
+++ b/lib/pci.h
@@ -127,6 +127,7 @@ struct pci_dev {
   pciaddr_t size[6];                   /* Region sizes */
   pciaddr_t rom_base_addr;             /* Expansion ROM base address */
   pciaddr_t rom_size;                  /* Expansion ROM size */
+  struct pci_cap *first_cap;           /* List of capabilities */
 
   /* Fields used internally: */
   struct pci_access *access;
@@ -157,10 +158,28 @@ int pci_fill_info(struct pci_dev *, int flags) PCI_ABI; /* Fill in device inform
 #define PCI_FILL_ROM_BASE      8
 #define PCI_FILL_SIZES         16
 #define PCI_FILL_CLASS         32
+#define PCI_FILL_CAPS          64
+#define PCI_FILL_EXT_CAPS      128
 #define PCI_FILL_RESCAN                0x10000
 
 void pci_setup_cache(struct pci_dev *, u8 *cache, int len) PCI_ABI;
 
+/*
+ *     Capabilities
+ */
+
+struct pci_cap {
+  struct pci_cap *next;
+  u16 id;                              /* PCI_CAP_ID_xxx */
+  u16 type;                            /* PCI_CAP_xxx */
+  unsigned int addr;                   /* Position in the config space */
+};
+
+#define PCI_CAP_NORMAL         0       /* Traditional PCI capabilities */
+#define PCI_CAP_EXTENDED       1       /* PCIe extended capabilities */
+
+struct pci_cap *pci_find_cap(struct pci_dev *, unsigned int id, unsigned int type) PCI_ABI;
+
 /*
  *     Filters
  */