# 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
{
if (d->methods->cleanup_dev)
d->methods->cleanup_dev(d);
+ pci_free_caps(d);
pci_mfree(d);
}
{
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);
--- /dev/null
+/*
+ * 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;
+}
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;
}
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;
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;
#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
*/