diff options
author | gibbs <gibbs@FreeBSD.org> | 1998-09-15 08:21:13 +0000 |
---|---|---|
committer | gibbs <gibbs@FreeBSD.org> | 1998-09-15 08:21:13 +0000 |
commit | 9ed6892f4808d56de443849229e151f8f7ad43b0 (patch) | |
tree | 89826b5df23f21ac18045990671bb1f5e2dd537c | |
parent | b954e9c2642216af00a08bacef1878dbfff8107d (diff) | |
download | FreeBSD-src-9ed6892f4808d56de443849229e151f8f7ad43b0.zip FreeBSD-src-9ed6892f4808d56de443849229e151f8f7ad43b0.tar.gz |
Revive PCIConf.
Submitted by: "Kenneth D. Merry" <ken@plutotech.com>
-rw-r--r-- | sys/dev/pci/pci.c | 395 | ||||
-rw-r--r-- | sys/dev/pci/pcivar.h | 30 | ||||
-rw-r--r-- | sys/pci/pci.c | 395 | ||||
-rw-r--r-- | sys/pci/pci_compat.c | 23 | ||||
-rw-r--r-- | sys/pci/pci_ioctl.h | 110 | ||||
-rw-r--r-- | sys/pci/pcivar.h | 30 | ||||
-rw-r--r-- | sys/sys/pciio.h | 110 | ||||
-rw-r--r-- | usr.sbin/pciconf/pciconf.c | 58 |
8 files changed, 1025 insertions, 126 deletions
diff --git a/sys/dev/pci/pci.c b/sys/dev/pci/pci.c index fceb220..afc9a59 100644 --- a/sys/dev/pci/pci.c +++ b/sys/dev/pci/pci.c @@ -23,7 +23,7 @@ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * - * $Id: pci.c,v 1.85 1998/08/13 19:12:20 gibbs Exp $ + * $Id: pci.c,v 1.86 1998/09/06 22:41:41 tegge Exp $ * */ @@ -39,12 +39,16 @@ #include <sys/fcntl.h> #include <sys/conf.h> #include <sys/kernel.h> +#include <sys/queue.h> +#include <sys/types.h> +#include <sys/buf.h> #ifdef DEVFS #include <sys/devfsext.h> #endif /* DEVFS */ #include <vm/vm.h> #include <vm/pmap.h> +#include <vm/vm_extern.h> #include <pci/pcireg.h> #include <pci/pcivar.h> @@ -54,6 +58,10 @@ #include <machine/smp.h> #endif /* APIC_IO */ +STAILQ_HEAD(devlist, pci_devinfo) pci_devq; +u_int32_t pci_numdevs = 0; +u_int32_t pci_generation = 0; + /* return highest PCI bus number known to be used, or -1 if none */ static int @@ -305,23 +313,30 @@ pci_hdrtypedata(pcicfgregs *cfg) /* read configuration header into pcicfgrect structure */ -static pcicfgregs * +static struct pci_devinfo * pci_readcfg(pcicfgregs *probe) { pcicfgregs *cfg = NULL; + struct pci_devinfo *devlist_entry; + struct devlist *devlist_head; + + devlist_head = &pci_devq; + + devlist_entry = NULL; if (pci_cfgread(probe, PCIR_DEVVENDOR, 4) != -1) { - cfg = malloc(sizeof (pcicfgregs), M_DEVBUF, M_WAITOK); - if (cfg == NULL) - return (cfg); + devlist_entry = malloc(sizeof(struct pci_devinfo), + M_DEVBUF, M_WAITOK); + if (devlist_entry == NULL) + return (NULL); + + cfg = &devlist_entry->cfg; bzero(cfg, sizeof *cfg); cfg->bus = probe->bus; cfg->slot = probe->slot; cfg->func = probe->func; - cfg->parent = probe->parent; - cfg->vendor = pci_cfgread(cfg, PCIR_VENDOR, 2); cfg->device = pci_cfgread(cfg, PCIR_DEVICE, 2); cfg->cmdreg = pci_cfgread(cfg, PCIR_COMMAND, 2); @@ -375,30 +390,64 @@ pci_readcfg(pcicfgregs *probe) pci_fixancient(cfg); pci_hdrtypedata(cfg); + + STAILQ_INSERT_TAIL(devlist_head, devlist_entry, pci_links); + + devlist_entry->conf.pc_sel.pc_bus = cfg->bus; + devlist_entry->conf.pc_sel.pc_dev = cfg->slot; + devlist_entry->conf.pc_sel.pc_func = cfg->func; + devlist_entry->conf.pc_hdr = cfg->hdrtype; + + devlist_entry->conf.pc_subvendor = cfg->subvendor; + devlist_entry->conf.pc_subdevice = cfg->subdevice; + devlist_entry->conf.pc_vendor = cfg->vendor; + devlist_entry->conf.pc_device = cfg->device; + + devlist_entry->conf.pc_class = cfg->baseclass; + devlist_entry->conf.pc_subclass = cfg->subclass; + devlist_entry->conf.pc_progif = cfg->progif; + devlist_entry->conf.pc_revid = cfg->revid; + + pci_numdevs++; + pci_generation++; } - return (cfg); + return (devlist_entry); } #if 0 /* free pcicfgregs structure and all depending data structures */ static int -pci_freecfg(pcicfgregs *cfg) +pci_freecfg(struct pci_devinfo *dinfo) { - if (cfg->hdrspec != NULL) - free(cfg->hdrspec, M_DEVBUF); - if (cfg->map != NULL) - free(cfg->map, M_DEVBUF); - free(cfg, M_DEVBUF); + struct devlist *devlist_head; + + devlist_head = &pci_devq; + + if (dinfo->cfg.hdrspec != NULL) + free(dinfo->cfg.hdrspec, M_DEVBUF); + if (dinfo->cfg.map != NULL) + free(dinfo->cfg.map, M_DEVBUF); + /* XXX this hasn't been tested */ + STAILQ_REMOVE(devlist_head, dinfo, pci_devinfo, pci_links); + free(dinfo, M_DEVBUF); + + /* increment the generation count */ + pci_generation++; + + /* we're losing one device */ + pci_numdevs--; return (0); } #endif static void -pci_addcfg(pcicfgregs *cfg) +pci_addcfg(struct pci_devinfo *dinfo) { if (bootverbose) { int i; + pcicfgregs *cfg = &dinfo->cfg; + printf("found->\tvendor=0x%04x, dev=0x%04x, revid=0x%02x\n", cfg->vendor, cfg->device, cfg->revid); printf("\tclass=%02x-%02x-%02x, hdrtype=0x%02x, mfdev=%d\n", @@ -420,7 +469,7 @@ pci_addcfg(pcicfgregs *cfg) i, m->type, m->ln2range, m->base, m->ln2size); } } - pci_drvattach(cfg); /* XXX currently defined in pci_compat.c */ + pci_drvattach(dinfo); /* XXX currently defined in pci_compat.c */ } /* return pointer to device that is a bridge to this bus */ @@ -445,14 +494,15 @@ pci_probebus(int bus) #endif bzero(&probe, sizeof probe); - probe.parent = pci_bridgeto(bus); + /* XXX KDM */ + /* probe.parent = pci_bridgeto(bus); */ probe.bus = bus; for (probe.slot = 0; probe.slot <= PCI_SLOTMAX; probe.slot++) { int pcifunchigh = 0; for (probe.func = 0; probe.func <= pcifunchigh; probe.func++) { - pcicfgregs *cfg = pci_readcfg(&probe); - if (cfg != NULL) { - if (cfg->mfdev) + struct pci_devinfo *dinfo = pci_readcfg(&probe); + if (dinfo != NULL) { + if (dinfo->cfg.mfdev) pcifunchigh = 7; /* * XXX: Temporarily move pci_addcfg() up before @@ -465,12 +515,13 @@ pci_probebus(int bus) * pci_addcfg() will then be moved back down * below the conditional statement ... */ - pci_addcfg(cfg); + pci_addcfg(dinfo); - if (bushigh < cfg->subordinatebus) - bushigh = cfg->subordinatebus; + if (bushigh < dinfo->cfg.subordinatebus) + bushigh = dinfo->cfg.subordinatebus; - cfg = NULL; /* we don't own this anymore ... */ + /* XXX KDM */ + /* cfg = NULL; we don't own this anymore ... */ } } } @@ -485,6 +536,8 @@ pci_probe(pciattach *parent) int bushigh; int bus = 0; + STAILQ_INIT(&pci_devq); + bushigh = pci_bushigh(); while (bus <= bushigh) { int newbushigh; @@ -518,33 +571,301 @@ pci_close(dev_t dev, int flag, int devtype, struct proc *p) return 0; } +/* + * Match a single pci_conf structure against an array of pci_match_conf + * structures. The first argument, 'matches', is an array of num_matches + * pci_match_conf structures. match_buf is a pointer to the pci_conf + * structure that will be compared to every entry in the matches array. + * This function returns 1 on failure, 0 on success. + */ +static int +pci_conf_match(struct pci_match_conf *matches, int num_matches, + struct pci_conf *match_buf) +{ + int i; + + if ((matches == NULL) || (match_buf == NULL) || (num_matches <= 0)) + return(1); + + for (i = 0; i < num_matches; i++) { + /* + * I'm not sure why someone would do this...but... + */ + if (matches[i].flags == PCI_GETCONF_NO_MATCH) + continue; + + /* + * Look at each of the match flags. If it's set, do the + * comparison. If the comparison fails, we don't have a + * match, go on to the next item if there is one. + */ + if (((matches[i].flags & PCI_GETCONF_MATCH_BUS) != 0) + && (match_buf->pc_sel.pc_bus != matches[i].pc_sel.pc_bus)) + continue; + + if (((matches[i].flags & PCI_GETCONF_MATCH_DEV) != 0) + && (match_buf->pc_sel.pc_dev != matches[i].pc_sel.pc_dev)) + continue; + + if (((matches[i].flags & PCI_GETCONF_MATCH_FUNC) != 0) + && (match_buf->pc_sel.pc_func != matches[i].pc_sel.pc_func)) + continue; + + if (((matches[i].flags & PCI_GETCONF_MATCH_VENDOR) != 0) + && (match_buf->pc_vendor != matches[i].pc_vendor)) + continue; + + if (((matches[i].flags & PCI_GETCONF_MATCH_DEVICE) != 0) + && (match_buf->pc_device != matches[i].pc_device)) + continue; + + if (((matches[i].flags & PCI_GETCONF_MATCH_CLASS) != 0) + && (match_buf->pc_class != matches[i].pc_class)) + continue; + + if (((matches[i].flags & PCI_GETCONF_MATCH_UNIT) != 0) + && (match_buf->pd_unit != matches[i].pd_unit)) + continue; + + if (((matches[i].flags & PCI_GETCONF_MATCH_NAME) != 0) + && (strncmp(matches[i].pd_name, match_buf->pd_name, + sizeof(match_buf->pd_name)) != 0)) + continue; + + return(0); + } + + return(1); +} + static int pci_ioctl(dev_t dev, u_long cmd, caddr_t data, int flag, struct proc *p) { struct pci_io *io; int error; - if (cmd != PCIOCGETCONF && !(flag & FWRITE)) + if (!(flag & FWRITE)) return EPERM; + switch(cmd) { case PCIOCGETCONF: -#ifdef NOTYET -static struct pci_conf *pci_dev_list; -static unsigned pci_dev_list_count; -static unsigned pci_dev_list_size; + { + struct pci_devinfo *dinfo; + struct pci_conf_io *cio; + struct devlist *devlist_head; + struct pci_match_conf *pattern_buf; + int num_patterns; + size_t iolen; + int ionum, i; cio = (struct pci_conf_io *)data; - iolen = min(cio->pci_len, - pci_dev_list_count * sizeof(struct pci_conf)); - cio->pci_len = pci_dev_list_count * sizeof(struct pci_conf); - error = copyout(pci_dev_list, cio->pci_buf, iolen); -#else - error = ENODEV; -#endif - break; + num_patterns = 0; + dinfo = NULL; + + /* + * Hopefully the user won't pass in a null pointer, but it + * can't hurt to check. + */ + if (cio == NULL) { + error = EINVAL; + break; + } + + /* + * If the user specified an offset into the device list, + * but the list has changed since they last called this + * ioctl, tell them that the list has changed. They will + * have to get the list from the beginning. + */ + if ((cio->offset != 0) + && (cio->generation != pci_generation)){ + cio->num_matches = 0; + cio->status = PCI_GETCONF_LIST_CHANGED; + error = 0; + break; + } + + /* + * Check to see whether the user has asked for an offset + * past the end of our list. + */ + if (cio->offset >= pci_numdevs) { + cio->num_matches = 0; + cio->status = PCI_GETCONF_LAST_DEVICE; + error = 0; + break; + } + + /* get the head of the device queue */ + devlist_head = &pci_devq; + + /* + * Determine how much room we have for pci_conf structures. + * Round the user's buffer size down to the nearest + * multiple of sizeof(struct pci_conf) in case the user + * didn't specify a multiple of that size. + */ + iolen = min(cio->match_buf_len - + (cio->match_buf_len % sizeof(struct pci_conf)), + pci_numdevs * sizeof(struct pci_conf)); + + /* + * Since we know that iolen is a multiple of the size of + * the pciconf union, it's okay to do this. + */ + ionum = iolen / sizeof(struct pci_conf); + + /* + * If this test is true, the user wants the pci_conf + * structures returned to match the supplied entries. + */ + if ((cio->num_patterns > 0) + && (cio->pat_buf_len > 0)) { + /* + * pat_buf_len needs to be: + * num_patterns * sizeof(struct pci_match_conf) + * While it is certainly possible the user just + * allocated a large buffer, but set the number of + * matches correctly, it is far more likely that + * their kernel doesn't match the userland utility + * they're using. It's also possible that the user + * forgot to initialize some variables. Yes, this + * may be overly picky, but I hazard to guess that + * it's far more likely to just catch folks that + * updated their kernel but not their userland. + */ + if ((cio->num_patterns * + sizeof(struct pci_match_conf)) != cio->pat_buf_len){ + /* The user made a mistake, return an error*/ + cio->status = PCI_GETCONF_ERROR; + printf("pci_ioctl: pat_buf_len %d != " + "num_patterns (%d) * sizeof(struct " + "pci_match_conf) (%d)\npci_ioctl: " + "pat_buf_len should be = %d\n", + cio->pat_buf_len, cio->num_patterns, + sizeof(struct pci_match_conf), + sizeof(struct pci_match_conf) * + cio->num_patterns); + printf("pci_ioctl: do your headers match your " + "kernel?\n"); + cio->num_matches = 0; + error = EINVAL; + break; + } + + /* + * Check the user's buffer to make sure it's readable. + */ + if ((error = useracc((caddr_t)cio->patterns, + cio->pat_buf_len, B_READ)) != 1){ + printf("pci_ioctl: pattern buffer %#lx, " + "length %u isn't user accessible for" + " READ\n", cio->patterns, + cio->pat_buf_len); + error = EACCES; + break; + } + /* + * Allocate a buffer to hold the patterns. + */ + pattern_buf = malloc(cio->pat_buf_len, M_TEMP, + M_WAITOK); + error = copyin(cio->patterns, pattern_buf, + cio->pat_buf_len); + if (error != 0) + break; + num_patterns = cio->num_patterns; + + } else if ((cio->num_patterns > 0) + || (cio->pat_buf_len > 0)) { + /* + * The user made a mistake, spit out an error. + */ + cio->status = PCI_GETCONF_ERROR; + cio->num_matches = 0; + printf("pci_ioctl: invalid GETCONF arguments\n"); + error = EINVAL; + break; + } else + pattern_buf = NULL; + + /* + * Make sure we can write to the match buffer. + */ + if ((error = useracc((caddr_t)cio->matches, cio->match_buf_len, + B_WRITE)) != 1) { + printf("pci_ioctl: match buffer %#lx, length %u " + "isn't user accessible for WRITE\n", + cio->matches, cio->match_buf_len); + error = EACCES; + break; + } + + /* + * Go through the list of devices and copy out the devices + * that match the user's criteria. + */ + for (cio->num_matches = 0, error = 0, i = 0, + dinfo = STAILQ_FIRST(devlist_head); + (dinfo != NULL) && (cio->num_matches < ionum) + && (error == 0) && (i < pci_numdevs); + dinfo = STAILQ_NEXT(dinfo, pci_links), i++) { + + if (i < cio->offset) + continue; + + if ((pattern_buf == NULL) || + (pci_conf_match(pattern_buf, num_patterns, + &dinfo->conf) == 0)) { + + /* + * If we've filled up the user's buffer, + * break out at this point. Since we've + * got a match here, we'll pick right back + * up at the matching entry. We can also + * tell the user that there are more matches + * left. + */ + if (cio->num_matches >= ionum) + break; + + error = copyout(&dinfo->conf, + &cio->matches[cio->num_matches], + sizeof(struct pci_conf)); + cio->num_matches++; + } + } + + /* + * Set the pointer into the list, so if the user is getting + * n records at a time, where n < pci_numdevs, + */ + cio->offset = i; + + /* + * Set the generation, the user will need this if they make + * another ioctl call with offset != 0. + */ + cio->generation = pci_generation; + /* + * If this is the last device, inform the user so he won't + * bother asking for more devices. If dinfo isn't NULL, we + * know that there are more matches in the list because of + * the way the traversal is done. + */ + if (dinfo == NULL) + cio->status = PCI_GETCONF_LAST_DEVICE; + else + cio->status = PCI_GETCONF_MORE_DEVS; + + if (pattern_buf != NULL) + free(pattern_buf, M_TEMP); + + break; + } case PCIOCREAD: io = (struct pci_io *)data; switch(io->pi_width) { diff --git a/sys/dev/pci/pcivar.h b/sys/dev/pci/pcivar.h index 3d0afd4..f18b2b9 100644 --- a/sys/dev/pci/pcivar.h +++ b/sys/dev/pci/pcivar.h @@ -1,6 +1,3 @@ -#ifndef PCI_COMPAT -#define PCI_COMPAT -#endif /* * Copyright (c) 1997, Stefan Esser <se@freebsd.org> * All rights reserved. @@ -26,10 +23,20 @@ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * - * $Id: pcivar.h,v 1.19 1998/07/22 08:39:08 dfr Exp $ + * $Id: pcivar.h,v 1.20 1998/08/13 19:12:20 gibbs Exp $ * */ +#ifndef _PCIVAR_H_ +#define _PCIVAR_H_ + +#ifndef PCI_COMPAT +#define PCI_COMPAT +#endif + +#include <pci/pci_ioctl.h> /* XXX KDM */ +#include <sys/queue.h> + /* some PCI bus constants */ #define PCI_BUSMAX 255 /* highest supported bus number */ @@ -65,8 +72,6 @@ typedef struct { /* config header information common to all header types */ typedef struct pcicfg { - struct pcicfg *parent; - struct pcicfg *next; pcimap *map; /* pointer to array of PCI maps */ void *hdrspec; /* pointer to header type specific data */ @@ -153,10 +158,20 @@ typedef struct pciattach { struct pciattach *next; } pciattach; +struct pci_devinfo { + STAILQ_ENTRY(pci_devinfo) pci_links; + struct pci_device *device; /* should this be ifdefed? */ + pcicfgregs cfg; + struct pci_conf conf; +}; + +extern u_int32_t pci_numdevs; + + /* externally visible functions */ int pci_probe (pciattach *attach); -void pci_drvattach(pcicfgregs *cfg); +void pci_drvattach(struct pci_devinfo *dinfo); /* low level PCI config register functions provided by pcibus.c */ @@ -208,3 +223,4 @@ int pci_unmap_int (pcici_t tag); int pci_register_lkm (struct pci_device *dvp, int if_revision); #endif /* PCI_COMPAT */ +#endif /* _PCIVAR_H_ */ diff --git a/sys/pci/pci.c b/sys/pci/pci.c index fceb220..afc9a59 100644 --- a/sys/pci/pci.c +++ b/sys/pci/pci.c @@ -23,7 +23,7 @@ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * - * $Id: pci.c,v 1.85 1998/08/13 19:12:20 gibbs Exp $ + * $Id: pci.c,v 1.86 1998/09/06 22:41:41 tegge Exp $ * */ @@ -39,12 +39,16 @@ #include <sys/fcntl.h> #include <sys/conf.h> #include <sys/kernel.h> +#include <sys/queue.h> +#include <sys/types.h> +#include <sys/buf.h> #ifdef DEVFS #include <sys/devfsext.h> #endif /* DEVFS */ #include <vm/vm.h> #include <vm/pmap.h> +#include <vm/vm_extern.h> #include <pci/pcireg.h> #include <pci/pcivar.h> @@ -54,6 +58,10 @@ #include <machine/smp.h> #endif /* APIC_IO */ +STAILQ_HEAD(devlist, pci_devinfo) pci_devq; +u_int32_t pci_numdevs = 0; +u_int32_t pci_generation = 0; + /* return highest PCI bus number known to be used, or -1 if none */ static int @@ -305,23 +313,30 @@ pci_hdrtypedata(pcicfgregs *cfg) /* read configuration header into pcicfgrect structure */ -static pcicfgregs * +static struct pci_devinfo * pci_readcfg(pcicfgregs *probe) { pcicfgregs *cfg = NULL; + struct pci_devinfo *devlist_entry; + struct devlist *devlist_head; + + devlist_head = &pci_devq; + + devlist_entry = NULL; if (pci_cfgread(probe, PCIR_DEVVENDOR, 4) != -1) { - cfg = malloc(sizeof (pcicfgregs), M_DEVBUF, M_WAITOK); - if (cfg == NULL) - return (cfg); + devlist_entry = malloc(sizeof(struct pci_devinfo), + M_DEVBUF, M_WAITOK); + if (devlist_entry == NULL) + return (NULL); + + cfg = &devlist_entry->cfg; bzero(cfg, sizeof *cfg); cfg->bus = probe->bus; cfg->slot = probe->slot; cfg->func = probe->func; - cfg->parent = probe->parent; - cfg->vendor = pci_cfgread(cfg, PCIR_VENDOR, 2); cfg->device = pci_cfgread(cfg, PCIR_DEVICE, 2); cfg->cmdreg = pci_cfgread(cfg, PCIR_COMMAND, 2); @@ -375,30 +390,64 @@ pci_readcfg(pcicfgregs *probe) pci_fixancient(cfg); pci_hdrtypedata(cfg); + + STAILQ_INSERT_TAIL(devlist_head, devlist_entry, pci_links); + + devlist_entry->conf.pc_sel.pc_bus = cfg->bus; + devlist_entry->conf.pc_sel.pc_dev = cfg->slot; + devlist_entry->conf.pc_sel.pc_func = cfg->func; + devlist_entry->conf.pc_hdr = cfg->hdrtype; + + devlist_entry->conf.pc_subvendor = cfg->subvendor; + devlist_entry->conf.pc_subdevice = cfg->subdevice; + devlist_entry->conf.pc_vendor = cfg->vendor; + devlist_entry->conf.pc_device = cfg->device; + + devlist_entry->conf.pc_class = cfg->baseclass; + devlist_entry->conf.pc_subclass = cfg->subclass; + devlist_entry->conf.pc_progif = cfg->progif; + devlist_entry->conf.pc_revid = cfg->revid; + + pci_numdevs++; + pci_generation++; } - return (cfg); + return (devlist_entry); } #if 0 /* free pcicfgregs structure and all depending data structures */ static int -pci_freecfg(pcicfgregs *cfg) +pci_freecfg(struct pci_devinfo *dinfo) { - if (cfg->hdrspec != NULL) - free(cfg->hdrspec, M_DEVBUF); - if (cfg->map != NULL) - free(cfg->map, M_DEVBUF); - free(cfg, M_DEVBUF); + struct devlist *devlist_head; + + devlist_head = &pci_devq; + + if (dinfo->cfg.hdrspec != NULL) + free(dinfo->cfg.hdrspec, M_DEVBUF); + if (dinfo->cfg.map != NULL) + free(dinfo->cfg.map, M_DEVBUF); + /* XXX this hasn't been tested */ + STAILQ_REMOVE(devlist_head, dinfo, pci_devinfo, pci_links); + free(dinfo, M_DEVBUF); + + /* increment the generation count */ + pci_generation++; + + /* we're losing one device */ + pci_numdevs--; return (0); } #endif static void -pci_addcfg(pcicfgregs *cfg) +pci_addcfg(struct pci_devinfo *dinfo) { if (bootverbose) { int i; + pcicfgregs *cfg = &dinfo->cfg; + printf("found->\tvendor=0x%04x, dev=0x%04x, revid=0x%02x\n", cfg->vendor, cfg->device, cfg->revid); printf("\tclass=%02x-%02x-%02x, hdrtype=0x%02x, mfdev=%d\n", @@ -420,7 +469,7 @@ pci_addcfg(pcicfgregs *cfg) i, m->type, m->ln2range, m->base, m->ln2size); } } - pci_drvattach(cfg); /* XXX currently defined in pci_compat.c */ + pci_drvattach(dinfo); /* XXX currently defined in pci_compat.c */ } /* return pointer to device that is a bridge to this bus */ @@ -445,14 +494,15 @@ pci_probebus(int bus) #endif bzero(&probe, sizeof probe); - probe.parent = pci_bridgeto(bus); + /* XXX KDM */ + /* probe.parent = pci_bridgeto(bus); */ probe.bus = bus; for (probe.slot = 0; probe.slot <= PCI_SLOTMAX; probe.slot++) { int pcifunchigh = 0; for (probe.func = 0; probe.func <= pcifunchigh; probe.func++) { - pcicfgregs *cfg = pci_readcfg(&probe); - if (cfg != NULL) { - if (cfg->mfdev) + struct pci_devinfo *dinfo = pci_readcfg(&probe); + if (dinfo != NULL) { + if (dinfo->cfg.mfdev) pcifunchigh = 7; /* * XXX: Temporarily move pci_addcfg() up before @@ -465,12 +515,13 @@ pci_probebus(int bus) * pci_addcfg() will then be moved back down * below the conditional statement ... */ - pci_addcfg(cfg); + pci_addcfg(dinfo); - if (bushigh < cfg->subordinatebus) - bushigh = cfg->subordinatebus; + if (bushigh < dinfo->cfg.subordinatebus) + bushigh = dinfo->cfg.subordinatebus; - cfg = NULL; /* we don't own this anymore ... */ + /* XXX KDM */ + /* cfg = NULL; we don't own this anymore ... */ } } } @@ -485,6 +536,8 @@ pci_probe(pciattach *parent) int bushigh; int bus = 0; + STAILQ_INIT(&pci_devq); + bushigh = pci_bushigh(); while (bus <= bushigh) { int newbushigh; @@ -518,33 +571,301 @@ pci_close(dev_t dev, int flag, int devtype, struct proc *p) return 0; } +/* + * Match a single pci_conf structure against an array of pci_match_conf + * structures. The first argument, 'matches', is an array of num_matches + * pci_match_conf structures. match_buf is a pointer to the pci_conf + * structure that will be compared to every entry in the matches array. + * This function returns 1 on failure, 0 on success. + */ +static int +pci_conf_match(struct pci_match_conf *matches, int num_matches, + struct pci_conf *match_buf) +{ + int i; + + if ((matches == NULL) || (match_buf == NULL) || (num_matches <= 0)) + return(1); + + for (i = 0; i < num_matches; i++) { + /* + * I'm not sure why someone would do this...but... + */ + if (matches[i].flags == PCI_GETCONF_NO_MATCH) + continue; + + /* + * Look at each of the match flags. If it's set, do the + * comparison. If the comparison fails, we don't have a + * match, go on to the next item if there is one. + */ + if (((matches[i].flags & PCI_GETCONF_MATCH_BUS) != 0) + && (match_buf->pc_sel.pc_bus != matches[i].pc_sel.pc_bus)) + continue; + + if (((matches[i].flags & PCI_GETCONF_MATCH_DEV) != 0) + && (match_buf->pc_sel.pc_dev != matches[i].pc_sel.pc_dev)) + continue; + + if (((matches[i].flags & PCI_GETCONF_MATCH_FUNC) != 0) + && (match_buf->pc_sel.pc_func != matches[i].pc_sel.pc_func)) + continue; + + if (((matches[i].flags & PCI_GETCONF_MATCH_VENDOR) != 0) + && (match_buf->pc_vendor != matches[i].pc_vendor)) + continue; + + if (((matches[i].flags & PCI_GETCONF_MATCH_DEVICE) != 0) + && (match_buf->pc_device != matches[i].pc_device)) + continue; + + if (((matches[i].flags & PCI_GETCONF_MATCH_CLASS) != 0) + && (match_buf->pc_class != matches[i].pc_class)) + continue; + + if (((matches[i].flags & PCI_GETCONF_MATCH_UNIT) != 0) + && (match_buf->pd_unit != matches[i].pd_unit)) + continue; + + if (((matches[i].flags & PCI_GETCONF_MATCH_NAME) != 0) + && (strncmp(matches[i].pd_name, match_buf->pd_name, + sizeof(match_buf->pd_name)) != 0)) + continue; + + return(0); + } + + return(1); +} + static int pci_ioctl(dev_t dev, u_long cmd, caddr_t data, int flag, struct proc *p) { struct pci_io *io; int error; - if (cmd != PCIOCGETCONF && !(flag & FWRITE)) + if (!(flag & FWRITE)) return EPERM; + switch(cmd) { case PCIOCGETCONF: -#ifdef NOTYET -static struct pci_conf *pci_dev_list; -static unsigned pci_dev_list_count; -static unsigned pci_dev_list_size; + { + struct pci_devinfo *dinfo; + struct pci_conf_io *cio; + struct devlist *devlist_head; + struct pci_match_conf *pattern_buf; + int num_patterns; + size_t iolen; + int ionum, i; cio = (struct pci_conf_io *)data; - iolen = min(cio->pci_len, - pci_dev_list_count * sizeof(struct pci_conf)); - cio->pci_len = pci_dev_list_count * sizeof(struct pci_conf); - error = copyout(pci_dev_list, cio->pci_buf, iolen); -#else - error = ENODEV; -#endif - break; + num_patterns = 0; + dinfo = NULL; + + /* + * Hopefully the user won't pass in a null pointer, but it + * can't hurt to check. + */ + if (cio == NULL) { + error = EINVAL; + break; + } + + /* + * If the user specified an offset into the device list, + * but the list has changed since they last called this + * ioctl, tell them that the list has changed. They will + * have to get the list from the beginning. + */ + if ((cio->offset != 0) + && (cio->generation != pci_generation)){ + cio->num_matches = 0; + cio->status = PCI_GETCONF_LIST_CHANGED; + error = 0; + break; + } + + /* + * Check to see whether the user has asked for an offset + * past the end of our list. + */ + if (cio->offset >= pci_numdevs) { + cio->num_matches = 0; + cio->status = PCI_GETCONF_LAST_DEVICE; + error = 0; + break; + } + + /* get the head of the device queue */ + devlist_head = &pci_devq; + + /* + * Determine how much room we have for pci_conf structures. + * Round the user's buffer size down to the nearest + * multiple of sizeof(struct pci_conf) in case the user + * didn't specify a multiple of that size. + */ + iolen = min(cio->match_buf_len - + (cio->match_buf_len % sizeof(struct pci_conf)), + pci_numdevs * sizeof(struct pci_conf)); + + /* + * Since we know that iolen is a multiple of the size of + * the pciconf union, it's okay to do this. + */ + ionum = iolen / sizeof(struct pci_conf); + + /* + * If this test is true, the user wants the pci_conf + * structures returned to match the supplied entries. + */ + if ((cio->num_patterns > 0) + && (cio->pat_buf_len > 0)) { + /* + * pat_buf_len needs to be: + * num_patterns * sizeof(struct pci_match_conf) + * While it is certainly possible the user just + * allocated a large buffer, but set the number of + * matches correctly, it is far more likely that + * their kernel doesn't match the userland utility + * they're using. It's also possible that the user + * forgot to initialize some variables. Yes, this + * may be overly picky, but I hazard to guess that + * it's far more likely to just catch folks that + * updated their kernel but not their userland. + */ + if ((cio->num_patterns * + sizeof(struct pci_match_conf)) != cio->pat_buf_len){ + /* The user made a mistake, return an error*/ + cio->status = PCI_GETCONF_ERROR; + printf("pci_ioctl: pat_buf_len %d != " + "num_patterns (%d) * sizeof(struct " + "pci_match_conf) (%d)\npci_ioctl: " + "pat_buf_len should be = %d\n", + cio->pat_buf_len, cio->num_patterns, + sizeof(struct pci_match_conf), + sizeof(struct pci_match_conf) * + cio->num_patterns); + printf("pci_ioctl: do your headers match your " + "kernel?\n"); + cio->num_matches = 0; + error = EINVAL; + break; + } + + /* + * Check the user's buffer to make sure it's readable. + */ + if ((error = useracc((caddr_t)cio->patterns, + cio->pat_buf_len, B_READ)) != 1){ + printf("pci_ioctl: pattern buffer %#lx, " + "length %u isn't user accessible for" + " READ\n", cio->patterns, + cio->pat_buf_len); + error = EACCES; + break; + } + /* + * Allocate a buffer to hold the patterns. + */ + pattern_buf = malloc(cio->pat_buf_len, M_TEMP, + M_WAITOK); + error = copyin(cio->patterns, pattern_buf, + cio->pat_buf_len); + if (error != 0) + break; + num_patterns = cio->num_patterns; + + } else if ((cio->num_patterns > 0) + || (cio->pat_buf_len > 0)) { + /* + * The user made a mistake, spit out an error. + */ + cio->status = PCI_GETCONF_ERROR; + cio->num_matches = 0; + printf("pci_ioctl: invalid GETCONF arguments\n"); + error = EINVAL; + break; + } else + pattern_buf = NULL; + + /* + * Make sure we can write to the match buffer. + */ + if ((error = useracc((caddr_t)cio->matches, cio->match_buf_len, + B_WRITE)) != 1) { + printf("pci_ioctl: match buffer %#lx, length %u " + "isn't user accessible for WRITE\n", + cio->matches, cio->match_buf_len); + error = EACCES; + break; + } + + /* + * Go through the list of devices and copy out the devices + * that match the user's criteria. + */ + for (cio->num_matches = 0, error = 0, i = 0, + dinfo = STAILQ_FIRST(devlist_head); + (dinfo != NULL) && (cio->num_matches < ionum) + && (error == 0) && (i < pci_numdevs); + dinfo = STAILQ_NEXT(dinfo, pci_links), i++) { + + if (i < cio->offset) + continue; + + if ((pattern_buf == NULL) || + (pci_conf_match(pattern_buf, num_patterns, + &dinfo->conf) == 0)) { + + /* + * If we've filled up the user's buffer, + * break out at this point. Since we've + * got a match here, we'll pick right back + * up at the matching entry. We can also + * tell the user that there are more matches + * left. + */ + if (cio->num_matches >= ionum) + break; + + error = copyout(&dinfo->conf, + &cio->matches[cio->num_matches], + sizeof(struct pci_conf)); + cio->num_matches++; + } + } + + /* + * Set the pointer into the list, so if the user is getting + * n records at a time, where n < pci_numdevs, + */ + cio->offset = i; + + /* + * Set the generation, the user will need this if they make + * another ioctl call with offset != 0. + */ + cio->generation = pci_generation; + /* + * If this is the last device, inform the user so he won't + * bother asking for more devices. If dinfo isn't NULL, we + * know that there are more matches in the list because of + * the way the traversal is done. + */ + if (dinfo == NULL) + cio->status = PCI_GETCONF_LAST_DEVICE; + else + cio->status = PCI_GETCONF_MORE_DEVS; + + if (pattern_buf != NULL) + free(pattern_buf, M_TEMP); + + break; + } case PCIOCREAD: io = (struct pci_io *)data; switch(io->pi_width) { diff --git a/sys/pci/pci_compat.c b/sys/pci/pci_compat.c index 4dfe6fd..ebec37a 100644 --- a/sys/pci/pci_compat.c +++ b/sys/pci/pci_compat.c @@ -23,7 +23,7 @@ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * - * $Id: pci_compat.c,v 1.9 1998/08/07 08:20:36 dfr Exp $ + * $Id: pci_compat.c,v 1.10 1998/09/06 22:41:42 tegge Exp $ * */ @@ -348,10 +348,12 @@ pci_drvmessage(pcicfgregs *cfg, char *name, int unit) void -pci_drvattach(pcicfgregs *cfg) +pci_drvattach(struct pci_devinfo *dinfo) { struct pci_device *dvp; + pcicfgregs *cfg; + cfg = &dinfo->cfg; dvp = pci_finddrv(cfg); if (dvp != NULL) { int unit; @@ -364,6 +366,23 @@ pci_drvattach(pcicfgregs *cfg) pci_drvmessage(cfg, dvp->pd_name, unit); if (dvp->pd_attach) dvp->pd_attach(cfg, unit); + + dinfo->device = dvp; + + /* + * XXX KDM for some devices, dvp->pd_name winds up NULL. + * I haven't investigated enough to figure out why this + * would happen. + */ + if (dvp->pd_name != NULL) + strncpy(dinfo->conf.pd_name, dvp->pd_name, + sizeof(dinfo->conf.pd_name)); + else + strncpy(dinfo->conf.pd_name, "????", + sizeof(dinfo->conf.pd_name)); + + dinfo->conf.pd_unit = unit; + } } diff --git a/sys/pci/pci_ioctl.h b/sys/pci/pci_ioctl.h index 1820c08..d460846 100644 --- a/sys/pci/pci_ioctl.h +++ b/sys/pci/pci_ioctl.h @@ -3,25 +3,113 @@ #include <sys/ioccom.h> + +#define PCI_MAXNAMELEN 16 /* max no. of characters in a device name */ + +typedef enum { + PCI_GETCONF_LAST_DEVICE, + PCI_GETCONF_LIST_CHANGED, + PCI_GETCONF_MORE_DEVS, + PCI_GETCONF_ERROR +} pci_getconf_status; + +typedef enum { + PCI_GETCONF_NO_MATCH = 0x00, + PCI_GETCONF_MATCH_BUS = 0x01, + PCI_GETCONF_MATCH_DEV = 0x02, + PCI_GETCONF_MATCH_FUNC = 0x04, + PCI_GETCONF_MATCH_NAME = 0x08, + PCI_GETCONF_MATCH_UNIT = 0x10, + PCI_GETCONF_MATCH_VENDOR = 0x20, + PCI_GETCONF_MATCH_DEVICE = 0x40, + PCI_GETCONF_MATCH_CLASS = 0x80 +} pci_getconf_flags; + struct pcisel { - u_char pc_bus; /* bus number */ - u_char pc_dev; /* device on this bus */ - u_char pc_func; /* function on this device */ + u_int8_t pc_bus; /* bus number */ + u_int8_t pc_dev; /* device on this bus */ + u_int8_t pc_func; /* function on this device */ }; struct pci_conf { struct pcisel pc_sel; /* bus+slot+function */ - u_char pc_hdr; /* PCI header type */ - pcidi_t pc_devid; /* device ID */ - pcidi_t pc_subid; /* subvendor ID */ - u_int32_t pc_class; /* device class */ - struct pci_device *pc_dvp; /* device driver pointer or NULL */ - struct pcicb *pc_cb; /* pointer to bus parameters */ + u_int8_t pc_hdr; /* PCI header type */ + u_int16_t pc_subvendor; /* card vendor ID */ + u_int16_t pc_subdevice; /* card device ID, assigned by + card vendor */ + u_int16_t pc_vendor; /* chip vendor ID */ + u_int16_t pc_device; /* chip device ID, assigned by + chip vendor */ + u_int8_t pc_class; /* chip PCI class */ + u_int8_t pc_subclass; /* chip PCI subclass */ + u_int8_t pc_progif; /* chip PCI programming interface */ + u_int8_t pc_revid; /* chip revision ID */ + char pd_name[PCI_MAXNAMELEN + 1]; /* Name of peripheral + device */ + u_long pd_unit; /* Unit number */ +}; + +struct pci_match_conf { + struct pcisel pc_sel; /* bus+slot+function */ + char pd_name[PCI_MAXNAMELEN + 1]; /* Name of peripheral + device */ + u_long pd_unit; /* Unit number */ + u_int16_t pc_vendor; /* PCI Vendor ID */ + u_int16_t pc_device; /* PCI Device ID */ + u_int8_t pc_class; /* PCI class */ + pci_getconf_flags flags; /* Matching expression */ }; struct pci_conf_io { - size_t pci_len; /* length of buffer */ - struct pci_conf *pci_buf; /* buffer */ + u_int32_t pat_buf_len; /* + * Length of buffer passed in from + * user space. + */ + u_int32_t num_patterns; /* + * Number of pci_match_conf structures + * passed in by the user. + */ + struct pci_match_conf *patterns; /* + * Patterns passed in by the user. + */ + u_int32_t match_buf_len;/* + * Length of match buffer passed + * in by the user. + */ + u_int32_t num_matches; /* + * Number of matches returned by + * the kernel. + */ + struct pci_conf *matches; /* + * PCI device matches returned by + * the kernel. + */ + u_int32_t offset; /* + * Passed in by the user code to + * indicate where the kernel should + * start traversing the device list. + * The value passed out by the kernel + * points to the record immediately + * after the last one returned. + * i.e. this value may be passed back + * unchanged by the user for a + * subsequent call. + */ + u_int32_t generation; /* + * PCI configuration generation. + * This only needs to be set if the + * offset is set. The kernel will + * compare its current generation + * number to the generation passed + * in by the user to determine + * whether the PCI device list has + * changed since the user last + * called the GETCONF ioctl. + */ + pci_getconf_status status; /* + * Status passed back from the + * kernel. + */ }; struct pci_io { diff --git a/sys/pci/pcivar.h b/sys/pci/pcivar.h index 3d0afd4..f18b2b9 100644 --- a/sys/pci/pcivar.h +++ b/sys/pci/pcivar.h @@ -1,6 +1,3 @@ -#ifndef PCI_COMPAT -#define PCI_COMPAT -#endif /* * Copyright (c) 1997, Stefan Esser <se@freebsd.org> * All rights reserved. @@ -26,10 +23,20 @@ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * - * $Id: pcivar.h,v 1.19 1998/07/22 08:39:08 dfr Exp $ + * $Id: pcivar.h,v 1.20 1998/08/13 19:12:20 gibbs Exp $ * */ +#ifndef _PCIVAR_H_ +#define _PCIVAR_H_ + +#ifndef PCI_COMPAT +#define PCI_COMPAT +#endif + +#include <pci/pci_ioctl.h> /* XXX KDM */ +#include <sys/queue.h> + /* some PCI bus constants */ #define PCI_BUSMAX 255 /* highest supported bus number */ @@ -65,8 +72,6 @@ typedef struct { /* config header information common to all header types */ typedef struct pcicfg { - struct pcicfg *parent; - struct pcicfg *next; pcimap *map; /* pointer to array of PCI maps */ void *hdrspec; /* pointer to header type specific data */ @@ -153,10 +158,20 @@ typedef struct pciattach { struct pciattach *next; } pciattach; +struct pci_devinfo { + STAILQ_ENTRY(pci_devinfo) pci_links; + struct pci_device *device; /* should this be ifdefed? */ + pcicfgregs cfg; + struct pci_conf conf; +}; + +extern u_int32_t pci_numdevs; + + /* externally visible functions */ int pci_probe (pciattach *attach); -void pci_drvattach(pcicfgregs *cfg); +void pci_drvattach(struct pci_devinfo *dinfo); /* low level PCI config register functions provided by pcibus.c */ @@ -208,3 +223,4 @@ int pci_unmap_int (pcici_t tag); int pci_register_lkm (struct pci_device *dvp, int if_revision); #endif /* PCI_COMPAT */ +#endif /* _PCIVAR_H_ */ diff --git a/sys/sys/pciio.h b/sys/sys/pciio.h index 1820c08..d460846 100644 --- a/sys/sys/pciio.h +++ b/sys/sys/pciio.h @@ -3,25 +3,113 @@ #include <sys/ioccom.h> + +#define PCI_MAXNAMELEN 16 /* max no. of characters in a device name */ + +typedef enum { + PCI_GETCONF_LAST_DEVICE, + PCI_GETCONF_LIST_CHANGED, + PCI_GETCONF_MORE_DEVS, + PCI_GETCONF_ERROR +} pci_getconf_status; + +typedef enum { + PCI_GETCONF_NO_MATCH = 0x00, + PCI_GETCONF_MATCH_BUS = 0x01, + PCI_GETCONF_MATCH_DEV = 0x02, + PCI_GETCONF_MATCH_FUNC = 0x04, + PCI_GETCONF_MATCH_NAME = 0x08, + PCI_GETCONF_MATCH_UNIT = 0x10, + PCI_GETCONF_MATCH_VENDOR = 0x20, + PCI_GETCONF_MATCH_DEVICE = 0x40, + PCI_GETCONF_MATCH_CLASS = 0x80 +} pci_getconf_flags; + struct pcisel { - u_char pc_bus; /* bus number */ - u_char pc_dev; /* device on this bus */ - u_char pc_func; /* function on this device */ + u_int8_t pc_bus; /* bus number */ + u_int8_t pc_dev; /* device on this bus */ + u_int8_t pc_func; /* function on this device */ }; struct pci_conf { struct pcisel pc_sel; /* bus+slot+function */ - u_char pc_hdr; /* PCI header type */ - pcidi_t pc_devid; /* device ID */ - pcidi_t pc_subid; /* subvendor ID */ - u_int32_t pc_class; /* device class */ - struct pci_device *pc_dvp; /* device driver pointer or NULL */ - struct pcicb *pc_cb; /* pointer to bus parameters */ + u_int8_t pc_hdr; /* PCI header type */ + u_int16_t pc_subvendor; /* card vendor ID */ + u_int16_t pc_subdevice; /* card device ID, assigned by + card vendor */ + u_int16_t pc_vendor; /* chip vendor ID */ + u_int16_t pc_device; /* chip device ID, assigned by + chip vendor */ + u_int8_t pc_class; /* chip PCI class */ + u_int8_t pc_subclass; /* chip PCI subclass */ + u_int8_t pc_progif; /* chip PCI programming interface */ + u_int8_t pc_revid; /* chip revision ID */ + char pd_name[PCI_MAXNAMELEN + 1]; /* Name of peripheral + device */ + u_long pd_unit; /* Unit number */ +}; + +struct pci_match_conf { + struct pcisel pc_sel; /* bus+slot+function */ + char pd_name[PCI_MAXNAMELEN + 1]; /* Name of peripheral + device */ + u_long pd_unit; /* Unit number */ + u_int16_t pc_vendor; /* PCI Vendor ID */ + u_int16_t pc_device; /* PCI Device ID */ + u_int8_t pc_class; /* PCI class */ + pci_getconf_flags flags; /* Matching expression */ }; struct pci_conf_io { - size_t pci_len; /* length of buffer */ - struct pci_conf *pci_buf; /* buffer */ + u_int32_t pat_buf_len; /* + * Length of buffer passed in from + * user space. + */ + u_int32_t num_patterns; /* + * Number of pci_match_conf structures + * passed in by the user. + */ + struct pci_match_conf *patterns; /* + * Patterns passed in by the user. + */ + u_int32_t match_buf_len;/* + * Length of match buffer passed + * in by the user. + */ + u_int32_t num_matches; /* + * Number of matches returned by + * the kernel. + */ + struct pci_conf *matches; /* + * PCI device matches returned by + * the kernel. + */ + u_int32_t offset; /* + * Passed in by the user code to + * indicate where the kernel should + * start traversing the device list. + * The value passed out by the kernel + * points to the record immediately + * after the last one returned. + * i.e. this value may be passed back + * unchanged by the user for a + * subsequent call. + */ + u_int32_t generation; /* + * PCI configuration generation. + * This only needs to be set if the + * offset is set. The kernel will + * compare its current generation + * number to the generation passed + * in by the user to determine + * whether the PCI device list has + * changed since the user last + * called the GETCONF ioctl. + */ + pci_getconf_status status; /* + * Status passed back from the + * kernel. + */ }; struct pci_io { diff --git a/usr.sbin/pciconf/pciconf.c b/usr.sbin/pciconf/pciconf.c index 85a09e8..2f2979a 100644 --- a/usr.sbin/pciconf/pciconf.c +++ b/usr.sbin/pciconf/pciconf.c @@ -29,7 +29,7 @@ #ifndef lint static const char rcsid[] = - "$Id$"; + "$Id: pciconf.c,v 1.5 1997/10/06 11:38:30 charnier Exp $"; #endif /* not lint */ #include <sys/types.h> @@ -135,24 +135,54 @@ list_devs(void) struct pci_conf_io pc; struct pci_conf conf[255], *p; - fd = open(_PATH_DEVPCI, O_RDONLY, 0); + fd = open(_PATH_DEVPCI, O_RDWR, 0); if (fd < 0) err(1, "%s", _PATH_DEVPCI); - pc.pci_len = sizeof(conf); - pc.pci_buf = conf; - - if (ioctl(fd, PCIOCGETCONF, &pc) < 0) - err(1, "ioctl(PCIOCGETCONF)"); + bzero(&pc, sizeof(struct pci_conf_io)); + pc.match_buf_len = sizeof(conf); + pc.matches = conf; + + do { + if (ioctl(fd, PCIOCGETCONF, &pc) == -1) + err(1, "ioctl(PCIOCGETCONF)"); + + /* + * 255 entries should be more than enough for most people, + * but if someone has more devices, and then changes things + * around between ioctls, we'll do the cheezy thing and + * just bail. The alternative would be to go back to the + * beginning of the list, and print things twice, which may + * not be desireable. + */ + if (pc.status == PCI_GETCONF_LIST_CHANGED) { + warnx("PCI device list changed, please try again"); + exitstatus = 1; + close(fd); + return; + } else if (pc.status == PCI_GETCONF_ERROR) { + warnx("Error returned from PCIOCGETCONF ioctl"); + exitstatus = 1; + close(fd); + return; + } + for (p = conf; p < &conf[pc.num_matches]; p++) { + if ((p->pd_name == NULL) || (*p->pd_name == '\0')) + continue; + + printf("%s%d@pci%d:%d:%d:\tclass=0x%06x card=0x%08lx " + "chip=0x%08lx rev=0x%02x hdr=0x%02x\n", + p->pd_name, p->pd_unit, + p->pc_sel.pc_bus, p->pc_sel.pc_dev, + p->pc_sel.pc_func, (p->pc_class << 16) | + (p->pc_subclass << 8) | p->pc_progif, + (p->pc_subdevice << 16) | p->pc_subvendor, + (p->pc_device << 16) | p->pc_vendor, + p->pc_revid, p->pc_hdr); + } + } while (pc.status == PCI_GETCONF_MORE_DEVS); close(fd); - - for (p = conf; p < &conf[pc.pci_len / sizeof conf[0]]; p++) { - printf("pci%d:%d:%d:\tclass=0x%06x card=0x%08lx chip=0x%08lx rev=0x%02x hdr=0x%02x\n", - p->pc_sel.pc_bus, p->pc_sel.pc_dev, p->pc_sel.pc_func, - p->pc_class >> 8, p->pc_subid, - p->pc_devid, p->pc_class & 0xff, p->pc_hdr); - } } static struct pcisel |