| .. | .. |
|---|
| 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 | } |
|---|