diff options
author | jhb <jhb@FreeBSD.org> | 2014-01-28 21:56:18 +0000 |
---|---|---|
committer | jhb <jhb@FreeBSD.org> | 2014-01-28 21:56:18 +0000 |
commit | 2fa5ef25cfad3ff1ad86af1db82d1d83a971e318 (patch) | |
tree | 0e8c2ea1f453e7e3e757500d7895d1cea1528ca4 /usr.sbin/pciconf/pciconf.c | |
parent | df3ef14ce8aad05588a6219bd20053233eeb5588 (diff) | |
download | FreeBSD-src-2fa5ef25cfad3ff1ad86af1db82d1d83a971e318.zip FreeBSD-src-2fa5ef25cfad3ff1ad86af1db82d1d83a971e318.tar.gz |
MFC 260910:
- Allow PCI devices that are attached to a driver to be identified by their
device name instead of just the selector.
- Accept an optional device argument to -l to restrict the output to only
listing details about a single device. This is mostly useful in
conjunction with other flags like -e or -c to allow a user to query
details about a single device.
Diffstat (limited to 'usr.sbin/pciconf/pciconf.c')
-rw-r--r-- | usr.sbin/pciconf/pciconf.c | 101 |
1 files changed, 92 insertions, 9 deletions
diff --git a/usr.sbin/pciconf/pciconf.c b/usr.sbin/pciconf/pciconf.c index 58ffddd..0d1bbde 100644 --- a/usr.sbin/pciconf/pciconf.c +++ b/usr.sbin/pciconf/pciconf.c @@ -35,6 +35,7 @@ static const char rcsid[] = #include <sys/types.h> #include <sys/fcntl.h> +#include <assert.h> #include <ctype.h> #include <err.h> #include <inttypes.h> @@ -67,8 +68,10 @@ struct pci_vendor_info TAILQ_HEAD(,pci_vendor_info) pci_vendors; +static struct pcisel getsel(const char *str); static void list_bars(int fd, struct pci_conf *p); -static void list_devs(int verbose, int bars, int caps, int errors); +static void list_devs(const char *name, int verbose, int bars, int caps, + int errors); static void list_verbose(struct pci_conf *p); static const char *guess_class(struct pci_conf *p); static const char *guess_subclass(struct pci_conf *p); @@ -83,10 +86,10 @@ static void usage(void) { fprintf(stderr, "%s\n%s\n%s\n%s\n", - "usage: pciconf -l [-bcev]", - " pciconf -a selector", - " pciconf -r [-b | -h] selector addr[:addr2]", - " pciconf -w [-b | -h] selector addr value"); + "usage: pciconf -l [-bcev] [device]", + " pciconf -a device", + " pciconf -r [-b | -h] device addr[:addr2]", + " pciconf -w [-b | -h] device addr value"); exit (1); } @@ -145,14 +148,15 @@ main(int argc, char **argv) } } - if ((listmode && optind != argc) + if ((listmode && optind >= argc + 1) || (writemode && optind + 3 != argc) || (readmode && optind + 2 != argc) || (attachedmode && optind + 1 != argc)) usage(); if (listmode) { - list_devs(verbose, bars, caps, errors); + list_devs(optind + 1 == argc ? argv[optind] : NULL, verbose, + bars, caps, errors); } else if (attachedmode) { chkattached(argv[optind]); } else if (readmode) { @@ -169,11 +173,12 @@ main(int argc, char **argv) } static void -list_devs(int verbose, int bars, int caps, int errors) +list_devs(const char *name, int verbose, int bars, int caps, int errors) { int fd; struct pci_conf_io pc; struct pci_conf conf[255], *p; + struct pci_match_conf patterns[1]; int none_count = 0; if (verbose) @@ -186,6 +191,16 @@ list_devs(int verbose, int bars, int caps, int errors) bzero(&pc, sizeof(struct pci_conf_io)); pc.match_buf_len = sizeof(conf); pc.matches = conf; + if (name != NULL) { + bzero(&patterns, sizeof(patterns)); + patterns[0].pc_sel = getsel(name); + patterns[0].flags = PCI_GETCONF_MATCH_DOMAIN | + PCI_GETCONF_MATCH_BUS | PCI_GETCONF_MATCH_DEV | + PCI_GETCONF_MATCH_FUNC; + pc.num_patterns = 1; + pc.pat_buf_len = sizeof(patterns); + pc.patterns = patterns; + } do { if (ioctl(fd, PCIOCGETCONF, &pc) == -1) @@ -557,7 +572,61 @@ read_config(int fd, struct pcisel *sel, long reg, int width) } static struct pcisel -getsel(const char *str) +getdevice(const char *name) +{ + struct pci_conf_io pc; + struct pci_conf conf[1]; + struct pci_match_conf patterns[1]; + char *cp; + int fd; + + fd = open(_PATH_DEVPCI, O_RDONLY, 0); + if (fd < 0) + err(1, "%s", _PATH_DEVPCI); + + bzero(&pc, sizeof(struct pci_conf_io)); + pc.match_buf_len = sizeof(conf); + pc.matches = conf; + + bzero(&patterns, sizeof(patterns)); + + /* + * The pattern structure requires the unit to be split out from + * the driver name. Walk backwards from the end of the name to + * find the start of the unit. + */ + if (name[0] == '\0') + err(1, "Empty device name"); + cp = strchr(name, '\0'); + assert(cp != NULL && cp != name); + cp--; + while (cp != name && isdigit(cp[-1])) + cp--; + if (cp == name) + errx(1, "Invalid device name"); + if ((size_t)(cp - name) + 1 > sizeof(patterns[0].pd_name)) + errx(1, "Device name i2s too long"); + memcpy(patterns[0].pd_name, name, cp - name); + patterns[0].pd_unit = strtol(cp, &cp, 10); + assert(*cp == '\0'); + patterns[0].flags = PCI_GETCONF_MATCH_NAME | PCI_GETCONF_MATCH_UNIT; + pc.num_patterns = 1; + pc.pat_buf_len = sizeof(patterns); + pc.patterns = patterns; + + if (ioctl(fd, PCIOCGETCONF, &pc) == -1) + err(1, "ioctl(PCIOCGETCONF)"); + if (pc.status != PCI_GETCONF_LAST_DEVICE && + pc.status != PCI_GETCONF_MORE_DEVS) + errx(1, "error returned from PCIOCGETCONF ioctl"); + close(fd); + if (pc.num_matches == 0) + errx(1, "Device not found"); + return (conf[0].pc_sel); +} + +static struct pcisel +parsesel(const char *str) { char *ep = strchr(str, '@'); char *epbase; @@ -595,6 +664,20 @@ getsel(const char *str) return sel; } +static struct pcisel +getsel(const char *str) +{ + + /* + * No device names contain colons and selectors always contain + * at least one colon. + */ + if (strchr(str, ':') == NULL) + return (getdevice(str)); + else + return (parsesel(str)); +} + static void readone(int fd, struct pcisel *sel, long reg, int width) { |