From 6d5e39acd6ad8eb295442f127df2216d3ed992f9 Mon Sep 17 00:00:00 2001 From: Martin Mares Date: Tue, 11 Nov 2008 00:03:01 +0100 Subject: [PATCH] Added library functions for accessing PCI capabilities. --- lib/Makefile | 2 +- lib/access.c | 2 + lib/caps.c | 114 +++++++++++++++++++++++++++++++++++++++++++++++++ lib/generic.c | 2 + lib/internal.h | 8 ++++ lib/pci.h | 19 +++++++++ 6 files changed, 146 insertions(+), 1 deletion(-) create mode 100644 lib/caps.c diff --git a/lib/Makefile b/lib/Makefile index 31ca1c1..a19aa33 100644 --- a/lib/Makefile +++ b/lib/Makefile @@ -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 diff --git a/lib/access.c b/lib/access.c index d9032a7..39be330 100644 --- a/lib/access.c +++ b/lib/access.c @@ -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 index 0000000..13e3956 --- /dev/null +++ b/lib/caps.c @@ -0,0 +1,114 @@ +/* + * The PCI Library -- Capabilities + * + * Copyright (c) 2008 Martin Mares + * + * Can be freely distributed and used under the terms of the GNU GPL. + */ + +#include + +#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; +} diff --git a/lib/generic.c b/lib/generic.c index cc902dc..c219592 100644 --- a/lib/generic.c +++ b/lib/generic.c @@ -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; } diff --git a/lib/internal.h b/lib/internal.h index 62c8f33..deeaaaf 100644 --- a/lib/internal.h +++ b/lib/internal.h @@ -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; diff --git a/lib/pci.h b/lib/pci.h index 8f4773f..7d79424 100644 --- 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 */ -- 2.39.5