summaryrefslogtreecommitdiffstats
path: root/usr.sbin/pciconf/pciconf.c
diff options
context:
space:
mode:
authorjhb <jhb@FreeBSD.org>2014-01-28 21:56:18 +0000
committerjhb <jhb@FreeBSD.org>2014-01-28 21:56:18 +0000
commit2fa5ef25cfad3ff1ad86af1db82d1d83a971e318 (patch)
tree0e8c2ea1f453e7e3e757500d7895d1cea1528ca4 /usr.sbin/pciconf/pciconf.c
parentdf3ef14ce8aad05588a6219bd20053233eeb5588 (diff)
downloadFreeBSD-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.c101
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)
{
OpenPOWER on IntegriCloud