.. | .. |
---|
6 | 6 | * Laurent Pinchart (laurent.pinchart@ideasonboard.com) |
---|
7 | 7 | */ |
---|
8 | 8 | |
---|
| 9 | +#include <asm/barrier.h> |
---|
9 | 10 | #include <linux/kernel.h> |
---|
10 | 11 | #include <linux/input.h> |
---|
11 | 12 | #include <linux/slab.h> |
---|
.. | .. |
---|
179 | 180 | |
---|
180 | 181 | switch (status->bAttribute) { |
---|
181 | 182 | case UVC_CTRL_VALUE_CHANGE: |
---|
182 | | - return uvc_ctrl_status_event(urb, chain, ctrl, status->bValue); |
---|
| 183 | + return uvc_ctrl_status_event_async(urb, chain, ctrl, |
---|
| 184 | + status->bValue); |
---|
183 | 185 | |
---|
184 | 186 | case UVC_CTRL_INFO_CHANGE: |
---|
185 | 187 | case UVC_CTRL_FAILURE_CHANGE: |
---|
.. | .. |
---|
309 | 311 | |
---|
310 | 312 | void uvc_status_stop(struct uvc_device *dev) |
---|
311 | 313 | { |
---|
| 314 | + struct uvc_ctrl_work *w = &dev->async_ctrl; |
---|
| 315 | + |
---|
| 316 | + /* |
---|
| 317 | + * Prevent the asynchronous control handler from requeing the URB. The |
---|
| 318 | + * barrier is needed so the flush_status change is visible to other |
---|
| 319 | + * CPUs running the asynchronous handler before usb_kill_urb() is |
---|
| 320 | + * called below. |
---|
| 321 | + */ |
---|
| 322 | + smp_store_release(&dev->flush_status, true); |
---|
| 323 | + |
---|
| 324 | + /* |
---|
| 325 | + * Cancel any pending asynchronous work. If any status event was queued, |
---|
| 326 | + * process it synchronously. |
---|
| 327 | + */ |
---|
| 328 | + if (cancel_work_sync(&w->work)) |
---|
| 329 | + uvc_ctrl_status_event(w->chain, w->ctrl, w->data); |
---|
| 330 | + |
---|
| 331 | + /* Kill the urb. */ |
---|
312 | 332 | usb_kill_urb(dev->int_urb); |
---|
| 333 | + |
---|
| 334 | + /* |
---|
| 335 | + * The URB completion handler may have queued asynchronous work. This |
---|
| 336 | + * won't resubmit the URB as flush_status is set, but it needs to be |
---|
| 337 | + * cancelled before returning or it could then race with a future |
---|
| 338 | + * uvc_status_start() call. |
---|
| 339 | + */ |
---|
| 340 | + if (cancel_work_sync(&w->work)) |
---|
| 341 | + uvc_ctrl_status_event(w->chain, w->ctrl, w->data); |
---|
| 342 | + |
---|
| 343 | + /* |
---|
| 344 | + * From this point, there are no events on the queue and the status URB |
---|
| 345 | + * is dead. No events will be queued until uvc_status_start() is called. |
---|
| 346 | + * The barrier is needed to make sure that flush_status is visible to |
---|
| 347 | + * uvc_ctrl_status_event_work() when uvc_status_start() will be called |
---|
| 348 | + * again. |
---|
| 349 | + */ |
---|
| 350 | + smp_store_release(&dev->flush_status, false); |
---|
313 | 351 | } |
---|