diff options
Diffstat (limited to 'drivers/usb/gadget/function/f_uvc.c')
-rw-r--r-- | drivers/usb/gadget/function/f_uvc.c | 54 |
1 files changed, 44 insertions, 10 deletions
diff --git a/drivers/usb/gadget/function/f_uvc.c b/drivers/usb/gadget/function/f_uvc.c index e126439e4b65..945b3bd2ca98 100644 --- a/drivers/usb/gadget/function/f_uvc.c +++ b/drivers/usb/gadget/function/f_uvc.c | |||
@@ -279,27 +279,41 @@ uvc_function_get_alt(struct usb_function *f, unsigned interface) | |||
279 | else if (interface != uvc->streaming_intf) | 279 | else if (interface != uvc->streaming_intf) |
280 | return -EINVAL; | 280 | return -EINVAL; |
281 | else | 281 | else |
282 | return uvc->state == UVC_STATE_STREAMING ? 1 : 0; | 282 | return uvc->video.ep->driver_data ? 1 : 0; |
283 | } | 283 | } |
284 | 284 | ||
285 | static int | 285 | static int |
286 | uvc_function_set_alt(struct usb_function *f, unsigned interface, unsigned alt) | 286 | uvc_function_set_alt(struct usb_function *f, unsigned interface, unsigned alt) |
287 | { | 287 | { |
288 | struct uvc_device *uvc = to_uvc(f); | 288 | struct uvc_device *uvc = to_uvc(f); |
289 | struct usb_composite_dev *cdev = f->config->cdev; | ||
289 | struct v4l2_event v4l2_event; | 290 | struct v4l2_event v4l2_event; |
290 | struct uvc_event *uvc_event = (void *)&v4l2_event.u.data; | 291 | struct uvc_event *uvc_event = (void *)&v4l2_event.u.data; |
291 | int ret; | 292 | int ret; |
292 | 293 | ||
293 | INFO(f->config->cdev, "uvc_function_set_alt(%u, %u)\n", interface, alt); | 294 | INFO(cdev, "uvc_function_set_alt(%u, %u)\n", interface, alt); |
294 | 295 | ||
295 | if (interface == uvc->control_intf) { | 296 | if (interface == uvc->control_intf) { |
296 | if (alt) | 297 | if (alt) |
297 | return -EINVAL; | 298 | return -EINVAL; |
298 | 299 | ||
300 | if (uvc->control_ep->driver_data) { | ||
301 | INFO(cdev, "reset UVC Control\n"); | ||
302 | usb_ep_disable(uvc->control_ep); | ||
303 | uvc->control_ep->driver_data = NULL; | ||
304 | } | ||
305 | |||
306 | if (!uvc->control_ep->desc) | ||
307 | if (config_ep_by_speed(cdev->gadget, f, uvc->control_ep)) | ||
308 | return -EINVAL; | ||
309 | |||
310 | usb_ep_enable(uvc->control_ep); | ||
311 | uvc->control_ep->driver_data = uvc; | ||
312 | |||
299 | if (uvc->state == UVC_STATE_DISCONNECTED) { | 313 | if (uvc->state == UVC_STATE_DISCONNECTED) { |
300 | memset(&v4l2_event, 0, sizeof(v4l2_event)); | 314 | memset(&v4l2_event, 0, sizeof(v4l2_event)); |
301 | v4l2_event.type = UVC_EVENT_CONNECT; | 315 | v4l2_event.type = UVC_EVENT_CONNECT; |
302 | uvc_event->speed = f->config->cdev->gadget->speed; | 316 | uvc_event->speed = cdev->gadget->speed; |
303 | v4l2_event_queue(uvc->vdev, &v4l2_event); | 317 | v4l2_event_queue(uvc->vdev, &v4l2_event); |
304 | 318 | ||
305 | uvc->state = UVC_STATE_CONNECTED; | 319 | uvc->state = UVC_STATE_CONNECTED; |
@@ -321,8 +335,10 @@ uvc_function_set_alt(struct usb_function *f, unsigned interface, unsigned alt) | |||
321 | if (uvc->state != UVC_STATE_STREAMING) | 335 | if (uvc->state != UVC_STATE_STREAMING) |
322 | return 0; | 336 | return 0; |
323 | 337 | ||
324 | if (uvc->video.ep) | 338 | if (uvc->video.ep) { |
325 | usb_ep_disable(uvc->video.ep); | 339 | usb_ep_disable(uvc->video.ep); |
340 | uvc->video.ep->driver_data = NULL; | ||
341 | } | ||
326 | 342 | ||
327 | memset(&v4l2_event, 0, sizeof(v4l2_event)); | 343 | memset(&v4l2_event, 0, sizeof(v4l2_event)); |
328 | v4l2_event.type = UVC_EVENT_STREAMOFF; | 344 | v4l2_event.type = UVC_EVENT_STREAMOFF; |
@@ -335,14 +351,22 @@ uvc_function_set_alt(struct usb_function *f, unsigned interface, unsigned alt) | |||
335 | if (uvc->state != UVC_STATE_CONNECTED) | 351 | if (uvc->state != UVC_STATE_CONNECTED) |
336 | return 0; | 352 | return 0; |
337 | 353 | ||
338 | if (uvc->video.ep) { | 354 | if (!uvc->video.ep) |
339 | ret = config_ep_by_speed(f->config->cdev->gadget, | 355 | return -EINVAL; |
340 | &(uvc->func), uvc->video.ep); | 356 | |
341 | if (ret) | 357 | if (uvc->video.ep->driver_data) { |
342 | return ret; | 358 | INFO(cdev, "reset UVC\n"); |
343 | usb_ep_enable(uvc->video.ep); | 359 | usb_ep_disable(uvc->video.ep); |
360 | uvc->video.ep->driver_data = NULL; | ||
344 | } | 361 | } |
345 | 362 | ||
363 | ret = config_ep_by_speed(f->config->cdev->gadget, | ||
364 | &(uvc->func), uvc->video.ep); | ||
365 | if (ret) | ||
366 | return ret; | ||
367 | usb_ep_enable(uvc->video.ep); | ||
368 | uvc->video.ep->driver_data = uvc; | ||
369 | |||
346 | memset(&v4l2_event, 0, sizeof(v4l2_event)); | 370 | memset(&v4l2_event, 0, sizeof(v4l2_event)); |
347 | v4l2_event.type = UVC_EVENT_STREAMON; | 371 | v4l2_event.type = UVC_EVENT_STREAMON; |
348 | v4l2_event_queue(uvc->vdev, &v4l2_event); | 372 | v4l2_event_queue(uvc->vdev, &v4l2_event); |
@@ -366,6 +390,16 @@ uvc_function_disable(struct usb_function *f) | |||
366 | v4l2_event_queue(uvc->vdev, &v4l2_event); | 390 | v4l2_event_queue(uvc->vdev, &v4l2_event); |
367 | 391 | ||
368 | uvc->state = UVC_STATE_DISCONNECTED; | 392 | uvc->state = UVC_STATE_DISCONNECTED; |
393 | |||
394 | if (uvc->video.ep->driver_data) { | ||
395 | usb_ep_disable(uvc->video.ep); | ||
396 | uvc->video.ep->driver_data = NULL; | ||
397 | } | ||
398 | |||
399 | if (uvc->control_ep->driver_data) { | ||
400 | usb_ep_disable(uvc->control_ep); | ||
401 | uvc->control_ep->driver_data = NULL; | ||
402 | } | ||
369 | } | 403 | } |
370 | 404 | ||
371 | /* -------------------------------------------------------------------------- | 405 | /* -------------------------------------------------------------------------- |