Merge lp:~lazyranma/ubuntu/wily/libusb/bug-1455924 into lp:ubuntu/wily/libusb
- Wily (15.10)
- bug-1455924
- Merge into wily
Status: | Rejected |
---|---|
Rejected by: | Steve Langasek |
Proposed branch: | lp:~lazyranma/ubuntu/wily/libusb/bug-1455924 |
Merge into: | lp:ubuntu/wily/libusb |
Diff against target: |
873 lines (+817/-2) 6 files modified
.pc/12_hang_after_resume.diff/linux.c (+742/-0) .pc/applied-patches (+1/-0) debian/changelog (+7/-0) debian/patches/12_hang_after_resume.diff (+47/-0) debian/patches/series (+1/-0) linux.c (+19/-2) |
To merge this branch: | bzr merge lp:~lazyranma/ubuntu/wily/libusb/bug-1455924 |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Steve Langasek | Disapprove | ||
Review via email: mp+259330@code.launchpad.net |
Commit message
Description of the change
Fixes LP: #1455924
- 21. By Alexander Ponyatykh
-
Changed version to ppa1
Iain Lane (laney) wrote : | # |
Alexander Ponyatykh (lazyranma) wrote : | # |
Legacy libusb is deprecated and not updated since 2006-03-04, upstream recommends to migrate to libusb-compat-0.1 http://
Debian uses the same version of the package (https:/
Ćukasz Zemczak (sil2100) wrote : | # |
Thanks for forwarding this to Debian as well! I suppose until this gets reviewed and approved in Debian, I'm willing to sponsor this patch to the Ubuntu archives. The patch itself makes sense and the code is well written. I like it because, even though in normal operation such carefulness shouldn't be required, it's an additional safety net to make sure the ioctl will not block forever.
Before I proceed with uploading though, do you think you could think of some good test-case or reproduction steps for those not using g15daemon? I know this seems to be happening occasionally in that particular use-case, but it would be nice if a way of reproducing the original problem for reviewers was available. I imagine that might be a bit tricky to do.
Alexander Ponyatykh (lazyranma) wrote : | # |
I've made a program that reproduces this problem: http://
1) $ gcc test.c -o test -lusb
2) use lsusb to find VID and PID of your mouse, e.g. 046d:c07c.
3) # ./test VID PID # driver will be detached, replug the mouse to make it work again after you are done.
4) You'll see dots when you move mouse or press buttons and every 1 second of inactivity.
5) # rtcwake -m mem -s 5 # or pm-suspend
6) If dots don't appear any more, then you've hit the bug, otherwise repeat from 5.
Steve Langasek (vorlon) wrote : | # |
This patch looks correct, but as you note this library is deprecated. I don't think it makes sense for us to patch this code in Ubuntu, as opposed to either:
- working to migrate the remaining reverse-
- working with the Debian maintainer (who maintains both libusb and libusb-1.0) to replace libusb-0.1 with libusb-compat-0.1.
Unmerged revisions
- 22. By Alexander Ponyatykh
-
New upstream release.
- 21. By Alexander Ponyatykh
-
Changed version to ppa1
- 20. By Alexander Ponyatykh
-
Added debian/
patches/ 12_hang_ after_resume. diff to fix occasional hang
after suspend/resume. (LP: #1455924)
Preview Diff
1 | === added directory '.pc/12_hang_after_resume.diff' |
2 | === added file '.pc/12_hang_after_resume.diff/linux.c' |
3 | --- .pc/12_hang_after_resume.diff/linux.c 1970-01-01 00:00:00 +0000 |
4 | +++ .pc/12_hang_after_resume.diff/linux.c 2015-05-21 08:58:22 +0000 |
5 | @@ -0,0 +1,742 @@ |
6 | +/* |
7 | + * Linux USB support |
8 | + * |
9 | + * Copyright (c) 2000-2003 Johannes Erdfelt <johannes@erdfelt.com> |
10 | + * |
11 | + * This library is covered by the LGPL, read LICENSE for details. |
12 | + */ |
13 | + |
14 | +#include <stdlib.h> /* getenv, etc */ |
15 | +#include <unistd.h> |
16 | +#include <string.h> |
17 | +#include <stdio.h> |
18 | +#include <fcntl.h> |
19 | +#include <errno.h> |
20 | +#include <sys/time.h> |
21 | +#include <dirent.h> |
22 | + |
23 | +#include "linux.h" |
24 | +#include "usbi.h" |
25 | + |
26 | +static char usb_path[PATH_MAX + 1] = ""; |
27 | + |
28 | +static int device_open(struct usb_device *dev) |
29 | +{ |
30 | + char filename[PATH_MAX + 1]; |
31 | + int fd; |
32 | + |
33 | + snprintf(filename, sizeof(filename) - 1, "%s/%s/%s", |
34 | + usb_path, dev->bus->dirname, dev->filename); |
35 | + |
36 | + fd = open(filename, O_RDWR); |
37 | + if (fd < 0) { |
38 | + fd = open(filename, O_RDONLY); |
39 | + if (fd < 0) |
40 | + USB_ERROR_STR(-errno, "failed to open %s: %s", |
41 | + filename, strerror(errno)); |
42 | + } |
43 | + |
44 | + return fd; |
45 | +} |
46 | + |
47 | +int usb_os_open(usb_dev_handle *dev) |
48 | +{ |
49 | + dev->fd = device_open(dev->device); |
50 | + |
51 | + return 0; |
52 | +} |
53 | + |
54 | +int usb_os_close(usb_dev_handle *dev) |
55 | +{ |
56 | + if (dev->fd < 0) |
57 | + return 0; |
58 | + |
59 | + if (close(dev->fd) == -1) |
60 | + /* Failing trying to close a file really isn't an error, so return 0 */ |
61 | + USB_ERROR_STR(0, "tried to close device fd %d: %s", dev->fd, |
62 | + strerror(errno)); |
63 | + |
64 | + return 0; |
65 | +} |
66 | + |
67 | +int usb_set_configuration(usb_dev_handle *dev, int configuration) |
68 | +{ |
69 | + int ret; |
70 | + |
71 | + ret = ioctl(dev->fd, IOCTL_USB_SETCONFIG, &configuration); |
72 | + if (ret < 0) |
73 | + USB_ERROR_STR(-errno, "could not set config %d: %s", configuration, |
74 | + strerror(errno)); |
75 | + |
76 | + dev->config = configuration; |
77 | + |
78 | + return 0; |
79 | +} |
80 | + |
81 | +int usb_claim_interface(usb_dev_handle *dev, int interface) |
82 | +{ |
83 | + int ret; |
84 | + |
85 | + ret = ioctl(dev->fd, IOCTL_USB_CLAIMINTF, &interface); |
86 | + if (ret < 0) { |
87 | + if (errno == EBUSY && usb_debug > 0) |
88 | + fprintf(stderr, "Check that you have permissions to write to %s/%s and, if you don't, that you set up hotplug (http://linux-hotplug.sourceforge.net/) correctly.\n", dev->bus->dirname, dev->device->filename); |
89 | + |
90 | + USB_ERROR_STR(-errno, "could not claim interface %d: %s", interface, |
91 | + strerror(errno)); |
92 | + } |
93 | + |
94 | + dev->interface = interface; |
95 | + |
96 | + return 0; |
97 | +} |
98 | + |
99 | +int usb_release_interface(usb_dev_handle *dev, int interface) |
100 | +{ |
101 | + int ret; |
102 | + |
103 | + ret = ioctl(dev->fd, IOCTL_USB_RELEASEINTF, &interface); |
104 | + if (ret < 0) |
105 | + USB_ERROR_STR(-errno, "could not release intf %d: %s", interface, |
106 | + strerror(errno)); |
107 | + |
108 | + dev->interface = -1; |
109 | + |
110 | + return 0; |
111 | +} |
112 | + |
113 | +int usb_set_altinterface(usb_dev_handle *dev, int alternate) |
114 | +{ |
115 | + int ret; |
116 | + struct usb_setinterface setintf; |
117 | + |
118 | + if (dev->interface < 0) |
119 | + USB_ERROR(-EINVAL); |
120 | + |
121 | + setintf.interface = dev->interface; |
122 | + setintf.altsetting = alternate; |
123 | + |
124 | + ret = ioctl(dev->fd, IOCTL_USB_SETINTF, &setintf); |
125 | + if (ret < 0) |
126 | + USB_ERROR_STR(-errno, "could not set alt intf %d/%d: %s", |
127 | + dev->interface, alternate, strerror(errno)); |
128 | + |
129 | + dev->altsetting = alternate; |
130 | + |
131 | + return 0; |
132 | +} |
133 | + |
134 | +/* |
135 | + * Linux usbfs has a limit of one page size for synchronous bulk read/write. |
136 | + * 4096 is the most portable maximum we can do for now. |
137 | + * Linux usbfs has a limit of 16KB for the URB interface. We use this now |
138 | + * to get better performance for USB 2.0 devices. |
139 | + */ |
140 | +#define MAX_READ_WRITE (16 * 1024) |
141 | + |
142 | +int usb_control_msg(usb_dev_handle *dev, int requesttype, int request, |
143 | + int value, int index, char *bytes, int size, int timeout) |
144 | +{ |
145 | + struct usb_ctrltransfer ctrl; |
146 | + int ret; |
147 | + |
148 | + ctrl.bRequestType = requesttype; |
149 | + ctrl.bRequest = request; |
150 | + ctrl.wValue = value; |
151 | + ctrl.wIndex = index; |
152 | + ctrl.wLength = size; |
153 | + |
154 | + ctrl.data = bytes; |
155 | + ctrl.timeout = timeout; |
156 | + |
157 | + ret = ioctl(dev->fd, IOCTL_USB_CONTROL, &ctrl); |
158 | + if (ret < 0) |
159 | + USB_ERROR_STR(-errno, "error sending control message: %s", strerror(errno)); |
160 | + |
161 | + return ret; |
162 | +} |
163 | + |
164 | +#define URB_USERCONTEXT_COOKIE ((void *)0x1) |
165 | + |
166 | +/* Reading and writing are the same except for the endpoint */ |
167 | +static int usb_urb_transfer(usb_dev_handle *dev, int ep, int urbtype, |
168 | + char *bytes, int size, int timeout) |
169 | +{ |
170 | + struct usb_urb urb; |
171 | + int bytesdone = 0, requested; |
172 | + struct timeval tv, tv_ref, tv_now; |
173 | + struct usb_urb *context; |
174 | + int ret, waiting; |
175 | + |
176 | + /* |
177 | + * HACK: The use of urb.usercontext is a hack to get threaded applications |
178 | + * sort of working again. Threaded support is still not recommended, but |
179 | + * this should allow applications to work in the common cases. Basically, |
180 | + * if we get the completion for an URB we're not waiting for, then we update |
181 | + * the usercontext pointer to 1 for the other threads URB and it will see |
182 | + * the change after it wakes up from the the timeout. Ugly, but it works. |
183 | + */ |
184 | + |
185 | + /* |
186 | + * Get actual time, and add the timeout value. The result is the absolute |
187 | + * time where we have to quit waiting for an message. |
188 | + */ |
189 | + gettimeofday(&tv_ref, NULL); |
190 | + tv_ref.tv_sec = tv_ref.tv_sec + timeout / 1000; |
191 | + tv_ref.tv_usec = tv_ref.tv_usec + (timeout % 1000) * 1000; |
192 | + |
193 | + if (tv_ref.tv_usec > 1000000) { |
194 | + tv_ref.tv_usec -= 1000000; |
195 | + tv_ref.tv_sec++; |
196 | + } |
197 | + |
198 | + waiting = 1; |
199 | + |
200 | + do { |
201 | + fd_set writefds; |
202 | + |
203 | + requested = size - bytesdone; |
204 | + if (requested > MAX_READ_WRITE) |
205 | + requested = MAX_READ_WRITE; |
206 | + |
207 | + urb.type = urbtype; |
208 | + urb.endpoint = ep; |
209 | + urb.flags = 0; |
210 | + urb.buffer = bytes + bytesdone; |
211 | + urb.buffer_length = requested; |
212 | + urb.signr = 0; |
213 | + urb.actual_length = 0; |
214 | + urb.number_of_packets = 0; /* don't do isochronous yet */ |
215 | + urb.usercontext = NULL; |
216 | + |
217 | + ret = ioctl(dev->fd, IOCTL_USB_SUBMITURB, &urb); |
218 | + if (ret < 0) { |
219 | + USB_ERROR_STR(-errno, "error submitting URB: %s", strerror(errno)); |
220 | + return ret; |
221 | + } |
222 | + |
223 | +restart: |
224 | + context = NULL; |
225 | + while (!urb.usercontext && ((ret = ioctl(dev->fd, IOCTL_USB_REAPURBNDELAY, &context)) == -1) && waiting) { |
226 | + if (ret == -1) |
227 | + { |
228 | + if (errno == ENODEV) |
229 | + { |
230 | + return -ENODEV; |
231 | + } |
232 | + } |
233 | + tv.tv_sec = 0; |
234 | + tv.tv_usec = 1000; // 1 msec |
235 | + FD_ZERO(&writefds); |
236 | + FD_SET(dev->fd, &writefds); |
237 | + select(dev->fd + 1, NULL, &writefds, NULL, &tv); //sub second wait |
238 | + |
239 | + if (timeout) { |
240 | + /* compare with actual time, as the select timeout is not that precise */ |
241 | + gettimeofday(&tv_now, NULL); |
242 | + |
243 | + if ((tv_now.tv_sec > tv_ref.tv_sec) || |
244 | + ((tv_now.tv_sec == tv_ref.tv_sec) && (tv_now.tv_usec >= tv_ref.tv_usec))) |
245 | + waiting = 0; |
246 | + } |
247 | + } |
248 | + |
249 | + if (context && context != &urb) { |
250 | + context->usercontext = URB_USERCONTEXT_COOKIE; |
251 | + /* We need to restart since we got a successful URB, but not ours */ |
252 | + waiting = 1; |
253 | + goto restart; |
254 | + } |
255 | + |
256 | + /* |
257 | + * If there was an error, that wasn't EAGAIN (no completion), then |
258 | + * something happened during the reaping and we should return that |
259 | + * error now |
260 | + */ |
261 | + if (ret < 0 && !urb.usercontext && errno != EAGAIN) |
262 | + USB_ERROR_STR(-errno, "error reaping URB: %s", strerror(errno)); |
263 | + |
264 | + bytesdone += urb.actual_length; |
265 | + } while ((ret == 0 || urb.usercontext) && bytesdone < size && urb.actual_length == requested && waiting); |
266 | + |
267 | + /* If the URB didn't complete in success or error, then let's unlink it */ |
268 | + if ((ret < 0 && !urb.usercontext) || (!waiting && bytesdone < size)) { |
269 | + int rc; |
270 | + |
271 | + if (!waiting) |
272 | + rc = -ETIMEDOUT; |
273 | + else |
274 | + rc = urb.status; |
275 | + |
276 | + ret = ioctl(dev->fd, IOCTL_USB_DISCARDURB, &urb); |
277 | + if (ret < 0 && errno != EINVAL && usb_debug >= 1) |
278 | + fprintf(stderr, "error discarding URB: %s", strerror(errno)); |
279 | + |
280 | + /* |
281 | + * When the URB is unlinked, it gets moved to the completed list and |
282 | + * then we need to reap it or else the next time we call this function, |
283 | + * we'll get the previous completion and exit early |
284 | + */ |
285 | + ioctl(dev->fd, IOCTL_USB_REAPURB, &context); |
286 | + |
287 | + return rc; |
288 | + } |
289 | + |
290 | + return bytesdone; |
291 | +} |
292 | + |
293 | +int usb_bulk_write(usb_dev_handle *dev, int ep, const char *bytes, int size, |
294 | + int timeout) |
295 | +{ |
296 | + /* Ensure the endpoint address is correct */ |
297 | + return usb_urb_transfer(dev, ep, USB_URB_TYPE_BULK, (char *)bytes, size, |
298 | + timeout); |
299 | +} |
300 | + |
301 | +int usb_bulk_read(usb_dev_handle *dev, int ep, char *bytes, int size, |
302 | + int timeout) |
303 | +{ |
304 | + /* Ensure the endpoint address is correct */ |
305 | + ep |= USB_ENDPOINT_IN; |
306 | + return usb_urb_transfer(dev, ep, USB_URB_TYPE_BULK, bytes, size, |
307 | + timeout); |
308 | +} |
309 | + |
310 | +/* |
311 | + * FIXME: Packetize large buffers here. 2.4 HCDs (atleast, haven't checked |
312 | + * 2.5 HCDs yet) don't handle multi-packet Interrupt transfers. So we need |
313 | + * to lookup the endpoint packet size and packetize appropriately here. |
314 | + */ |
315 | +int usb_interrupt_write(usb_dev_handle *dev, int ep, const char *bytes, int size, |
316 | + int timeout) |
317 | +{ |
318 | + /* Ensure the endpoint address is correct */ |
319 | + return usb_urb_transfer(dev, ep, USB_URB_TYPE_INTERRUPT, (char *)bytes, size, |
320 | + timeout); |
321 | +} |
322 | + |
323 | +int usb_interrupt_read(usb_dev_handle *dev, int ep, char *bytes, int size, |
324 | + int timeout) |
325 | +{ |
326 | + /* Ensure the endpoint address is correct */ |
327 | + ep |= USB_ENDPOINT_IN; |
328 | + return usb_urb_transfer(dev, ep, USB_URB_TYPE_INTERRUPT, bytes, size, |
329 | + timeout); |
330 | +} |
331 | + |
332 | +int usb_os_find_busses(struct usb_bus **busses) |
333 | +{ |
334 | + struct usb_bus *fbus = NULL; |
335 | + DIR *dir; |
336 | + struct dirent *entry; |
337 | + |
338 | + dir = opendir(usb_path); |
339 | + if (!dir) |
340 | + USB_ERROR_STR(-errno, "couldn't opendir(%s): %s", usb_path, |
341 | + strerror(errno)); |
342 | + |
343 | + while ((entry = readdir(dir)) != NULL) { |
344 | + struct usb_bus *bus; |
345 | + |
346 | + /* Skip anything starting with a . */ |
347 | + if (entry->d_name[0] == '.') |
348 | + continue; |
349 | + |
350 | + if (!strchr("0123456789", entry->d_name[strlen(entry->d_name) - 1])) { |
351 | + if (usb_debug >= 2) |
352 | + fprintf(stderr, "usb_os_find_busses: Skipping non bus directory %s\n", |
353 | + entry->d_name); |
354 | + continue; |
355 | + } |
356 | + |
357 | + bus = malloc(sizeof(*bus)); |
358 | + if (!bus) |
359 | + USB_ERROR(-ENOMEM); |
360 | + |
361 | + memset((void *)bus, 0, sizeof(*bus)); |
362 | + |
363 | + strncpy(bus->dirname, entry->d_name, sizeof(bus->dirname) - 1); |
364 | + bus->dirname[sizeof(bus->dirname) - 1] = 0; |
365 | + bus->location = atoi(bus->dirname); |
366 | + |
367 | + LIST_ADD(fbus, bus); |
368 | + |
369 | + if (usb_debug >= 2) |
370 | + fprintf(stderr, "usb_os_find_busses: Found %s\n", bus->dirname); |
371 | + } |
372 | + |
373 | + closedir(dir); |
374 | + |
375 | + *busses = fbus; |
376 | + |
377 | + return 0; |
378 | +} |
379 | + |
380 | +int usb_os_find_devices(struct usb_bus *bus, struct usb_device **devices) |
381 | +{ |
382 | + struct usb_device *fdev = NULL; |
383 | + DIR *dir; |
384 | + struct dirent *entry; |
385 | + char dirpath[PATH_MAX + 1]; |
386 | + |
387 | + snprintf(dirpath, PATH_MAX, "%s/%s", usb_path, bus->dirname); |
388 | + |
389 | + dir = opendir(dirpath); |
390 | + if (!dir) |
391 | + USB_ERROR_STR(-errno, "couldn't opendir(%s): %s", dirpath, |
392 | + strerror(errno)); |
393 | + |
394 | + while ((entry = readdir(dir)) != NULL) { |
395 | + unsigned char device_desc[DEVICE_DESC_LENGTH]; |
396 | + char filename[PATH_MAX + 1]; |
397 | + struct usb_device *dev; |
398 | + struct usb_connectinfo connectinfo; |
399 | + int i, fd, ret; |
400 | + |
401 | + /* Skip anything starting with a . */ |
402 | + if (entry->d_name[0] == '.') |
403 | + continue; |
404 | + |
405 | + dev = malloc(sizeof(*dev)); |
406 | + if (!dev) |
407 | + USB_ERROR(-ENOMEM); |
408 | + |
409 | + memset((void *)dev, 0, sizeof(*dev)); |
410 | + |
411 | + dev->bus = bus; |
412 | + |
413 | + strncpy(dev->filename, entry->d_name, sizeof(dev->filename) - 1); |
414 | + dev->filename[sizeof(dev->filename) - 1] = 0; |
415 | + |
416 | + snprintf(filename, sizeof(filename) - 1, "%s/%s", dirpath, entry->d_name); |
417 | + fd = open(filename, O_RDWR); |
418 | + if (fd < 0) { |
419 | + fd = open(filename, O_RDONLY); |
420 | + if (fd < 0) { |
421 | + if (usb_debug >= 2) |
422 | + fprintf(stderr, "usb_os_find_devices: Couldn't open %s\n", |
423 | + filename); |
424 | + |
425 | + free(dev); |
426 | + continue; |
427 | + } |
428 | + } |
429 | + |
430 | + /* Get the device number */ |
431 | + ret = ioctl(fd, IOCTL_USB_CONNECTINFO, &connectinfo); |
432 | + if (ret < 0) { |
433 | + if (usb_debug) |
434 | + fprintf(stderr, "usb_os_find_devices: couldn't get connect info\n"); |
435 | + } else |
436 | + dev->devnum = connectinfo.devnum; |
437 | + |
438 | + ret = read(fd, (void *)device_desc, DEVICE_DESC_LENGTH); |
439 | + if (ret < 0) { |
440 | + if (usb_debug) |
441 | + fprintf(stderr, "usb_os_find_devices: Couldn't read descriptor\n"); |
442 | + |
443 | + free(dev); |
444 | + |
445 | + goto err; |
446 | + } |
447 | + |
448 | + /* |
449 | + * Linux kernel converts the words in this descriptor to CPU endian, so |
450 | + * we use the undocumented W character for usb_parse_descriptor() that |
451 | + * doesn't convert endianess when parsing the descriptor |
452 | + */ |
453 | + usb_parse_descriptor(device_desc, "bbWbbbbWWWbbbb", &dev->descriptor); |
454 | + |
455 | + LIST_ADD(fdev, dev); |
456 | + |
457 | + if (usb_debug >= 2) |
458 | + fprintf(stderr, "usb_os_find_devices: Found %s on %s\n", |
459 | + dev->filename, bus->dirname); |
460 | + |
461 | + /* Now try to fetch the rest of the descriptors */ |
462 | + if (dev->descriptor.bNumConfigurations > USB_MAXCONFIG) |
463 | + /* Silent since we'll try again later */ |
464 | + goto err; |
465 | + |
466 | + if (dev->descriptor.bNumConfigurations < 1) |
467 | + /* Silent since we'll try again later */ |
468 | + goto err; |
469 | + |
470 | + dev->config = (struct usb_config_descriptor *)malloc(dev->descriptor.bNumConfigurations * sizeof(struct usb_config_descriptor)); |
471 | + if (!dev->config) |
472 | + /* Silent since we'll try again later */ |
473 | + goto err; |
474 | + |
475 | + memset(dev->config, 0, dev->descriptor.bNumConfigurations * |
476 | + sizeof(struct usb_config_descriptor)); |
477 | + |
478 | + for (i = 0; i < dev->descriptor.bNumConfigurations; i++) { |
479 | + unsigned char buffer[8], *bigbuffer; |
480 | + struct usb_config_descriptor config; |
481 | + |
482 | + /* Get the first 8 bytes so we can figure out what the total length is */ |
483 | + ret = read(fd, (void *)buffer, 8); |
484 | + if (ret < 8) { |
485 | + if (usb_debug >= 1) { |
486 | + if (ret < 0) |
487 | + fprintf(stderr, "Unable to get descriptor (%d)\n", ret); |
488 | + else |
489 | + fprintf(stderr, "Config descriptor too short (expected %d, got %d)\n", 8, ret); |
490 | + } |
491 | + |
492 | + goto err; |
493 | + } |
494 | + |
495 | + usb_parse_descriptor(buffer, "bbw", &config); |
496 | + |
497 | + bigbuffer = malloc(config.wTotalLength); |
498 | + if (!bigbuffer) { |
499 | + if (usb_debug >= 1) |
500 | + fprintf(stderr, "Unable to allocate memory for descriptors\n"); |
501 | + goto err; |
502 | + } |
503 | + |
504 | + /* Read the rest of the config descriptor */ |
505 | + memcpy(bigbuffer, buffer, 8); |
506 | + |
507 | + ret = read(fd, (void *)(bigbuffer + 8), config.wTotalLength - 8); |
508 | + if (ret < config.wTotalLength - 8) { |
509 | + if (usb_debug >= 1) { |
510 | + if (ret < 0) |
511 | + fprintf(stderr, "Unable to get descriptor (%d)\n", ret); |
512 | + else |
513 | + fprintf(stderr, "Config descriptor too short (expected %d, got %d)\n", config.wTotalLength, ret); |
514 | + } |
515 | + |
516 | + free(bigbuffer); |
517 | + goto err; |
518 | + } |
519 | + |
520 | + ret = usb_parse_configuration(&dev->config[i], bigbuffer); |
521 | + if (usb_debug >= 2) { |
522 | + if (ret > 0) |
523 | + fprintf(stderr, "Descriptor data still left\n"); |
524 | + else if (ret < 0) |
525 | + fprintf(stderr, "Unable to parse descriptors\n"); |
526 | + } |
527 | + |
528 | + free(bigbuffer); |
529 | + } |
530 | + |
531 | +err: |
532 | + close(fd); |
533 | + } |
534 | + |
535 | + closedir(dir); |
536 | + |
537 | + *devices = fdev; |
538 | + |
539 | + return 0; |
540 | +} |
541 | + |
542 | +int usb_os_determine_children(struct usb_bus *bus) |
543 | +{ |
544 | + struct usb_device *dev, *devices[256]; |
545 | + struct usb_ioctl command; |
546 | + int ret, i, i1; |
547 | + |
548 | + /* Create a list of devices first */ |
549 | + memset(devices, 0, sizeof(devices)); |
550 | + for (dev = bus->devices; dev; dev = dev->next) |
551 | + if (dev->devnum) |
552 | + devices[dev->devnum] = dev; |
553 | + |
554 | + /* Now fetch the children for each device */ |
555 | + for (dev = bus->devices; dev; dev = dev->next) { |
556 | + struct usb_hub_portinfo portinfo; |
557 | + int fd; |
558 | + |
559 | + fd = device_open(dev); |
560 | + if (fd < 0) |
561 | + continue; |
562 | + |
563 | + /* Query the hub driver for the children of this device */ |
564 | + if (dev->config && dev->config->interface && dev->config->interface->altsetting) |
565 | + command.ifno = dev->config->interface->altsetting->bInterfaceNumber; |
566 | + else |
567 | + command.ifno = 0; |
568 | + command.ioctl_code = IOCTL_USB_HUB_PORTINFO; |
569 | + command.data = &portinfo; |
570 | + ret = ioctl(fd, IOCTL_USB_IOCTL, &command); |
571 | + if (ret < 0) { |
572 | + /* errno == ENOSYS means the device probably wasn't a hub */ |
573 | + if (errno != ENOSYS && usb_debug > 1) |
574 | + fprintf(stderr, "error obtaining child information: %s\n", |
575 | + strerror(errno)); |
576 | + |
577 | + close(fd); |
578 | + continue; |
579 | + } |
580 | + |
581 | + dev->num_children = 0; |
582 | + for (i = 0; i < portinfo.numports; i++) |
583 | + if (portinfo.port[i]) |
584 | + dev->num_children++; |
585 | + |
586 | + /* Free any old children first */ |
587 | + free(dev->children); |
588 | + |
589 | + dev->children = malloc(sizeof(struct usb_device *) * dev->num_children); |
590 | + if (!dev->children) { |
591 | + if (usb_debug > 1) |
592 | + fprintf(stderr, "error allocating %zu bytes memory for dev->children\n", |
593 | + sizeof(struct usb_device *) * dev->num_children); |
594 | + |
595 | + dev->num_children = 0; |
596 | + close(fd); |
597 | + continue; |
598 | + } |
599 | + |
600 | + for (i = 0, i1 = 0; i < portinfo.numports; i++) { |
601 | + if (!portinfo.port[i]) |
602 | + continue; |
603 | + |
604 | + dev->children[i1++] = devices[portinfo.port[i]]; |
605 | + |
606 | + devices[portinfo.port[i]] = NULL; |
607 | + } |
608 | + |
609 | + close(fd); |
610 | + } |
611 | + |
612 | + /* |
613 | + * There should be one device left in the devices list and that should be |
614 | + * the root device |
615 | + */ |
616 | + for (i = 0; i < sizeof(devices) / sizeof(devices[0]); i++) { |
617 | + if (devices[i]) |
618 | + bus->root_dev = devices[i]; |
619 | + } |
620 | + |
621 | + return 0; |
622 | +} |
623 | + |
624 | +static int check_usb_vfs(const char *dirname) |
625 | +{ |
626 | + DIR *dir; |
627 | + struct dirent *entry; |
628 | + int found = 0; |
629 | + |
630 | + dir = opendir(dirname); |
631 | + if (!dir) |
632 | + return 0; |
633 | + |
634 | + while ((entry = readdir(dir)) != NULL) { |
635 | + /* Skip anything starting with a . */ |
636 | + if (entry->d_name[0] == '.') |
637 | + continue; |
638 | + |
639 | + /* We assume if we find any files that it must be the right place */ |
640 | + found = 1; |
641 | + break; |
642 | + } |
643 | + |
644 | + closedir(dir); |
645 | + |
646 | + return found; |
647 | +} |
648 | + |
649 | +void usb_os_init(void) |
650 | +{ |
651 | + /* Find the path to the virtual filesystem */ |
652 | + if (getenv("USB_DEVFS_PATH")) { |
653 | + if (check_usb_vfs(getenv("USB_DEVFS_PATH"))) { |
654 | + strncpy(usb_path, getenv("USB_DEVFS_PATH"), sizeof(usb_path) - 1); |
655 | + usb_path[sizeof(usb_path) - 1] = 0; |
656 | + } else if (usb_debug) |
657 | + fprintf(stderr, "usb_os_init: couldn't find USB VFS in USB_DEVFS_PATH\n"); |
658 | + } |
659 | + |
660 | + if (!usb_path[0]) { |
661 | + if (check_usb_vfs("/dev/bus/usb")) { |
662 | + strncpy(usb_path, "/dev/bus/usb", sizeof(usb_path) - 1); |
663 | + usb_path[sizeof(usb_path) - 1] = 0; |
664 | + } else if (check_usb_vfs("/proc/bus/usb")) { |
665 | + strncpy(usb_path, "/proc/bus/usb", sizeof(usb_path) - 1); |
666 | + usb_path[sizeof(usb_path) - 1] = 0; |
667 | + } else |
668 | + usb_path[0] = 0; /* No path, no USB support */ |
669 | + } |
670 | + |
671 | + if (usb_debug) { |
672 | + if (usb_path[0]) |
673 | + fprintf(stderr, "usb_os_init: Found USB VFS at %s\n", usb_path); |
674 | + else |
675 | + fprintf(stderr, "usb_os_init: No USB VFS found, is it mounted?\n"); |
676 | + } |
677 | +} |
678 | + |
679 | +int usb_resetep(usb_dev_handle *dev, unsigned int ep) |
680 | +{ |
681 | + int ret; |
682 | + |
683 | + ret = ioctl(dev->fd, IOCTL_USB_RESETEP, &ep); |
684 | + if (ret) |
685 | + USB_ERROR_STR(-errno, "could not reset ep %d: %s", ep, |
686 | + strerror(errno)); |
687 | + |
688 | + return 0; |
689 | +} |
690 | + |
691 | +int usb_clear_halt(usb_dev_handle *dev, unsigned int ep) |
692 | +{ |
693 | + int ret; |
694 | + |
695 | + ret = ioctl(dev->fd, IOCTL_USB_CLEAR_HALT, &ep); |
696 | + if (ret) |
697 | + USB_ERROR_STR(-errno, "could not clear/halt ep %d: %s", ep, |
698 | + strerror(errno)); |
699 | + |
700 | + return 0; |
701 | +} |
702 | + |
703 | +int usb_reset(usb_dev_handle *dev) |
704 | +{ |
705 | + int ret; |
706 | + |
707 | + ret = ioctl(dev->fd, IOCTL_USB_RESET, NULL); |
708 | + if (ret) |
709 | + USB_ERROR_STR(-errno, "could not reset: %s", strerror(errno)); |
710 | + |
711 | + return 0; |
712 | +} |
713 | + |
714 | +int usb_get_driver_np(usb_dev_handle *dev, int interface, char *name, |
715 | + unsigned int namelen) |
716 | +{ |
717 | + struct usb_getdriver getdrv; |
718 | + int ret; |
719 | + |
720 | + getdrv.interface = interface; |
721 | + ret = ioctl(dev->fd, IOCTL_USB_GETDRIVER, &getdrv); |
722 | + if (ret) |
723 | + USB_ERROR_STR(-errno, "could not get bound driver: %s", strerror(errno)); |
724 | + |
725 | + strncpy(name, getdrv.driver, namelen - 1); |
726 | + name[namelen - 1] = 0; |
727 | + |
728 | + return 0; |
729 | +} |
730 | + |
731 | +int usb_detach_kernel_driver_np(usb_dev_handle *dev, int interface) |
732 | +{ |
733 | + struct usb_ioctl command; |
734 | + int ret; |
735 | + |
736 | + command.ifno = interface; |
737 | + command.ioctl_code = IOCTL_USB_DISCONNECT; |
738 | + command.data = NULL; |
739 | + |
740 | + ret = ioctl(dev->fd, IOCTL_USB_IOCTL, &command); |
741 | + if (ret) |
742 | + USB_ERROR_STR(-errno, "could not detach kernel driver from interface %d: %s", |
743 | + interface, strerror(errno)); |
744 | + |
745 | + return 0; |
746 | +} |
747 | + |
748 | |
749 | === modified file '.pc/applied-patches' |
750 | --- .pc/applied-patches 2014-07-03 20:17:03 +0000 |
751 | +++ .pc/applied-patches 2015-05-21 08:58:22 +0000 |
752 | @@ -10,4 +10,5 @@ |
753 | 09_dummy.diff |
754 | 10_hurd.diff |
755 | 11_transfer_timeout.diff |
756 | +12_hang_after_resume.diff |
757 | 91_ac_prog_cxx.diff |
758 | |
759 | === modified file 'debian/changelog' |
760 | --- debian/changelog 2014-10-21 20:43:38 +0000 |
761 | +++ debian/changelog 2015-05-21 08:58:22 +0000 |
762 | @@ -1,3 +1,10 @@ |
763 | +libusb (2:0.1.12-25ppa1) wily; urgency=medium |
764 | + |
765 | + * Added debian/patches/12_hang_after_resume.diff to fix occasional hang |
766 | + after suspend/resume. (LP: #1455924) |
767 | + |
768 | + -- Alexander Ponyatykh <lazyranma@gmail.com> Sun, 17 May 2015 19:32:24 +0300 |
769 | + |
770 | libusb (2:0.1.12-25) unstable; urgency=medium |
771 | |
772 | * Move /usr/lib/$multiarch/libusb-0.1.so.4 from libusb-0.1-4 to |
773 | |
774 | === added file 'debian/patches/12_hang_after_resume.diff' |
775 | --- debian/patches/12_hang_after_resume.diff 1970-01-01 00:00:00 +0000 |
776 | +++ debian/patches/12_hang_after_resume.diff 2015-05-21 08:58:22 +0000 |
777 | @@ -0,0 +1,47 @@ |
778 | +Description: Fix occasional hang after suspend/resume. |
779 | + After suspend/resume linux kernel may loose our requests, and |
780 | + usb_urb_transfer() will wait forever for lost requests to complete. With this |
781 | + patch lost request are properly handled. |
782 | + Also fixes race condition leading to stack corruption. |
783 | +Author: Alexander Ponyatykh <lazyranma@gmail.com> |
784 | +Bug-Ubuntu: https://bugs.launchpad.net/ubuntu/+source/libusb/+bug/1455924 |
785 | +Forwarded: no |
786 | +Last-Update: 2015-05-17 |
787 | +=================================================================== |
788 | +--- a/linux.c |
789 | ++++ b/linux.c |
790 | +@@ -166,7 +166,7 @@ |
791 | + int bytesdone = 0, requested; |
792 | + struct timeval tv, tv_ref, tv_now; |
793 | + struct usb_urb *context; |
794 | +- int ret, waiting; |
795 | ++ int ret, waiting, our_urb_dequeued; |
796 | + |
797 | + /* |
798 | + * HACK: The use of urb.usercontext is a hack to get threaded applications |
799 | +@@ -277,7 +277,24 @@ |
800 | + * then we need to reap it or else the next time we call this function, |
801 | + * we'll get the previous completion and exit early |
802 | + */ |
803 | +- ioctl(dev->fd, IOCTL_USB_REAPURB, &context); |
804 | ++ our_urb_dequeued = 0; |
805 | ++ do { |
806 | ++ context = NULL; |
807 | ++ ret = ioctl(dev->fd, IOCTL_USB_REAPURBNDELAY, &context); |
808 | ++ if (context) { |
809 | ++ if (context == &urb) { |
810 | ++ our_urb_dequeued = 1; |
811 | ++ } else { |
812 | ++ /* We need to retry since we got completed URB, but not ours. |
813 | ++ * Otherwise other thread may write to our already deallocated |
814 | ++ * urb.usercontext */ |
815 | ++ context->usercontext = URB_USERCONTEXT_COOKIE; |
816 | ++ } |
817 | ++ } |
818 | ++ } while (context && !our_urb_dequeued); |
819 | ++ |
820 | ++ if (usb_debug > 0 && !our_urb_dequeued && !urb.usercontext) |
821 | ++ fprintf(stderr, "usb_urb_transfer: kernel may have lost our URB! (or other thread dequeued it)\n"); |
822 | + |
823 | + return rc; |
824 | + } |
825 | |
826 | === modified file 'debian/patches/series' |
827 | --- debian/patches/series 2014-07-03 20:17:03 +0000 |
828 | +++ debian/patches/series 2015-05-21 08:58:22 +0000 |
829 | @@ -10,4 +10,5 @@ |
830 | 09_dummy.diff |
831 | 10_hurd.diff |
832 | 11_transfer_timeout.diff |
833 | +12_hang_after_resume.diff |
834 | 91_ac_prog_cxx.diff |
835 | |
836 | === modified file 'linux.c' |
837 | --- linux.c 2014-07-03 20:17:03 +0000 |
838 | +++ linux.c 2015-05-21 08:58:22 +0000 |
839 | @@ -166,7 +166,7 @@ |
840 | int bytesdone = 0, requested; |
841 | struct timeval tv, tv_ref, tv_now; |
842 | struct usb_urb *context; |
843 | - int ret, waiting; |
844 | + int ret, waiting, our_urb_dequeued; |
845 | |
846 | /* |
847 | * HACK: The use of urb.usercontext is a hack to get threaded applications |
848 | @@ -277,7 +277,24 @@ |
849 | * then we need to reap it or else the next time we call this function, |
850 | * we'll get the previous completion and exit early |
851 | */ |
852 | - ioctl(dev->fd, IOCTL_USB_REAPURB, &context); |
853 | + our_urb_dequeued = 0; |
854 | + do { |
855 | + context = NULL; |
856 | + ret = ioctl(dev->fd, IOCTL_USB_REAPURBNDELAY, &context); |
857 | + if (context) { |
858 | + if (context == &urb) { |
859 | + our_urb_dequeued = 1; |
860 | + } else { |
861 | + /* We need to retry since we got completed URB, but not ours. |
862 | + * Otherwise other thread may write to our already deallocated |
863 | + * urb.usercontext */ |
864 | + context->usercontext = URB_USERCONTEXT_COOKIE; |
865 | + } |
866 | + } |
867 | + } while (context && !our_urb_dequeued); |
868 | + |
869 | + if (usb_debug > 0 && !our_urb_dequeued && !urb.usercontext) |
870 | + fprintf(stderr, "usb_urb_transfer: kernel may have lost our URB! (or other thread dequeued it)\n"); |
871 | |
872 | return rc; |
873 | } |
Thanks -- could you please ask upstream to review? Maybe Debian instead of upstream as it seems our version might be a bit behind them (don't see linux.c there).
See https:/ /wiki.ubuntu. com/Debian/ ForUbuntuDevelo pers#Forwarding _bug_reports for information (use reportbug -B debian in Ubuntu) :)