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