// SPDX-License-Identifier: GPL-2.0-only /* usbreset -- send a USB port reset to a USB device */ /* Copyright (c) 2009-2016 Alan Stern */ /* To build: gcc -o usbreset usbreset.c */ #include #include #include #include #include #include #include #include #include #include #include #include #include /* static char *usbfs = NULL; */ struct usbentry { int bus_num; int dev_num; int vendor_id; int product_id; char vendor_name[128]; char product_name[128]; }; static char *sysfs_attr(const char *dev, const char *attr) { int fd, len = 0; char path[PATH_MAX]; static char buf[129]; memset(buf, 0, sizeof(buf)); snprintf(path, sizeof(path) - 1, "/sys/bus/usb/devices/%s/%s", dev, attr); fd = open(path, O_RDONLY); if (fd >= 0) { len = read(fd, buf, sizeof(buf) - 1); close(fd); } while (--len > 0 && isspace(buf[len])) buf[len] = 0; return (len >= 0) ? buf : NULL; } static struct usbentry *parse_devlist(DIR *d) { char *attr; struct dirent *e; static struct usbentry dev; do { e = readdir(d); if (!e) return NULL; } while (!isdigit(e->d_name[0]) || strchr(e->d_name, ':')); memset(&dev, 0, sizeof(dev)); dev.vendor_id = -1; dev.product_id = -1; attr = sysfs_attr(e->d_name, "busnum"); if (attr) dev.bus_num = strtoul(attr, NULL, 10); attr = sysfs_attr(e->d_name, "devnum"); if (attr) dev.dev_num = strtoul(attr, NULL, 10); attr = sysfs_attr(e->d_name, "idVendor"); if (attr) dev.vendor_id = strtoul(attr, NULL, 16); attr = sysfs_attr(e->d_name, "idProduct"); if (attr) dev.product_id = strtoul(attr, NULL, 16); attr = sysfs_attr(e->d_name, "manufacturer"); if (attr) strcpy(dev.vendor_name, attr); attr = sysfs_attr(e->d_name, "product"); if (attr) strcpy(dev.product_name, attr); if (dev.bus_num && dev.dev_num && dev.vendor_id >= 0 && dev.product_id >= 0) return &dev; return NULL; } static void list_devices(void) { DIR *devs = opendir("/sys/bus/usb/devices"); struct usbentry *dev; if (!devs) return; while ((dev = parse_devlist(devs)) != NULL) printf(" Number %03d/%03d ID %04x:%04x %s\n", dev->bus_num, dev->dev_num, dev->vendor_id, dev->product_id, dev->product_name); closedir(devs); } static struct usbentry *find_device(int *bus, int *dev, int *vid, int *pid, const char *product) { DIR *devs = opendir("/sys/bus/usb/devices"); struct usbentry *e, *match = NULL; if (!devs) return NULL; while ((e = parse_devlist(devs)) != NULL) if ((bus && (e->bus_num == *bus) && (e->dev_num == *dev)) || (vid && (e->vendor_id == *vid) && (e->product_id == *pid)) || (product && !strcasecmp(e->product_name, product))) { match = e; break; } closedir(devs); return match; } static void reset_device(struct usbentry *dev) { int fd; char path[PATH_MAX]; snprintf(path, sizeof(path) - 1, "/dev/bus/usb/%03d/%03d", dev->bus_num, dev->dev_num); printf("Resetting %s ... ", dev->product_name); fd = open(path, O_WRONLY); if (fd > -1) { if (ioctl(fd, USBDEVFS_RESET, 0) < 0) printf("failed [%s]\n", strerror(errno)); else printf("ok\n"); close(fd); } else { printf("can't open [%s]\n", strerror(errno)); } } int main(int argc, char **argv) { int id1, id2; struct usbentry *dev; if ((argc == 2) && (sscanf(argv[1], "%3d/%3d", &id1, &id2) == 2)) dev = find_device(&id1, &id2, NULL, NULL, NULL); else if ((argc == 2) && (sscanf(argv[1], "%4x:%4x", &id1, &id2) == 2)) dev = find_device(NULL, NULL, &id1, &id2, NULL); else if ((argc == 2) && strlen(argv[1]) < 128) dev = find_device(NULL, NULL, NULL, NULL, argv[1]); else { printf("Usage:\n" " usbreset PPPP:VVVV - reset by product and vendor id\n" " usbreset BBB/DDD - reset by bus and device number\n" " usbreset \"Product\" - reset by product name\n\n" "Devices:\n"); list_devices(); return 1; } if (!dev) { fprintf(stderr, "No such device found\n"); return 1; } reset_device(dev); return 0; }