blob: 65ae624e7c0a260df25134ecd62941d3321d79bc [file] [log] [blame]
// 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 <stdint.h>
#include <stdio.h>
#include <stdbool.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#include <string.h>
#include <ctype.h>
#include <limits.h>
#include <dirent.h>
#include <sys/ioctl.h>
#include <linux/usbdevice_fs.h>
struct usbentry {
int bus_num;
int dev_num;
int vendor_id;
int product_id;
char vendor_name[128];
char product_name[128];
char serial[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)
strncpy(dev.vendor_name, attr, sizeof(dev.vendor_name) - 1);
attr = sysfs_attr(e->d_name, "product");
if (attr)
strncpy(dev.product_name, attr, sizeof(dev.product_name) - 1);
attr = sysfs_attr(e->d_name, "serial");
if (attr)
strncpy(dev.serial, attr, sizeof(dev.serial) - 1);
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;
int max_serial_length = 0;
if (!devs)
return;
/* Calculate the largest size of the serial numbers if present */
while ((dev = parse_devlist(devs)) != NULL) {
int serial_length = strlen(dev->serial);
if (serial_length > max_serial_length)
max_serial_length = serial_length;
}
closedir(devs);
devs = opendir("/sys/bus/usb/devices");
if (!devs)
return;
while ((dev = parse_devlist(devs)) != NULL) {
printf(" Number %03d/%03d ID %04x:%04x ", dev->bus_num, dev->dev_num, dev->vendor_id,
dev->product_id);
if (strlen(dev->serial) > 0)
printf("SN:%-*s", max_serial_length, dev->serial);
else
printf(" %*s", max_serial_length, "");
printf(" %s\n", dev->product_name);
}
closedir(devs);
}
static struct usbentry *find_device(int *bus, int *dev, int *vid, int *pid, const char *serial, 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)) ||
(serial && !strcasecmp(e->serial, serial)) || (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, NULL);
else if ((argc == 2) && (sscanf(argv[1], "%4x:%4x", &id1, &id2) == 2))
dev = find_device(NULL, NULL, &id1, &id2, NULL, NULL);
else if ((argc == 2) && (strncmp(argv[1], "SN:", 3) == 0))
dev = find_device(NULL, NULL, NULL, NULL, &(argv[1])[3], NULL);
else if ((argc == 2) && strlen(argv[1]) < 128)
dev = find_device(NULL, NULL, NULL, NULL, NULL, argv[1]);
else {
printf("Usage:\n"
" usbreset VVVV:PPPP - reset by vendor and product id\n"
" usbreset BBB/DDD - reset by bus and device number\n"
" usbreset SN:SERIAL - reset by serial 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;
}