| 1 | /* usbreset -- send a USB port reset to a USB device */ |
| 2 | |
| 3 | /* |
| 4 | |
| 5 | http://marc.info/?l=linux-usb-users&m=116827193506484&w=2 |
| 6 | |
| 7 | and needs mounted usbfs filesystem |
| 8 | |
| 9 | sudo mount -t usbfs none /proc/bus/usb |
| 10 | |
| 11 | There is a way to suspend a USB device. In order to use it, |
| 12 | you must have a kernel with CONFIG_PM_SYSFS_DEPRECATED turned on. To |
| 13 | suspend a device, do (as root): |
| 14 | |
| 15 | echo -n 2 >/sys/bus/usb/devices/.../power/state |
| 16 | |
| 17 | where the "..." is the ID for your device. To unsuspend, do the same |
| 18 | thing but with a "0" instead of the "2" above. |
| 19 | |
| 20 | Note that this mechanism is slated to be removed from the kernel within |
| 21 | the next year. Hopefully some other mechanism will take its place. |
| 22 | |
| 23 | > To reset a |
| 24 | > device? |
| 25 | |
| 26 | Here's a program to do it. You invoke it as either |
| 27 | |
| 28 | usbreset /proc/bus/usb/BBB/DDD |
| 29 | or |
| 30 | usbreset /dev/usbB.D |
| 31 | |
| 32 | depending on how your system is set up, where BBB and DDD are the bus and |
| 33 | device address numbers. |
| 34 | |
| 35 | Alan Stern |
| 36 | |
| 37 | */ |
| 38 | |
| 39 | #include <stdio.h> |
| 40 | #include <stdbool.h> |
| 41 | #include <unistd.h> |
| 42 | #include <fcntl.h> |
| 43 | #include <errno.h> |
| 44 | #include <string.h> |
| 45 | #include <sys/ioctl.h> |
| 46 | |
| 47 | #include <linux/usbdevice_fs.h> |
| 48 | |
| 49 | |
| 50 | static char *usbfs = NULL; |
| 51 | |
| 52 | struct usbentry { |
| 53 | int bus_num; |
| 54 | int dev_num; |
| 55 | int vendor_id; |
| 56 | int product_id; |
| 57 | char vendor_name[128]; |
| 58 | char product_name[128]; |
| 59 | }; |
| 60 | |
| 61 | |
| 62 | static bool find_usbfs(void) |
| 63 | { |
| 64 | FILE *mtab; |
| 65 | |
| 66 | char buf[1024], type[32]; |
| 67 | static char path[1024]; |
| 68 | |
| 69 | if ((mtab = fopen("/proc/mounts", "r")) != NULL) |
| 70 | { |
| 71 | while (fgets(buf, sizeof(buf), mtab)) |
| 72 | { |
| 73 | if (sscanf(buf, "%*s %1023s %31s ", path, type) == 2 && |
| 74 | !strncmp(type, "usbfs", 5)) |
| 75 | { |
| 76 | usbfs = path; |
| 77 | break; |
| 78 | } |
| 79 | } |
| 80 | |
| 81 | fclose(mtab); |
| 82 | } |
| 83 | |
| 84 | return !!usbfs; |
| 85 | } |
| 86 | |
| 87 | static FILE * open_devlist(void) |
| 88 | { |
| 89 | char buf[1024]; |
| 90 | snprintf(buf, sizeof(buf), "%s/devices", usbfs); |
| 91 | return fopen(buf, "r"); |
| 92 | } |
| 93 | |
| 94 | static void close_devlist(FILE *devs) |
| 95 | { |
| 96 | fclose(devs); |
| 97 | } |
| 98 | |
| 99 | static struct usbentry * parse_devlist(FILE *devs) |
| 100 | { |
| 101 | char buf[1024]; |
| 102 | static struct usbentry dev; |
| 103 | |
| 104 | memset(&dev, 0, sizeof(dev)); |
| 105 | |
| 106 | while (fgets(buf, sizeof(buf), devs)) |
| 107 | { |
| 108 | buf[strlen(buf)-1] = 0; |
| 109 | |
| 110 | switch (buf[0]) |
| 111 | { |
| 112 | case 'T': |
| 113 | sscanf(buf, "T: Bus=%d Lev=%*d Prnt=%*d Port=%*d Cnt=%*d Dev#=%d", |
| 114 | &dev.bus_num, &dev.dev_num); |
| 115 | break; |
| 116 | |
| 117 | case 'P': |
| 118 | sscanf(buf, "P: Vendor=%x ProdID=%x", |
| 119 | &dev.vendor_id, &dev.product_id); |
| 120 | break; |
| 121 | |
| 122 | case 'S': |
| 123 | if (!strncmp(buf, "S: Manufacturer=", 17)) |
| 124 | snprintf(dev.vendor_name, sizeof(dev.vendor_name), |
| 125 | "%s", buf+17); |
| 126 | else if (!strncmp(buf, "S: Product=", 12)) |
| 127 | snprintf(dev.product_name, sizeof(dev.product_name), |
| 128 | "%s", buf+12); |
| 129 | break; |
| 130 | } |
| 131 | |
| 132 | if (dev.product_name[0]) |
| 133 | return &dev; |
| 134 | } |
| 135 | |
| 136 | return NULL; |
| 137 | } |
| 138 | |
| 139 | static void list_devices(void) |
| 140 | { |
| 141 | FILE *devs = open_devlist(); |
| 142 | struct usbentry *dev; |
| 143 | |
| 144 | if (!devs) |
| 145 | return; |
| 146 | |
| 147 | while ((dev = parse_devlist(devs)) != NULL) |
| 148 | { |
| 149 | printf(" Number %03d/%03d ID %04x:%04x %s\n", |
| 150 | dev->bus_num, dev->dev_num, |
| 151 | dev->vendor_id, dev->product_id, |
| 152 | dev->product_name); |
| 153 | } |
| 154 | |
| 155 | close_devlist(devs); |
| 156 | } |
| 157 | |
| 158 | struct usbentry * find_device(int *bus, int *dev, |
| 159 | int *vid, int *pid, |
| 160 | const char *product) |
| 161 | { |
| 162 | FILE *devs = open_devlist(); |
| 163 | |
| 164 | struct usbentry *e, *match = NULL; |
| 165 | |
| 166 | if (!devs) |
| 167 | return NULL; |
| 168 | |
| 169 | while ((e = parse_devlist(devs)) != NULL) |
| 170 | { |
| 171 | if ((bus && (e->bus_num == *bus) && (e->dev_num == *dev)) || |
| 172 | (vid && (e->vendor_id == *vid) && (e->product_id == *pid)) || |
| 173 | (product && !strcasecmp(e->product_name, product))) |
| 174 | { |
| 175 | match = e; |
| 176 | break; |
| 177 | } |
| 178 | } |
| 179 | |
| 180 | close_devlist(devs); |
| 181 | |
| 182 | return match; |
| 183 | } |
| 184 | |
| 185 | static void reset_device(struct usbentry *dev) |
| 186 | { |
| 187 | int fd; |
| 188 | char path[1024]; |
| 189 | |
| 190 | snprintf(path, sizeof(path), "%s/%03d/%03d", |
| 191 | usbfs, dev->bus_num, dev->dev_num); |
| 192 | |
| 193 | printf("Resetting %s ... ", dev->product_name); |
| 194 | |
| 195 | if ((fd = open(path, O_WRONLY)) > -1) |
| 196 | { |
| 197 | if (ioctl(fd, USBDEVFS_RESET, 0) < 0) |
| 198 | printf("failed [%s]\n", strerror(errno)); |
| 199 | else |
| 200 | printf("ok\n"); |
| 201 | |
| 202 | close(fd); |
| 203 | } |
| 204 | else |
| 205 | { |
| 206 | printf("can't open [%s]\n", strerror(errno)); |
| 207 | } |
| 208 | } |
| 209 | |
| 210 | |
| 211 | int main(int argc, char **argv) |
| 212 | { |
| 213 | int id1, id2; |
| 214 | struct usbentry *dev; |
| 215 | |
| 216 | if (!find_usbfs()) |
| 217 | { |
| 218 | fprintf(stderr, "Unable to find usbfs, is it mounted?\n"); |
| 219 | return 1; |
| 220 | } |
| 221 | |
| 222 | if ((argc == 2) && (sscanf(argv[1], "%3d/%3d", &id1, &id2) == 2)) |
| 223 | { |
| 224 | dev = find_device(&id1, &id2, NULL, NULL, NULL); |
| 225 | } |
| 226 | else if ((argc == 2) && (sscanf(argv[1], "%4x:%4x", &id1, &id2) == 2)) |
| 227 | { |
| 228 | dev = find_device(NULL, NULL, &id1, &id2, NULL); |
| 229 | } |
| 230 | else if ((argc == 2) && strlen(argv[1]) < 128) |
| 231 | { |
| 232 | dev = find_device(NULL, NULL, NULL, NULL, argv[1]); |
| 233 | } |
| 234 | else |
| 235 | { |
| 236 | printf("Usage:\n" |
| 237 | " usbreset PPPP:VVVV - reset by product and vendor id\n" |
| 238 | " usbreset BBB/DDD - reset by bus and device number\n" |
| 239 | " usbreset \"Product\" - reset by product name\n\n" |
| 240 | "Devices:\n"); |
| 241 | list_devices(); |
| 242 | return 1; |
| 243 | } |
| 244 | |
| 245 | if (!dev) |
| 246 | { |
| 247 | fprintf(stderr, "No such device found\n"); |
| 248 | return 1; |
| 249 | } |
| 250 | |
| 251 | reset_device(dev); |
| 252 | return 0; |
| 253 | } |
| 254 | |