Merge lp:~lazyranma/ubuntu/wily/libusb/bug-1455924 into lp:ubuntu/wily/libusb

Proposed by Alexander Ponyatykh
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
Reviewer Review Type Date Requested Status
Steve Langasek Disapprove
Review via email: mp+259330@code.launchpad.net

Description of the change

Fixes LP: #1455924

To post a comment you must log in.
21. By Alexander Ponyatykh

Changed version to ppa1

Revision history for this message
Iain Lane (laney) wrote :

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/ForUbuntuDevelopers#Forwarding_bug_reports for information (use reportbug -B debian in Ubuntu) :)

Revision history for this message
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://www.libusb.org/wiki/libusb-compat-0.1 (LP: #991004). I've already reported this bug to Debian (https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=785578).
Debian uses the same version of the package (https://tracker.debian.org/pkg/libusb). The linux.c you are talking about is probably from the .pc directory, which is quilt's working directory.

Revision history for this message
Ɓ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.

Revision history for this message
Alexander Ponyatykh (lazyranma) wrote :

I've made a program that reproduces this problem: http://pastebin.com/y8fGAmwh (based on source code from this bug report: http://www.spinics.net/lists/linux-usb/msg15688.html).
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.

Revision history for this message
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-dependencies of libusb to libusb-1.0 in Ubuntu;
 - working with the Debian maintainer (who maintains both libusb and libusb-1.0) to replace libusb-0.1 with libusb-compat-0.1.

review: Disapprove

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

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== added directory '.pc/12_hang_after_resume.diff'
=== added file '.pc/12_hang_after_resume.diff/linux.c'
--- .pc/12_hang_after_resume.diff/linux.c 1970-01-01 00:00:00 +0000
+++ .pc/12_hang_after_resume.diff/linux.c 2015-05-21 08:58:22 +0000
@@ -0,0 +1,742 @@
1/*
2 * Linux USB support
3 *
4 * Copyright (c) 2000-2003 Johannes Erdfelt <johannes@erdfelt.com>
5 *
6 * This library is covered by the LGPL, read LICENSE for details.
7 */
8
9#include <stdlib.h> /* getenv, etc */
10#include <unistd.h>
11#include <string.h>
12#include <stdio.h>
13#include <fcntl.h>
14#include <errno.h>
15#include <sys/time.h>
16#include <dirent.h>
17
18#include "linux.h"
19#include "usbi.h"
20
21static char usb_path[PATH_MAX + 1] = "";
22
23static int device_open(struct usb_device *dev)
24{
25 char filename[PATH_MAX + 1];
26 int fd;
27
28 snprintf(filename, sizeof(filename) - 1, "%s/%s/%s",
29 usb_path, dev->bus->dirname, dev->filename);
30
31 fd = open(filename, O_RDWR);
32 if (fd < 0) {
33 fd = open(filename, O_RDONLY);
34 if (fd < 0)
35 USB_ERROR_STR(-errno, "failed to open %s: %s",
36 filename, strerror(errno));
37 }
38
39 return fd;
40}
41
42int usb_os_open(usb_dev_handle *dev)
43{
44 dev->fd = device_open(dev->device);
45
46 return 0;
47}
48
49int usb_os_close(usb_dev_handle *dev)
50{
51 if (dev->fd < 0)
52 return 0;
53
54 if (close(dev->fd) == -1)
55 /* Failing trying to close a file really isn't an error, so return 0 */
56 USB_ERROR_STR(0, "tried to close device fd %d: %s", dev->fd,
57 strerror(errno));
58
59 return 0;
60}
61
62int usb_set_configuration(usb_dev_handle *dev, int configuration)
63{
64 int ret;
65
66 ret = ioctl(dev->fd, IOCTL_USB_SETCONFIG, &configuration);
67 if (ret < 0)
68 USB_ERROR_STR(-errno, "could not set config %d: %s", configuration,
69 strerror(errno));
70
71 dev->config = configuration;
72
73 return 0;
74}
75
76int usb_claim_interface(usb_dev_handle *dev, int interface)
77{
78 int ret;
79
80 ret = ioctl(dev->fd, IOCTL_USB_CLAIMINTF, &interface);
81 if (ret < 0) {
82 if (errno == EBUSY && usb_debug > 0)
83 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);
84
85 USB_ERROR_STR(-errno, "could not claim interface %d: %s", interface,
86 strerror(errno));
87 }
88
89 dev->interface = interface;
90
91 return 0;
92}
93
94int usb_release_interface(usb_dev_handle *dev, int interface)
95{
96 int ret;
97
98 ret = ioctl(dev->fd, IOCTL_USB_RELEASEINTF, &interface);
99 if (ret < 0)
100 USB_ERROR_STR(-errno, "could not release intf %d: %s", interface,
101 strerror(errno));
102
103 dev->interface = -1;
104
105 return 0;
106}
107
108int usb_set_altinterface(usb_dev_handle *dev, int alternate)
109{
110 int ret;
111 struct usb_setinterface setintf;
112
113 if (dev->interface < 0)
114 USB_ERROR(-EINVAL);
115
116 setintf.interface = dev->interface;
117 setintf.altsetting = alternate;
118
119 ret = ioctl(dev->fd, IOCTL_USB_SETINTF, &setintf);
120 if (ret < 0)
121 USB_ERROR_STR(-errno, "could not set alt intf %d/%d: %s",
122 dev->interface, alternate, strerror(errno));
123
124 dev->altsetting = alternate;
125
126 return 0;
127}
128
129/*
130 * Linux usbfs has a limit of one page size for synchronous bulk read/write.
131 * 4096 is the most portable maximum we can do for now.
132 * Linux usbfs has a limit of 16KB for the URB interface. We use this now
133 * to get better performance for USB 2.0 devices.
134 */
135#define MAX_READ_WRITE (16 * 1024)
136
137int usb_control_msg(usb_dev_handle *dev, int requesttype, int request,
138 int value, int index, char *bytes, int size, int timeout)
139{
140 struct usb_ctrltransfer ctrl;
141 int ret;
142
143 ctrl.bRequestType = requesttype;
144 ctrl.bRequest = request;
145 ctrl.wValue = value;
146 ctrl.wIndex = index;
147 ctrl.wLength = size;
148
149 ctrl.data = bytes;
150 ctrl.timeout = timeout;
151
152 ret = ioctl(dev->fd, IOCTL_USB_CONTROL, &ctrl);
153 if (ret < 0)
154 USB_ERROR_STR(-errno, "error sending control message: %s", strerror(errno));
155
156 return ret;
157}
158
159#define URB_USERCONTEXT_COOKIE ((void *)0x1)
160
161/* Reading and writing are the same except for the endpoint */
162static int usb_urb_transfer(usb_dev_handle *dev, int ep, int urbtype,
163 char *bytes, int size, int timeout)
164{
165 struct usb_urb urb;
166 int bytesdone = 0, requested;
167 struct timeval tv, tv_ref, tv_now;
168 struct usb_urb *context;
169 int ret, waiting;
170
171 /*
172 * HACK: The use of urb.usercontext is a hack to get threaded applications
173 * sort of working again. Threaded support is still not recommended, but
174 * this should allow applications to work in the common cases. Basically,
175 * if we get the completion for an URB we're not waiting for, then we update
176 * the usercontext pointer to 1 for the other threads URB and it will see
177 * the change after it wakes up from the the timeout. Ugly, but it works.
178 */
179
180 /*
181 * Get actual time, and add the timeout value. The result is the absolute
182 * time where we have to quit waiting for an message.
183 */
184 gettimeofday(&tv_ref, NULL);
185 tv_ref.tv_sec = tv_ref.tv_sec + timeout / 1000;
186 tv_ref.tv_usec = tv_ref.tv_usec + (timeout % 1000) * 1000;
187
188 if (tv_ref.tv_usec > 1000000) {
189 tv_ref.tv_usec -= 1000000;
190 tv_ref.tv_sec++;
191 }
192
193 waiting = 1;
194
195 do {
196 fd_set writefds;
197
198 requested = size - bytesdone;
199 if (requested > MAX_READ_WRITE)
200 requested = MAX_READ_WRITE;
201
202 urb.type = urbtype;
203 urb.endpoint = ep;
204 urb.flags = 0;
205 urb.buffer = bytes + bytesdone;
206 urb.buffer_length = requested;
207 urb.signr = 0;
208 urb.actual_length = 0;
209 urb.number_of_packets = 0; /* don't do isochronous yet */
210 urb.usercontext = NULL;
211
212 ret = ioctl(dev->fd, IOCTL_USB_SUBMITURB, &urb);
213 if (ret < 0) {
214 USB_ERROR_STR(-errno, "error submitting URB: %s", strerror(errno));
215 return ret;
216 }
217
218restart:
219 context = NULL;
220 while (!urb.usercontext && ((ret = ioctl(dev->fd, IOCTL_USB_REAPURBNDELAY, &context)) == -1) && waiting) {
221 if (ret == -1)
222 {
223 if (errno == ENODEV)
224 {
225 return -ENODEV;
226 }
227 }
228 tv.tv_sec = 0;
229 tv.tv_usec = 1000; // 1 msec
230 FD_ZERO(&writefds);
231 FD_SET(dev->fd, &writefds);
232 select(dev->fd + 1, NULL, &writefds, NULL, &tv); //sub second wait
233
234 if (timeout) {
235 /* compare with actual time, as the select timeout is not that precise */
236 gettimeofday(&tv_now, NULL);
237
238 if ((tv_now.tv_sec > tv_ref.tv_sec) ||
239 ((tv_now.tv_sec == tv_ref.tv_sec) && (tv_now.tv_usec >= tv_ref.tv_usec)))
240 waiting = 0;
241 }
242 }
243
244 if (context && context != &urb) {
245 context->usercontext = URB_USERCONTEXT_COOKIE;
246 /* We need to restart since we got a successful URB, but not ours */
247 waiting = 1;
248 goto restart;
249 }
250
251 /*
252 * If there was an error, that wasn't EAGAIN (no completion), then
253 * something happened during the reaping and we should return that
254 * error now
255 */
256 if (ret < 0 && !urb.usercontext && errno != EAGAIN)
257 USB_ERROR_STR(-errno, "error reaping URB: %s", strerror(errno));
258
259 bytesdone += urb.actual_length;
260 } while ((ret == 0 || urb.usercontext) && bytesdone < size && urb.actual_length == requested && waiting);
261
262 /* If the URB didn't complete in success or error, then let's unlink it */
263 if ((ret < 0 && !urb.usercontext) || (!waiting && bytesdone < size)) {
264 int rc;
265
266 if (!waiting)
267 rc = -ETIMEDOUT;
268 else
269 rc = urb.status;
270
271 ret = ioctl(dev->fd, IOCTL_USB_DISCARDURB, &urb);
272 if (ret < 0 && errno != EINVAL && usb_debug >= 1)
273 fprintf(stderr, "error discarding URB: %s", strerror(errno));
274
275 /*
276 * When the URB is unlinked, it gets moved to the completed list and
277 * then we need to reap it or else the next time we call this function,
278 * we'll get the previous completion and exit early
279 */
280 ioctl(dev->fd, IOCTL_USB_REAPURB, &context);
281
282 return rc;
283 }
284
285 return bytesdone;
286}
287
288int usb_bulk_write(usb_dev_handle *dev, int ep, const char *bytes, int size,
289 int timeout)
290{
291 /* Ensure the endpoint address is correct */
292 return usb_urb_transfer(dev, ep, USB_URB_TYPE_BULK, (char *)bytes, size,
293 timeout);
294}
295
296int usb_bulk_read(usb_dev_handle *dev, int ep, char *bytes, int size,
297 int timeout)
298{
299 /* Ensure the endpoint address is correct */
300 ep |= USB_ENDPOINT_IN;
301 return usb_urb_transfer(dev, ep, USB_URB_TYPE_BULK, bytes, size,
302 timeout);
303}
304
305/*
306 * FIXME: Packetize large buffers here. 2.4 HCDs (atleast, haven't checked
307 * 2.5 HCDs yet) don't handle multi-packet Interrupt transfers. So we need
308 * to lookup the endpoint packet size and packetize appropriately here.
309 */
310int usb_interrupt_write(usb_dev_handle *dev, int ep, const char *bytes, int size,
311 int timeout)
312{
313 /* Ensure the endpoint address is correct */
314 return usb_urb_transfer(dev, ep, USB_URB_TYPE_INTERRUPT, (char *)bytes, size,
315 timeout);
316}
317
318int usb_interrupt_read(usb_dev_handle *dev, int ep, char *bytes, int size,
319 int timeout)
320{
321 /* Ensure the endpoint address is correct */
322 ep |= USB_ENDPOINT_IN;
323 return usb_urb_transfer(dev, ep, USB_URB_TYPE_INTERRUPT, bytes, size,
324 timeout);
325}
326
327int usb_os_find_busses(struct usb_bus **busses)
328{
329 struct usb_bus *fbus = NULL;
330 DIR *dir;
331 struct dirent *entry;
332
333 dir = opendir(usb_path);
334 if (!dir)
335 USB_ERROR_STR(-errno, "couldn't opendir(%s): %s", usb_path,
336 strerror(errno));
337
338 while ((entry = readdir(dir)) != NULL) {
339 struct usb_bus *bus;
340
341 /* Skip anything starting with a . */
342 if (entry->d_name[0] == '.')
343 continue;
344
345 if (!strchr("0123456789", entry->d_name[strlen(entry->d_name) - 1])) {
346 if (usb_debug >= 2)
347 fprintf(stderr, "usb_os_find_busses: Skipping non bus directory %s\n",
348 entry->d_name);
349 continue;
350 }
351
352 bus = malloc(sizeof(*bus));
353 if (!bus)
354 USB_ERROR(-ENOMEM);
355
356 memset((void *)bus, 0, sizeof(*bus));
357
358 strncpy(bus->dirname, entry->d_name, sizeof(bus->dirname) - 1);
359 bus->dirname[sizeof(bus->dirname) - 1] = 0;
360 bus->location = atoi(bus->dirname);
361
362 LIST_ADD(fbus, bus);
363
364 if (usb_debug >= 2)
365 fprintf(stderr, "usb_os_find_busses: Found %s\n", bus->dirname);
366 }
367
368 closedir(dir);
369
370 *busses = fbus;
371
372 return 0;
373}
374
375int usb_os_find_devices(struct usb_bus *bus, struct usb_device **devices)
376{
377 struct usb_device *fdev = NULL;
378 DIR *dir;
379 struct dirent *entry;
380 char dirpath[PATH_MAX + 1];
381
382 snprintf(dirpath, PATH_MAX, "%s/%s", usb_path, bus->dirname);
383
384 dir = opendir(dirpath);
385 if (!dir)
386 USB_ERROR_STR(-errno, "couldn't opendir(%s): %s", dirpath,
387 strerror(errno));
388
389 while ((entry = readdir(dir)) != NULL) {
390 unsigned char device_desc[DEVICE_DESC_LENGTH];
391 char filename[PATH_MAX + 1];
392 struct usb_device *dev;
393 struct usb_connectinfo connectinfo;
394 int i, fd, ret;
395
396 /* Skip anything starting with a . */
397 if (entry->d_name[0] == '.')
398 continue;
399
400 dev = malloc(sizeof(*dev));
401 if (!dev)
402 USB_ERROR(-ENOMEM);
403
404 memset((void *)dev, 0, sizeof(*dev));
405
406 dev->bus = bus;
407
408 strncpy(dev->filename, entry->d_name, sizeof(dev->filename) - 1);
409 dev->filename[sizeof(dev->filename) - 1] = 0;
410
411 snprintf(filename, sizeof(filename) - 1, "%s/%s", dirpath, entry->d_name);
412 fd = open(filename, O_RDWR);
413 if (fd < 0) {
414 fd = open(filename, O_RDONLY);
415 if (fd < 0) {
416 if (usb_debug >= 2)
417 fprintf(stderr, "usb_os_find_devices: Couldn't open %s\n",
418 filename);
419
420 free(dev);
421 continue;
422 }
423 }
424
425 /* Get the device number */
426 ret = ioctl(fd, IOCTL_USB_CONNECTINFO, &connectinfo);
427 if (ret < 0) {
428 if (usb_debug)
429 fprintf(stderr, "usb_os_find_devices: couldn't get connect info\n");
430 } else
431 dev->devnum = connectinfo.devnum;
432
433 ret = read(fd, (void *)device_desc, DEVICE_DESC_LENGTH);
434 if (ret < 0) {
435 if (usb_debug)
436 fprintf(stderr, "usb_os_find_devices: Couldn't read descriptor\n");
437
438 free(dev);
439
440 goto err;
441 }
442
443 /*
444 * Linux kernel converts the words in this descriptor to CPU endian, so
445 * we use the undocumented W character for usb_parse_descriptor() that
446 * doesn't convert endianess when parsing the descriptor
447 */
448 usb_parse_descriptor(device_desc, "bbWbbbbWWWbbbb", &dev->descriptor);
449
450 LIST_ADD(fdev, dev);
451
452 if (usb_debug >= 2)
453 fprintf(stderr, "usb_os_find_devices: Found %s on %s\n",
454 dev->filename, bus->dirname);
455
456 /* Now try to fetch the rest of the descriptors */
457 if (dev->descriptor.bNumConfigurations > USB_MAXCONFIG)
458 /* Silent since we'll try again later */
459 goto err;
460
461 if (dev->descriptor.bNumConfigurations < 1)
462 /* Silent since we'll try again later */
463 goto err;
464
465 dev->config = (struct usb_config_descriptor *)malloc(dev->descriptor.bNumConfigurations * sizeof(struct usb_config_descriptor));
466 if (!dev->config)
467 /* Silent since we'll try again later */
468 goto err;
469
470 memset(dev->config, 0, dev->descriptor.bNumConfigurations *
471 sizeof(struct usb_config_descriptor));
472
473 for (i = 0; i < dev->descriptor.bNumConfigurations; i++) {
474 unsigned char buffer[8], *bigbuffer;
475 struct usb_config_descriptor config;
476
477 /* Get the first 8 bytes so we can figure out what the total length is */
478 ret = read(fd, (void *)buffer, 8);
479 if (ret < 8) {
480 if (usb_debug >= 1) {
481 if (ret < 0)
482 fprintf(stderr, "Unable to get descriptor (%d)\n", ret);
483 else
484 fprintf(stderr, "Config descriptor too short (expected %d, got %d)\n", 8, ret);
485 }
486
487 goto err;
488 }
489
490 usb_parse_descriptor(buffer, "bbw", &config);
491
492 bigbuffer = malloc(config.wTotalLength);
493 if (!bigbuffer) {
494 if (usb_debug >= 1)
495 fprintf(stderr, "Unable to allocate memory for descriptors\n");
496 goto err;
497 }
498
499 /* Read the rest of the config descriptor */
500 memcpy(bigbuffer, buffer, 8);
501
502 ret = read(fd, (void *)(bigbuffer + 8), config.wTotalLength - 8);
503 if (ret < config.wTotalLength - 8) {
504 if (usb_debug >= 1) {
505 if (ret < 0)
506 fprintf(stderr, "Unable to get descriptor (%d)\n", ret);
507 else
508 fprintf(stderr, "Config descriptor too short (expected %d, got %d)\n", config.wTotalLength, ret);
509 }
510
511 free(bigbuffer);
512 goto err;
513 }
514
515 ret = usb_parse_configuration(&dev->config[i], bigbuffer);
516 if (usb_debug >= 2) {
517 if (ret > 0)
518 fprintf(stderr, "Descriptor data still left\n");
519 else if (ret < 0)
520 fprintf(stderr, "Unable to parse descriptors\n");
521 }
522
523 free(bigbuffer);
524 }
525
526err:
527 close(fd);
528 }
529
530 closedir(dir);
531
532 *devices = fdev;
533
534 return 0;
535}
536
537int usb_os_determine_children(struct usb_bus *bus)
538{
539 struct usb_device *dev, *devices[256];
540 struct usb_ioctl command;
541 int ret, i, i1;
542
543 /* Create a list of devices first */
544 memset(devices, 0, sizeof(devices));
545 for (dev = bus->devices; dev; dev = dev->next)
546 if (dev->devnum)
547 devices[dev->devnum] = dev;
548
549 /* Now fetch the children for each device */
550 for (dev = bus->devices; dev; dev = dev->next) {
551 struct usb_hub_portinfo portinfo;
552 int fd;
553
554 fd = device_open(dev);
555 if (fd < 0)
556 continue;
557
558 /* Query the hub driver for the children of this device */
559 if (dev->config && dev->config->interface && dev->config->interface->altsetting)
560 command.ifno = dev->config->interface->altsetting->bInterfaceNumber;
561 else
562 command.ifno = 0;
563 command.ioctl_code = IOCTL_USB_HUB_PORTINFO;
564 command.data = &portinfo;
565 ret = ioctl(fd, IOCTL_USB_IOCTL, &command);
566 if (ret < 0) {
567 /* errno == ENOSYS means the device probably wasn't a hub */
568 if (errno != ENOSYS && usb_debug > 1)
569 fprintf(stderr, "error obtaining child information: %s\n",
570 strerror(errno));
571
572 close(fd);
573 continue;
574 }
575
576 dev->num_children = 0;
577 for (i = 0; i < portinfo.numports; i++)
578 if (portinfo.port[i])
579 dev->num_children++;
580
581 /* Free any old children first */
582 free(dev->children);
583
584 dev->children = malloc(sizeof(struct usb_device *) * dev->num_children);
585 if (!dev->children) {
586 if (usb_debug > 1)
587 fprintf(stderr, "error allocating %zu bytes memory for dev->children\n",
588 sizeof(struct usb_device *) * dev->num_children);
589
590 dev->num_children = 0;
591 close(fd);
592 continue;
593 }
594
595 for (i = 0, i1 = 0; i < portinfo.numports; i++) {
596 if (!portinfo.port[i])
597 continue;
598
599 dev->children[i1++] = devices[portinfo.port[i]];
600
601 devices[portinfo.port[i]] = NULL;
602 }
603
604 close(fd);
605 }
606
607 /*
608 * There should be one device left in the devices list and that should be
609 * the root device
610 */
611 for (i = 0; i < sizeof(devices) / sizeof(devices[0]); i++) {
612 if (devices[i])
613 bus->root_dev = devices[i];
614 }
615
616 return 0;
617}
618
619static int check_usb_vfs(const char *dirname)
620{
621 DIR *dir;
622 struct dirent *entry;
623 int found = 0;
624
625 dir = opendir(dirname);
626 if (!dir)
627 return 0;
628
629 while ((entry = readdir(dir)) != NULL) {
630 /* Skip anything starting with a . */
631 if (entry->d_name[0] == '.')
632 continue;
633
634 /* We assume if we find any files that it must be the right place */
635 found = 1;
636 break;
637 }
638
639 closedir(dir);
640
641 return found;
642}
643
644void usb_os_init(void)
645{
646 /* Find the path to the virtual filesystem */
647 if (getenv("USB_DEVFS_PATH")) {
648 if (check_usb_vfs(getenv("USB_DEVFS_PATH"))) {
649 strncpy(usb_path, getenv("USB_DEVFS_PATH"), sizeof(usb_path) - 1);
650 usb_path[sizeof(usb_path) - 1] = 0;
651 } else if (usb_debug)
652 fprintf(stderr, "usb_os_init: couldn't find USB VFS in USB_DEVFS_PATH\n");
653 }
654
655 if (!usb_path[0]) {
656 if (check_usb_vfs("/dev/bus/usb")) {
657 strncpy(usb_path, "/dev/bus/usb", sizeof(usb_path) - 1);
658 usb_path[sizeof(usb_path) - 1] = 0;
659 } else if (check_usb_vfs("/proc/bus/usb")) {
660 strncpy(usb_path, "/proc/bus/usb", sizeof(usb_path) - 1);
661 usb_path[sizeof(usb_path) - 1] = 0;
662 } else
663 usb_path[0] = 0; /* No path, no USB support */
664 }
665
666 if (usb_debug) {
667 if (usb_path[0])
668 fprintf(stderr, "usb_os_init: Found USB VFS at %s\n", usb_path);
669 else
670 fprintf(stderr, "usb_os_init: No USB VFS found, is it mounted?\n");
671 }
672}
673
674int usb_resetep(usb_dev_handle *dev, unsigned int ep)
675{
676 int ret;
677
678 ret = ioctl(dev->fd, IOCTL_USB_RESETEP, &ep);
679 if (ret)
680 USB_ERROR_STR(-errno, "could not reset ep %d: %s", ep,
681 strerror(errno));
682
683 return 0;
684}
685
686int usb_clear_halt(usb_dev_handle *dev, unsigned int ep)
687{
688 int ret;
689
690 ret = ioctl(dev->fd, IOCTL_USB_CLEAR_HALT, &ep);
691 if (ret)
692 USB_ERROR_STR(-errno, "could not clear/halt ep %d: %s", ep,
693 strerror(errno));
694
695 return 0;
696}
697
698int usb_reset(usb_dev_handle *dev)
699{
700 int ret;
701
702 ret = ioctl(dev->fd, IOCTL_USB_RESET, NULL);
703 if (ret)
704 USB_ERROR_STR(-errno, "could not reset: %s", strerror(errno));
705
706 return 0;
707}
708
709int usb_get_driver_np(usb_dev_handle *dev, int interface, char *name,
710 unsigned int namelen)
711{
712 struct usb_getdriver getdrv;
713 int ret;
714
715 getdrv.interface = interface;
716 ret = ioctl(dev->fd, IOCTL_USB_GETDRIVER, &getdrv);
717 if (ret)
718 USB_ERROR_STR(-errno, "could not get bound driver: %s", strerror(errno));
719
720 strncpy(name, getdrv.driver, namelen - 1);
721 name[namelen - 1] = 0;
722
723 return 0;
724}
725
726int usb_detach_kernel_driver_np(usb_dev_handle *dev, int interface)
727{
728 struct usb_ioctl command;
729 int ret;
730
731 command.ifno = interface;
732 command.ioctl_code = IOCTL_USB_DISCONNECT;
733 command.data = NULL;
734
735 ret = ioctl(dev->fd, IOCTL_USB_IOCTL, &command);
736 if (ret)
737 USB_ERROR_STR(-errno, "could not detach kernel driver from interface %d: %s",
738 interface, strerror(errno));
739
740 return 0;
741}
742
0743
=== modified file '.pc/applied-patches'
--- .pc/applied-patches 2014-07-03 20:17:03 +0000
+++ .pc/applied-patches 2015-05-21 08:58:22 +0000
@@ -10,4 +10,5 @@
1009_dummy.diff1009_dummy.diff
1110_hurd.diff1110_hurd.diff
1211_transfer_timeout.diff1211_transfer_timeout.diff
1312_hang_after_resume.diff
1391_ac_prog_cxx.diff1491_ac_prog_cxx.diff
1415
=== modified file 'debian/changelog'
--- debian/changelog 2014-10-21 20:43:38 +0000
+++ debian/changelog 2015-05-21 08:58:22 +0000
@@ -1,3 +1,10 @@
1libusb (2:0.1.12-25ppa1) wily; urgency=medium
2
3 * Added debian/patches/12_hang_after_resume.diff to fix occasional hang
4 after suspend/resume. (LP: #1455924)
5
6 -- Alexander Ponyatykh <lazyranma@gmail.com> Sun, 17 May 2015 19:32:24 +0300
7
1libusb (2:0.1.12-25) unstable; urgency=medium8libusb (2:0.1.12-25) unstable; urgency=medium
29
3 * Move /usr/lib/$multiarch/libusb-0.1.so.4 from libusb-0.1-4 to10 * Move /usr/lib/$multiarch/libusb-0.1.so.4 from libusb-0.1-4 to
411
=== added file 'debian/patches/12_hang_after_resume.diff'
--- debian/patches/12_hang_after_resume.diff 1970-01-01 00:00:00 +0000
+++ debian/patches/12_hang_after_resume.diff 2015-05-21 08:58:22 +0000
@@ -0,0 +1,47 @@
1Description: Fix occasional hang after suspend/resume.
2 After suspend/resume linux kernel may loose our requests, and
3 usb_urb_transfer() will wait forever for lost requests to complete. With this
4 patch lost request are properly handled.
5 Also fixes race condition leading to stack corruption.
6Author: Alexander Ponyatykh <lazyranma@gmail.com>
7Bug-Ubuntu: https://bugs.launchpad.net/ubuntu/+source/libusb/+bug/1455924
8Forwarded: no
9Last-Update: 2015-05-17
10===================================================================
11--- a/linux.c
12+++ b/linux.c
13@@ -166,7 +166,7 @@
14 int bytesdone = 0, requested;
15 struct timeval tv, tv_ref, tv_now;
16 struct usb_urb *context;
17- int ret, waiting;
18+ int ret, waiting, our_urb_dequeued;
19
20 /*
21 * HACK: The use of urb.usercontext is a hack to get threaded applications
22@@ -277,7 +277,24 @@
23 * then we need to reap it or else the next time we call this function,
24 * we'll get the previous completion and exit early
25 */
26- ioctl(dev->fd, IOCTL_USB_REAPURB, &context);
27+ our_urb_dequeued = 0;
28+ do {
29+ context = NULL;
30+ ret = ioctl(dev->fd, IOCTL_USB_REAPURBNDELAY, &context);
31+ if (context) {
32+ if (context == &urb) {
33+ our_urb_dequeued = 1;
34+ } else {
35+ /* We need to retry since we got completed URB, but not ours.
36+ * Otherwise other thread may write to our already deallocated
37+ * urb.usercontext */
38+ context->usercontext = URB_USERCONTEXT_COOKIE;
39+ }
40+ }
41+ } while (context && !our_urb_dequeued);
42+
43+ if (usb_debug > 0 && !our_urb_dequeued && !urb.usercontext)
44+ fprintf(stderr, "usb_urb_transfer: kernel may have lost our URB! (or other thread dequeued it)\n");
45
46 return rc;
47 }
048
=== modified file 'debian/patches/series'
--- debian/patches/series 2014-07-03 20:17:03 +0000
+++ debian/patches/series 2015-05-21 08:58:22 +0000
@@ -10,4 +10,5 @@
1009_dummy.diff1009_dummy.diff
1110_hurd.diff1110_hurd.diff
1211_transfer_timeout.diff1211_transfer_timeout.diff
1312_hang_after_resume.diff
1391_ac_prog_cxx.diff1491_ac_prog_cxx.diff
1415
=== modified file 'linux.c'
--- linux.c 2014-07-03 20:17:03 +0000
+++ linux.c 2015-05-21 08:58:22 +0000
@@ -166,7 +166,7 @@
166 int bytesdone = 0, requested;166 int bytesdone = 0, requested;
167 struct timeval tv, tv_ref, tv_now;167 struct timeval tv, tv_ref, tv_now;
168 struct usb_urb *context;168 struct usb_urb *context;
169 int ret, waiting;169 int ret, waiting, our_urb_dequeued;
170170
171 /*171 /*
172 * HACK: The use of urb.usercontext is a hack to get threaded applications172 * HACK: The use of urb.usercontext is a hack to get threaded applications
@@ -277,7 +277,24 @@
277 * then we need to reap it or else the next time we call this function,277 * then we need to reap it or else the next time we call this function,
278 * we'll get the previous completion and exit early278 * we'll get the previous completion and exit early
279 */279 */
280 ioctl(dev->fd, IOCTL_USB_REAPURB, &context);280 our_urb_dequeued = 0;
281 do {
282 context = NULL;
283 ret = ioctl(dev->fd, IOCTL_USB_REAPURBNDELAY, &context);
284 if (context) {
285 if (context == &urb) {
286 our_urb_dequeued = 1;
287 } else {
288 /* We need to retry since we got completed URB, but not ours.
289 * Otherwise other thread may write to our already deallocated
290 * urb.usercontext */
291 context->usercontext = URB_USERCONTEXT_COOKIE;
292 }
293 }
294 } while (context && !our_urb_dequeued);
295
296 if (usb_debug > 0 && !our_urb_dequeued && !urb.usercontext)
297 fprintf(stderr, "usb_urb_transfer: kernel may have lost our URB! (or other thread dequeued it)\n");
281298
282 return rc;299 return rc;
283 }300 }

Subscribers

People subscribed via source and target branches