usbreset: rewrite to not rely on /proc/bus/usb anymore
[openwrt/openwrt.git] / package / utils / usbreset / src / usbreset.c
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 <ctype.h>
46 #include <limits.h>
47 #include <dirent.h>
48 #include <sys/ioctl.h>
49 #include <sys/types.h>
50
51 #include <linux/usbdevice_fs.h>
52
53
54 static char *usbfs = NULL;
55
56 struct usbentry {
57 int bus_num;
58 int dev_num;
59 int vendor_id;
60 int product_id;
61 char vendor_name[128];
62 char product_name[128];
63 };
64
65
66 static char *sysfs_attr(const char *dev, const char *attr)
67 {
68 int fd, len;
69 char path[PATH_MAX];
70 static char buf[129];
71
72 memset(buf, 0, sizeof(buf));
73 snprintf(path, sizeof(path) - 1, "/sys/bus/usb/devices/%s/%s", dev, attr);
74
75 if ((fd = open(path, O_RDONLY)) >= 0)
76 {
77 len = read(fd, buf, sizeof(buf) - 1);
78 close(fd);
79 }
80
81 while (--len > 0 && isspace(buf[len]))
82 buf[len] = 0;
83
84 return (len >= 0) ? buf : NULL;
85 }
86
87 static struct usbentry * parse_devlist(DIR *d)
88 {
89 char *attr;
90 struct dirent *e;
91 static struct usbentry dev;
92
93 do {
94 e = readdir(d);
95
96 if (!e)
97 return NULL;
98 }
99 while(!isdigit(e->d_name[0]) || strchr(e->d_name, ':'));
100
101 memset(&dev, 0, sizeof(dev));
102
103 if ((attr = sysfs_attr(e->d_name, "busnum")) != NULL)
104 dev.bus_num = strtoul(attr, NULL, 10);
105
106 if ((attr = sysfs_attr(e->d_name, "devnum")) != NULL)
107 dev.dev_num = strtoul(attr, NULL, 10);
108
109 if ((attr = sysfs_attr(e->d_name, "idVendor")) != NULL)
110 dev.vendor_id = strtoul(attr, NULL, 16);
111
112 if ((attr = sysfs_attr(e->d_name, "idProduct")) != NULL)
113 dev.product_id = strtoul(attr, NULL, 16);
114
115 if ((attr = sysfs_attr(e->d_name, "manufacturer")) != NULL)
116 strcpy(dev.vendor_name, attr);
117
118 if ((attr = sysfs_attr(e->d_name, "product")) != NULL)
119 strcpy(dev.product_name, attr);
120
121 if (dev.bus_num && dev.dev_num && dev.vendor_id && dev.product_id)
122 return &dev;
123
124 return NULL;
125 }
126
127 static void list_devices(void)
128 {
129 DIR *devs = opendir("/sys/bus/usb/devices");
130 struct usbentry *dev;
131
132 if (!devs)
133 return;
134
135 while ((dev = parse_devlist(devs)) != NULL)
136 {
137 printf(" Number %03d/%03d ID %04x:%04x %s\n",
138 dev->bus_num, dev->dev_num,
139 dev->vendor_id, dev->product_id,
140 dev->product_name);
141 }
142
143 closedir(devs);
144 }
145
146 struct usbentry * find_device(int *bus, int *dev,
147 int *vid, int *pid,
148 const char *product)
149 {
150 DIR *devs = opendir("/sys/bus/usb/devices");
151
152 struct usbentry *e, *match = NULL;
153
154 if (!devs)
155 return NULL;
156
157 while ((e = parse_devlist(devs)) != NULL)
158 {
159 if ((bus && (e->bus_num == *bus) && (e->dev_num == *dev)) ||
160 (vid && (e->vendor_id == *vid) && (e->product_id == *pid)) ||
161 (product && !strcasecmp(e->product_name, product)))
162 {
163 match = e;
164 break;
165 }
166 }
167
168 closedir(devs);
169
170 return match;
171 }
172
173 static void reset_device(struct usbentry *dev)
174 {
175 int fd;
176 char path[PATH_MAX];
177
178 snprintf(path, sizeof(path) - 1, "/dev/bus/usb/%03d/%03d",
179 dev->bus_num, dev->dev_num);
180
181 printf("Resetting %s ... ", dev->product_name);
182
183 if ((fd = open(path, O_WRONLY)) > -1)
184 {
185 if (ioctl(fd, USBDEVFS_RESET, 0) < 0)
186 printf("failed [%s]\n", strerror(errno));
187 else
188 printf("ok\n");
189
190 close(fd);
191 }
192 else
193 {
194 printf("can't open [%s]\n", strerror(errno));
195 }
196 }
197
198
199 int main(int argc, char **argv)
200 {
201 int id1, id2;
202 struct usbentry *dev;
203
204 if ((argc == 2) && (sscanf(argv[1], "%3d/%3d", &id1, &id2) == 2))
205 {
206 dev = find_device(&id1, &id2, NULL, NULL, NULL);
207 }
208 else if ((argc == 2) && (sscanf(argv[1], "%4x:%4x", &id1, &id2) == 2))
209 {
210 dev = find_device(NULL, NULL, &id1, &id2, NULL);
211 }
212 else if ((argc == 2) && strlen(argv[1]) < 128)
213 {
214 dev = find_device(NULL, NULL, NULL, NULL, argv[1]);
215 }
216 else
217 {
218 printf("Usage:\n"
219 " usbreset PPPP:VVVV - reset by product and vendor id\n"
220 " usbreset BBB/DDD - reset by bus and device number\n"
221 " usbreset \"Product\" - reset by product name\n\n"
222 "Devices:\n");
223 list_devices();
224 return 1;
225 }
226
227 if (!dev)
228 {
229 fprintf(stderr, "No such device found\n");
230 return 1;
231 }
232
233 reset_device(dev);
234 return 0;
235 }