diff options
-rw-r--r-- | .gitignore | 1 | ||||
-rw-r--r-- | Makefile | 12 | ||||
-rw-r--r-- | usb-print-caps.c | 255 |
3 files changed, 268 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..819a290 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +usb-print-caps diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..12a6610 --- /dev/null +++ b/Makefile @@ -0,0 +1,12 @@ + +CC ?= gcc +CFLAGS ?= -Os +CFLAGS += -Wall + +TARGETS := usb-print-caps + +all: $(TARGETS) + +clean: + rm -f $(TARGETS) + rm -f *~ diff --git a/usb-print-caps.c b/usb-print-caps.c new file mode 100644 index 0000000..fac3b99 --- /dev/null +++ b/usb-print-caps.c @@ -0,0 +1,255 @@ +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <inttypes.h> +#include <fcntl.h> +#include <string.h> +#include <errno.h> +#include <dirent.h> + +#include <sys/stat.h> +#include <sys/mman.h> + +/* ------------------------------------------------------------- */ + +#define BASEPATH "/sys/bus/pci/devices" + +void *mapbar(char *dev, int bar) +{ + char filename[128]; + struct stat st; + void *map; + int fd; + + snprintf(filename, sizeof(filename), + "%s/%s/resource%d", BASEPATH, dev, bar); + fd = open(filename, O_RDONLY); + if (-1 == fd) { + fprintf(stderr, "open %s: %s", filename, strerror(errno)); + return NULL; + } + fstat(fd, &st); + + map = mmap(NULL, st.st_size, PROT_READ, MAP_SHARED, fd, 0); + if (MAP_FAILED == map) { + fprintf(stderr, "mmap %s: %s", filename, strerror(errno)); + return NULL; + } + + close(fd); + return map; +} + +uint32_t readsys(char *dev, char *file) +{ + char filename[128], content[128]; + FILE *fp; + + snprintf(filename, sizeof(filename), + "%s/%s/%s", BASEPATH, dev, file); + fp = fopen(filename, "r"); + if (NULL == fp) + return 0; + fgets(content, sizeof(content), fp); + fclose(fp); + return strtol(content, NULL, 0); +} + +/* ------------------------------------------------------------- */ + +static const char *xhci_capname[] = { + [ 0 ] = "Reserved", + [ 1 ] = "USB Legacy Support", + [ 2 ] = "Supported Protocol", + [ 3 ] = "Extended Power Management", + [ 4 ] = "I/O Virtualization", + [ 5 ] = "Message Interrupt", + [ 6 ] = "Local Memory", + [ 7 ... 9 ] = "Reserved", + [ 10 ] = "USB Debug Capability", + [ 11 ... 16 ] = "Reserved", + [ 17 ] = "Extended Message Interrupt", + [ 18 ... 191 ] = "Reserved", + [ 192 ... 255 ] = "Vendor Defined", +}; + +void xhcidump(char *dev) +{ + uint32_t *map, val; + int i, n, ext = 0, off, cid; + + map = mapbar(dev, 0); + if (!map) + return; + + n = map[0] & 0xff; + if (n > 0x20) + n = 0x20; + printf(" xhci base caps\n"); + for (i = 0; i * 4 < n; i++) { + val = map[i]; + printf(" 0x%04x: 0x%08x", i * 4, val); + switch (i * 4) { + case 0x00: + printf(" - version %x.%02x, caplength 0x%x", + (val >> 24) & 0xff, (val >> 16) & 0xff, val & 0xff); + break; + case 0x04: + printf(" - ports %d, intrs %d, slots %d", + (val >> 24) & 0xff, (val >> 8) & 0x3ff, val & 0xff); + break; + case 0x08: + printf(" - erst max 0x%x, ist 0x%x", + (val >> 4) & 0x0f, val & 0x0f); + break; + case 0x0c: + printf(" - u2 lat 0x%x, u1 lat 0x%x", + (val >> 16) & 0xffff, val & 0xff); + break; + case 0x10: + ext = (val >> 16) & 0xffff; + printf(" - extptr 0x%x, maxpsasize 0x%x%s%s%s%s%s%s%s%s", + ext, (val >> 12) & 0xf, + (val & 0x80) ? ", nss" : "", + (val & 0x40) ? ", ltc" : "", + (val & 0x20) ? ", lhrc" : "", + (val & 0x10) ? ", pind" : "", + (val & 0x08) ? ", ppc" : "", + (val & 0x04) ? ", csz" : "", + (val & 0x02) ? ", bnc" : "", + (val & 0x01) ? ", ac64" : ""); + break; + case 0x14: + printf(" - doorbell offset 0x%x", val); + break; + case 0x18: + printf(" - runtime offset 0x%x", val); + break; + default: + break; + } + printf("\n"); + } + + while (ext) { + printf(" xhci ext cap @ 0x%x\n", ext*4); + val = map[ext]; + off = val >> 8 & 0xff; + cid = val & 0xff; + printf(" 0x%04x: 0x%08x - next 0x%x, cap 0x%x [ %s ]\n", + ext*4, val, off, cid, xhci_capname[cid]); + switch (cid) { + case 2: + printf(" - version %x.%02x\n", + (val >> 24) & 0xff, (val >> 16) & 0xff); + val = map[ext+1]; + printf(" 0x%04x: 0x%08x - name %c%c%c%c\n", + ext*4+4, val, val & 0xff, (val >> 8) & 0xff, + (val >> 16) & 0xff, (val >> 24) & 0xff); + val = map[ext+2]; + printf(" 0x%04x: 0x%08x - psic 0x%x, port count %d, port offset %d\n", + ext*4+8, val, (val >> 28) & 0xf, (val >> 8) & 0xff, val & 0xff); + break; + } + if (off) { + ext += off; + } else { + ext = 0; + } + } + return; +} + +/* ------------------------------------------------------------- */ + +void ehcidump(char *dev) +{ + uint32_t *map, val; + int i, n; + + map = mapbar(dev, 0); + if (!map) + return; + + n = map[0] & 0xff; + if (n > 0x10) + n = 0x10; + printf(" ehci caps\n"); + for (i = 0; i * 4 < n; i++) { + val = map[i]; + printf(" 0x%04x: 0x%08x", i * 4, val); + switch (i * 4) { + case 0x00: + printf(" - version %x.%02x, caplength 0x%x", + (val >> 24) & 0xff, (val >> 16) & 0xff, val & 0xff); + break; + case 0x04: + printf(" - dbg port %d%s, companion %d ctl %d prt%s, ports %d", + (val >> 20) & 0x0f, + (val >> 16) & 0x01 ? ", pi" : "", + (val >> 12) & 0x0f, + (val >> 8) & 0x0f, + (val >> 4) & 0x01 ? ", ppc" : "", + (val >> 0) & 0x07); + break; + case 0x08: + printf(" - extptr 0x%x, iso thresh %d%s%s%s", + (val >> 8) & 0xff, + (val >> 4) & 0x0f, + (val >> 2) & 0x01 ? ", aspc" : "", + (val >> 1) & 0x01 ? ", pflf" : "", + (val >> 0) & 0x01 ? ", 64ac" : ""); + break; + default: + break; + } + printf("\n"); + } + + return; +} + +/* ------------------------------------------------------------- */ + +int main(int argc, char *argv[]) +{ + unsigned long addr0; + struct dirent *entry; + uint32_t class, vendor, device; + DIR *dir; + + dir = opendir(BASEPATH); + while ((entry = readdir(dir)) != NULL) { + if (entry->d_name[0] == '.') + continue; + if (argc > 1 && !strstr(entry->d_name, argv[1])) + continue; + class = readsys(entry->d_name, "class"); + vendor = readsys(entry->d_name, "vendor"); + device = readsys(entry->d_name, "device"); + addr0 = readsys(entry->d_name, "resource"); + switch (class) { + case 0x0c0300: + printf("uhci @ %s %04x:%04x\n", entry->d_name, vendor, device); + break; + case 0x0c0310: + printf("ohci @ %s %04x:%04x\n", entry->d_name, vendor, device); + break; + case 0x0c0320: + printf("ehci @ %s %04x:%04x\n", entry->d_name, vendor, device); + printf(" bar0 @ 0x%lx\n", addr0); + if (addr0 & 0xfff) { + printf(" Oops, not page aligned, skipping\n"); + } else { + ehcidump(entry->d_name); + } + break; + case 0x0c0330: + printf("xhci @ %s %04x:%04x\n", entry->d_name, vendor, device); + printf(" bar0 @ 0x%lx\n", addr0); + xhcidump(entry->d_name); + break; + } + } + return 0; +} |