From d7577b389233a74609841492feaf6a55967aa5c8 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Mon, 29 Sep 2014 09:19:59 -0500 Subject: usb: gadget: function: uvc: conditionally dequeue We shouldn't try to dequeue a NULL pointer. Signed-off-by: Felipe Balbi --- drivers/usb/gadget/function/uvc_video.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'drivers/usb/gadget') diff --git a/drivers/usb/gadget/function/uvc_video.c b/drivers/usb/gadget/function/uvc_video.c index c3e1f27dbbef..9cb86bc1a9a5 100644 --- a/drivers/usb/gadget/function/uvc_video.c +++ b/drivers/usb/gadget/function/uvc_video.c @@ -352,7 +352,8 @@ int uvcg_video_enable(struct uvc_video *video, int enable) if (!enable) { for (i = 0; i < UVC_NUM_REQUESTS; ++i) - usb_ep_dequeue(video->ep, video->req[i]); + if (video->req[i]) + usb_ep_dequeue(video->ep, video->req[i]); uvc_video_free_requests(video); uvcg_queue_enable(&video->queue, 0); -- cgit v1.2.3 From c92bae753722a0010f1cabfb242581e130378b9f Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Mon, 29 Sep 2014 09:20:35 -0500 Subject: usb: gadget: function: uvc: make sure to balance ep enable/disable If a set_alt() to the same alternate setting that's already selected is received, functions are required to reset the interface state, this means we must disable all endpoints and reenable them again. This is also documented on our kdoc for struct usb_function * @set_alt: (REQUIRED) Reconfigures altsettings; function drivers may * initialize usb_ep.driver data at this time (when it is used). * Note that setting an interface to its current altsetting resets * interface state, and that all interfaces have a disabled state. Signed-off-by: Felipe Balbi --- drivers/usb/gadget/function/f_uvc.c | 29 ++++++++++++++++++++--------- 1 file changed, 20 insertions(+), 9 deletions(-) (limited to 'drivers/usb/gadget') diff --git a/drivers/usb/gadget/function/f_uvc.c b/drivers/usb/gadget/function/f_uvc.c index e126439e4b65..e00e8b79390a 100644 --- a/drivers/usb/gadget/function/f_uvc.c +++ b/drivers/usb/gadget/function/f_uvc.c @@ -286,11 +286,12 @@ static int uvc_function_set_alt(struct usb_function *f, unsigned interface, unsigned alt) { struct uvc_device *uvc = to_uvc(f); + struct usb_composite_dev *cdev = f->config->cdev; struct v4l2_event v4l2_event; struct uvc_event *uvc_event = (void *)&v4l2_event.u.data; int ret; - INFO(f->config->cdev, "uvc_function_set_alt(%u, %u)\n", interface, alt); + INFO(cdev, "uvc_function_set_alt(%u, %u)\n", interface, alt); if (interface == uvc->control_intf) { if (alt) @@ -299,7 +300,7 @@ uvc_function_set_alt(struct usb_function *f, unsigned interface, unsigned alt) if (uvc->state == UVC_STATE_DISCONNECTED) { memset(&v4l2_event, 0, sizeof(v4l2_event)); v4l2_event.type = UVC_EVENT_CONNECT; - uvc_event->speed = f->config->cdev->gadget->speed; + uvc_event->speed = cdev->gadget->speed; v4l2_event_queue(uvc->vdev, &v4l2_event); uvc->state = UVC_STATE_CONNECTED; @@ -321,8 +322,10 @@ uvc_function_set_alt(struct usb_function *f, unsigned interface, unsigned alt) if (uvc->state != UVC_STATE_STREAMING) return 0; - if (uvc->video.ep) + if (uvc->video.ep) { usb_ep_disable(uvc->video.ep); + uvc->video.ep->driver_data = NULL; + } memset(&v4l2_event, 0, sizeof(v4l2_event)); v4l2_event.type = UVC_EVENT_STREAMOFF; @@ -335,14 +338,22 @@ uvc_function_set_alt(struct usb_function *f, unsigned interface, unsigned alt) if (uvc->state != UVC_STATE_CONNECTED) return 0; - if (uvc->video.ep) { - ret = config_ep_by_speed(f->config->cdev->gadget, - &(uvc->func), uvc->video.ep); - if (ret) - return ret; - usb_ep_enable(uvc->video.ep); + if (!uvc->video.ep) + return -EINVAL; + + if (uvc->video.ep->driver_data) { + INFO(cdev, "reset UVC\n"); + usb_ep_disable(uvc->video.ep); + uvc->video.ep->driver_data = NULL; } + ret = config_ep_by_speed(f->config->cdev->gadget, + &(uvc->func), uvc->video.ep); + if (ret) + return ret; + usb_ep_enable(uvc->video.ep); + uvc->video.ep->driver_data = uvc; + memset(&v4l2_event, 0, sizeof(v4l2_event)); v4l2_event.type = UVC_EVENT_STREAMON; v4l2_event_queue(uvc->vdev, &v4l2_event); -- cgit v1.2.3 From e975be287b87e0862b0f57e7326a79667e32a90a Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Mon, 29 Sep 2014 11:13:26 -0500 Subject: usb: gadget: function: uvc: return correct alt-setting If our alternate setting has been selected, we must return that on a subsequent Get Interface request even if we're not streaming. Signed-off-by: Felipe Balbi --- drivers/usb/gadget/function/f_uvc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/usb/gadget') diff --git a/drivers/usb/gadget/function/f_uvc.c b/drivers/usb/gadget/function/f_uvc.c index e00e8b79390a..4138ad5adb77 100644 --- a/drivers/usb/gadget/function/f_uvc.c +++ b/drivers/usb/gadget/function/f_uvc.c @@ -279,7 +279,7 @@ uvc_function_get_alt(struct usb_function *f, unsigned interface) else if (interface != uvc->streaming_intf) return -EINVAL; else - return uvc->state == UVC_STATE_STREAMING ? 1 : 0; + return uvc->video.ep->driver_data ? 1 : 0; } static int -- cgit v1.2.3 From 52ec49a5e56a27c5b6f8217708783eff39f24c16 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Mon, 29 Sep 2014 13:35:54 -0500 Subject: usb: gadget: function: acm: make f_acm pass USB20CV Chapter9 During Halt Endpoint Test, our interrupt endpoint will be disabled, which will clear out ep->desc to NULL. Unless we call config_ep_by_speed() again, we will not be able to enable this endpoint which will make us fail that test. Fixes: f9c56cd (usb: gadget: Clear usb_endpoint_descriptor inside the struct usb_ep on disable) Cc: # v3.4+ Signed-off-by: Felipe Balbi --- drivers/usb/gadget/function/f_acm.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'drivers/usb/gadget') diff --git a/drivers/usb/gadget/function/f_acm.c b/drivers/usb/gadget/function/f_acm.c index 6da4685490ef..aad8165e98ef 100644 --- a/drivers/usb/gadget/function/f_acm.c +++ b/drivers/usb/gadget/function/f_acm.c @@ -433,12 +433,12 @@ static int acm_set_alt(struct usb_function *f, unsigned intf, unsigned alt) dev_vdbg(&cdev->gadget->dev, "reset acm control interface %d\n", intf); usb_ep_disable(acm->notify); - } else { - dev_vdbg(&cdev->gadget->dev, - "init acm ctrl interface %d\n", intf); + } + + if (!acm->notify->desc) if (config_ep_by_speed(cdev->gadget, f, acm->notify)) return -EINVAL; - } + usb_ep_enable(acm->notify); acm->notify->driver_data = acm; -- cgit v1.2.3 From 62e370785cb337981999ac7ec364e22ffb6c2cd3 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Mon, 29 Sep 2014 13:41:04 -0500 Subject: usb: gadget: function: uvc: manage our video control endpoint just like any other endpoint, we must enable/disable our video control endpoint based on calls to our ->set_alt() method. Signed-off-by: Felipe Balbi --- drivers/usb/gadget/function/f_uvc.c | 13 +++++++++++++ 1 file changed, 13 insertions(+) (limited to 'drivers/usb/gadget') diff --git a/drivers/usb/gadget/function/f_uvc.c b/drivers/usb/gadget/function/f_uvc.c index 4138ad5adb77..413a09f366c4 100644 --- a/drivers/usb/gadget/function/f_uvc.c +++ b/drivers/usb/gadget/function/f_uvc.c @@ -297,6 +297,19 @@ uvc_function_set_alt(struct usb_function *f, unsigned interface, unsigned alt) if (alt) return -EINVAL; + if (uvc->control_ep->driver_data) { + INFO(cdev, "reset UVC Control\n"); + usb_ep_disable(uvc->control_ep); + uvc->control_ep->driver_data = NULL; + } + + if (!uvc->control_ep->desc) + if (config_ep_by_speed(cdev->gadget, f, uvc->control_ep)) + return -EINVAL; + + usb_ep_enable(uvc->control_ep); + uvc->control_ep->driver_data = uvc; + if (uvc->state == UVC_STATE_DISCONNECTED) { memset(&v4l2_event, 0, sizeof(v4l2_event)); v4l2_event.type = UVC_EVENT_CONNECT; -- cgit v1.2.3 From e3122f5fedb6d88a043b60822f601f7ab517a465 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Mon, 29 Sep 2014 13:43:20 -0500 Subject: usb: gadget: function: uvc: disable endpoints on ->disable() when our ->disable() method is called, we must make sure to teardown all our resources, including endpoints. Signed-off-by: Felipe Balbi --- drivers/usb/gadget/function/f_uvc.c | 10 ++++++++++ 1 file changed, 10 insertions(+) (limited to 'drivers/usb/gadget') diff --git a/drivers/usb/gadget/function/f_uvc.c b/drivers/usb/gadget/function/f_uvc.c index 413a09f366c4..945b3bd2ca98 100644 --- a/drivers/usb/gadget/function/f_uvc.c +++ b/drivers/usb/gadget/function/f_uvc.c @@ -390,6 +390,16 @@ uvc_function_disable(struct usb_function *f) v4l2_event_queue(uvc->vdev, &v4l2_event); uvc->state = UVC_STATE_DISCONNECTED; + + if (uvc->video.ep->driver_data) { + usb_ep_disable(uvc->video.ep); + uvc->video.ep->driver_data = NULL; + } + + if (uvc->control_ep->driver_data) { + usb_ep_disable(uvc->control_ep); + uvc->control_ep->driver_data = NULL; + } } /* -------------------------------------------------------------------------- -- cgit v1.2.3 From 703a303c187ef7e3c8daf8a1be343576c9579eaf Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Mon, 29 Sep 2014 14:18:14 -0500 Subject: usb: gadget: function: uac2: add wMaxPacketSize to ep desc Endpoint descriptors should pass wMaxPacketSize. Note that this also fixes USB20CV Other Speed Endpoint Descriptor Tests. Signed-off-by: Felipe Balbi --- drivers/usb/gadget/function/f_uac2.c | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'drivers/usb/gadget') diff --git a/drivers/usb/gadget/function/f_uac2.c b/drivers/usb/gadget/function/f_uac2.c index a5a27a504d67..fa511180c241 100644 --- a/drivers/usb/gadget/function/f_uac2.c +++ b/drivers/usb/gadget/function/f_uac2.c @@ -772,6 +772,7 @@ struct usb_endpoint_descriptor fs_epout_desc = { .bEndpointAddress = USB_DIR_OUT, .bmAttributes = USB_ENDPOINT_XFER_ISOC | USB_ENDPOINT_SYNC_ASYNC, + .wMaxPacketSize = cpu_to_le16(1023), .bInterval = 1, }; @@ -780,6 +781,7 @@ struct usb_endpoint_descriptor hs_epout_desc = { .bDescriptorType = USB_DT_ENDPOINT, .bmAttributes = USB_ENDPOINT_XFER_ISOC | USB_ENDPOINT_SYNC_ASYNC, + .wMaxPacketSize = cpu_to_le16(1024), .bInterval = 4, }; @@ -847,6 +849,7 @@ struct usb_endpoint_descriptor fs_epin_desc = { .bEndpointAddress = USB_DIR_IN, .bmAttributes = USB_ENDPOINT_XFER_ISOC | USB_ENDPOINT_SYNC_ASYNC, + .wMaxPacketSize = cpu_to_le16(1023), .bInterval = 1, }; @@ -855,6 +858,7 @@ struct usb_endpoint_descriptor hs_epin_desc = { .bDescriptorType = USB_DT_ENDPOINT, .bmAttributes = USB_ENDPOINT_XFER_ISOC | USB_ENDPOINT_SYNC_ASYNC, + .wMaxPacketSize = cpu_to_le16(1024), .bInterval = 4, }; -- cgit v1.2.3 From f3bb7b298120df8a9b7354e4f6d07e3185c15db7 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Mon, 29 Sep 2014 14:23:41 -0500 Subject: usb: gadget: function: uac2: prevent double ep disable without this check, f_uac2 would try to disable the same endpoint twice. Fix that. Signed-off-by: Felipe Balbi --- drivers/usb/gadget/function/f_uac2.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'drivers/usb/gadget') diff --git a/drivers/usb/gadget/function/f_uac2.c b/drivers/usb/gadget/function/f_uac2.c index fa511180c241..1146f4d5f66d 100644 --- a/drivers/usb/gadget/function/f_uac2.c +++ b/drivers/usb/gadget/function/f_uac2.c @@ -951,6 +951,9 @@ free_ep(struct uac2_rtd_params *prm, struct usb_ep *ep) struct snd_uac2_chip *uac2 = prm->uac2; int i; + if (!prm->ep_enabled) + return; + prm->ep_enabled = false; for (i = 0; i < USB_XFERS; i++) { -- cgit v1.2.3 From de1e6e799fc4e6f0452737e454267c0bfdf88c62 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Mon, 29 Sep 2014 14:24:09 -0500 Subject: usb: gadget: function: uac2: add a release method devices are required to provide a release method. This patch fixes the following WARN(): [ 42.611159] ------------[ cut here ]------------ [ 42.616025] WARNING: CPU: 0 PID: 1453 at drivers/base/core.c:250 device_release+0x94/0xa0() [ 42.624820] Device 'snd_uac2.0' does not have a release() function, it is broken and must be fixed. [ 42.634328] Modules linked in: usb_f_uac2 g_audio(-) libcomposite configfs xhci_hcd snd_soc_davinci_mcasp snd_soc_edma snd_soc_tlv320aic3x snd_soc_omap snd_soc_evm snd_soc_core dwc3 snd_compress omapdrm snd_pcm_dmaengine snd_pcm snd_timer snd fb_sys_fops lis3lv02d_i2c matrix_keypad dwc3_omap lis3lv02d panel_dpi input_polldev soundcore [ 42.665687] CPU: 0 PID: 1453 Comm: modprobe Tainted: G D 3.17.0-rc6-00448-g9f3d0ec-dirty #188 [ 42.675756] [] (unwind_backtrace) from [] (show_stack+0x20/0x24) [ 42.683911] [] (show_stack) from [] (dump_stack+0x8c/0xa4) [ 42.691526] [] (dump_stack) from [] (warn_slowpath_common+0x7c/0xa0) [ 42.700004] [] (warn_slowpath_common) from [] (warn_slowpath_fmt+0x40/0x48) [ 42.709194] [] (warn_slowpath_fmt) from [] (device_release+0x94/0xa0) [ 42.717794] [] (device_release) from [] (kobject_cleanup+0x4c/0x7c) [ 42.726189] [] (kobject_cleanup) from [] (kobject_put+0x60/0x90) [ 42.734316] [] (kobject_put) from [] (put_device+0x24/0x28) [ 42.741995] [] (put_device) from [] (platform_device_unregister+0x2c/0x30) [ 42.751061] [] (platform_device_unregister) from [] (afunc_unbind+0x2c/0x68 [usb_f_uac2]) [ 42.761523] [] (afunc_unbind [usb_f_uac2]) from [] (remove_config.isra.8+0xe8/0x100 [libcomposite]) [ 42.772868] [] (remove_config.isra.8 [libcomposite]) from [] (__composite_unbind+0x48/0xb0 [libcomposite]) [ 42.784855] [] (__composite_unbind [libcomposite]) from [] (composite_unbind+0x1c/0x20 [libcomposite]) [ 42.796446] [] (composite_unbind [libcomposite]) from [] (usb_gadget_remove_driver+0x78/0xb0) [ 42.807224] [] (usb_gadget_remove_driver) from [] (usb_gadget_unregister_driver+0x74/0xb8) [ 42.817742] [] (usb_gadget_unregister_driver) from [] (usb_composite_unregister+0x1c/0x20 [libcomposite]) [ 42.829632] [] (usb_composite_unregister [libcomposite]) from [] (audio_driver_exit+0x14/0x1c [g_audio]) [ 42.841430] [] (audio_driver_exit [g_audio]) from [] (SyS_delete_module+0x120/0x1b0) [ 42.851415] [] (SyS_delete_module) from [] (ret_fast_syscall+0x0/0x48) [ 42.860075] ---[ end trace bb22e678d8d6db7b ]--- root@saruman:~# Signed-off-by: Felipe Balbi --- drivers/usb/gadget/function/f_uac2.c | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'drivers/usb/gadget') diff --git a/drivers/usb/gadget/function/f_uac2.c b/drivers/usb/gadget/function/f_uac2.c index 1146f4d5f66d..9296e598428c 100644 --- a/drivers/usb/gadget/function/f_uac2.c +++ b/drivers/usb/gadget/function/f_uac2.c @@ -512,6 +512,11 @@ static int snd_uac2_remove(struct platform_device *pdev) return 0; } +static void snd_uac2_release(struct device *dev) +{ + dev_dbg(dev, "releasing '%s'\n", dev_name(dev)); +} + static int alsa_uac2_init(struct audio_dev *agdev) { struct snd_uac2_chip *uac2 = &agdev->uac2; @@ -523,6 +528,7 @@ static int alsa_uac2_init(struct audio_dev *agdev) uac2->pdev.id = 0; uac2->pdev.name = uac2_name; + uac2->pdev.dev.release = snd_uac2_release; /* Register snd_uac2 driver */ err = platform_driver_register(&uac2->pdrv); -- cgit v1.2.3 From 3985f3ab0834edf014ebd19192d9dd77422dea67 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Mon, 29 Sep 2014 15:18:20 -0500 Subject: usb: gadget: function: f_obex: fix Interface Descriptor Test On USB20CV's Interface Descriptor Test, a series of SetInterface/GetInterface requests are issued and gadget driver is required to always return correct alternate setting. In one step of the test, g_serial with f_obex was returning the wrong value (1 instead of 0). In order to fix this, we will now hold currently selected alternate setting inside our struct f_obex and just return that from our ->get_alt() implementation. Note that his also simplifies the code a bit. Signed-off-by: Felipe Balbi --- drivers/usb/gadget/function/f_obex.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'drivers/usb/gadget') diff --git a/drivers/usb/gadget/function/f_obex.c b/drivers/usb/gadget/function/f_obex.c index 5f40080c92cc..1a1a490415f4 100644 --- a/drivers/usb/gadget/function/f_obex.c +++ b/drivers/usb/gadget/function/f_obex.c @@ -35,6 +35,7 @@ struct f_obex { struct gserial port; u8 ctrl_id; u8 data_id; + u8 cur_alt; u8 port_num; u8 can_activate; }; @@ -235,6 +236,8 @@ static int obex_set_alt(struct usb_function *f, unsigned intf, unsigned alt) } else goto fail; + obex->cur_alt = alt; + return 0; fail: @@ -245,10 +248,7 @@ static int obex_get_alt(struct usb_function *f, unsigned intf) { struct f_obex *obex = func_to_obex(f); - if (intf == obex->ctrl_id) - return 0; - - return obex->port.in->driver_data ? 1 : 0; + return obex->cur_alt; } static void obex_disable(struct usb_function *f) -- cgit v1.2.3 From a6615937bcd9234e6d6bb817c3701fce44d0a84d Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Tue, 30 Sep 2014 16:08:03 -0500 Subject: usb: gadget: composite: enable BESL support According to USB 2.0 ECN Errata for Link Power Management (USB2-LPM-Errata-final.pdf), BESL must be enabled if LPM is enabled. This helps with USB30CV TD 9.21 LPM L1 Suspend Resume Test. Signed-off-by: Felipe Balbi --- drivers/usb/gadget/composite.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/usb/gadget') diff --git a/drivers/usb/gadget/composite.c b/drivers/usb/gadget/composite.c index a8c18df171c3..f6a51fddd5b5 100644 --- a/drivers/usb/gadget/composite.c +++ b/drivers/usb/gadget/composite.c @@ -560,7 +560,7 @@ static int bos_desc(struct usb_composite_dev *cdev) usb_ext->bLength = USB_DT_USB_EXT_CAP_SIZE; usb_ext->bDescriptorType = USB_DT_DEVICE_CAPABILITY; usb_ext->bDevCapabilityType = USB_CAP_TYPE_EXT; - usb_ext->bmAttributes = cpu_to_le32(USB_LPM_SUPPORT); + usb_ext->bmAttributes = cpu_to_le32(USB_LPM_SUPPORT | USB_BESL_SUPPORT); /* * The Superspeed USB Capability descriptor shall be implemented by all -- cgit v1.2.3 From f1113be1bff674b8410fcb301d6551ab5a43193e Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Thu, 9 Oct 2014 16:15:05 +0200 Subject: usb: gadget: udc: USB_GADGET_XILINX should depend on HAS_DMA If NO_DMA=y: drivers/built-in.o: In function `xudc_done': udc-xilinx.c:(.text+0x54f4d2): undefined reference to `usb_gadget_unmap_request' drivers/built-in.o: In function `xudc_dma_send': udc-xilinx.c:(.text+0x54f9f8): undefined reference to `dma_sync_single_for_cpu' drivers/built-in.o: In function `xudc_read_fifo': udc-xilinx.c:(.text+0x54ff4a): undefined reference to `dma_sync_single_for_cpu' drivers/built-in.o: In function `xudc_ep_queue': udc-xilinx.c:(.text+0x550e7c): undefined reference to `usb_gadget_map_request' Signed-off-by: Geert Uytterhoeven Signed-off-by: Felipe Balbi --- drivers/usb/gadget/udc/Kconfig | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers/usb/gadget') diff --git a/drivers/usb/gadget/udc/Kconfig b/drivers/usb/gadget/udc/Kconfig index 3ea287b0e448..217365d35a25 100644 --- a/drivers/usb/gadget/udc/Kconfig +++ b/drivers/usb/gadget/udc/Kconfig @@ -357,6 +357,7 @@ config USB_EG20T config USB_GADGET_XILINX tristate "Xilinx USB Driver" + depends on HAS_DMA depends on OF || COMPILE_TEST help USB peripheral controller driver for Xilinx USB2 device. -- cgit v1.2.3 From a3058a5d82e296daaca07411c3738a9ddd79f302 Mon Sep 17 00:00:00 2001 From: Robert Baldyga Date: Thu, 9 Oct 2014 09:41:16 +0200 Subject: usb: gadget: f_fs: remove redundant ffs_data_get() During FunctionFS bind, ffs_data_get() function was called twice (in functionfs_bind() and in ffs_do_functionfs_bind()), while on unbind ffs_data_put() was called once (in functionfs_unbind() function). In result refcount never reached value 0, and ffs memory resources has been never released. Since ffs_data_get() call in ffs_do_functionfs_bind() is redundant and not neccessary, we remove it to have equal number of gets ans puts, and free allocated memory after refcount reach 0. Fixes: 5920cda (usb: gadget: FunctionFS: convert to new function interface with backward compatibility) Cc: # v3.14+ Signed-off-by: Robert Baldyga Signed-off-by: Felipe Balbi --- drivers/usb/gadget/function/f_fs.c | 2 -- 1 file changed, 2 deletions(-) (limited to 'drivers/usb/gadget') diff --git a/drivers/usb/gadget/function/f_fs.c b/drivers/usb/gadget/function/f_fs.c index 7c6771d027a2..12dbdafd78a8 100644 --- a/drivers/usb/gadget/function/f_fs.c +++ b/drivers/usb/gadget/function/f_fs.c @@ -2663,8 +2663,6 @@ static inline struct f_fs_opts *ffs_do_functionfs_bind(struct usb_function *f, func->conf = c; func->gadget = c->cdev->gadget; - ffs_data_get(func->ffs); - /* * in drivers/usb/gadget/configfs.c:configfs_composite_bind() * configurations are bound in sequence with list_for_each_entry, -- cgit v1.2.3 From c0d31b3c3d9a025b8d5a57c35671e60c5f388bf7 Mon Sep 17 00:00:00 2001 From: David Cohen Date: Mon, 13 Oct 2014 11:15:54 -0700 Subject: usb: ffs: fix regression when quirk_ep_out_aligned_size flag is set The commit '2e4c7553cd usb: gadget: f_fs: add aio support' broke the quirk implemented to align buffer size to maxpacketsize on out endpoint. As result, functionfs does not work on Intel platforms using dwc3 driver (i.e. Bay Trail and Merrifield). This patch fixes the issue. This code is based on a previous Qiuxu's patch. Fixes: 2e4c7553cd (usb: gadget: f_fs: add aio support) Cc: # v3.16+ Signed-off-by: David Cohen Signed-off-by: Qiuxu Zhuo Acked-by: Michal Nazarewicz Signed-off-by: Felipe Balbi --- drivers/usb/gadget/function/f_fs.c | 40 ++++++++++++++++++++++++++++++++------ 1 file changed, 34 insertions(+), 6 deletions(-) (limited to 'drivers/usb/gadget') diff --git a/drivers/usb/gadget/function/f_fs.c b/drivers/usb/gadget/function/f_fs.c index 12dbdafd78a8..63314ede7ba6 100644 --- a/drivers/usb/gadget/function/f_fs.c +++ b/drivers/usb/gadget/function/f_fs.c @@ -647,15 +647,26 @@ static void ffs_user_copy_worker(struct work_struct *work) if (io_data->read && ret > 0) { int i; size_t pos = 0; + + /* + * Since req->length may be bigger than io_data->len (after + * being rounded up to maxpacketsize), we may end up with more + * data then user space has space for. + */ + ret = min_t(int, ret, io_data->len); + use_mm(io_data->mm); for (i = 0; i < io_data->nr_segs; i++) { + size_t len = min_t(size_t, ret - pos, + io_data->iovec[i].iov_len); + if (!len) + break; if (unlikely(copy_to_user(io_data->iovec[i].iov_base, - &io_data->buf[pos], - io_data->iovec[i].iov_len))) { + &io_data->buf[pos], len))) { ret = -EFAULT; break; } - pos += io_data->iovec[i].iov_len; + pos += len; } unuse_mm(io_data->mm); } @@ -687,7 +698,7 @@ static ssize_t ffs_epfile_io(struct file *file, struct ffs_io_data *io_data) struct ffs_epfile *epfile = file->private_data; struct ffs_ep *ep; char *data = NULL; - ssize_t ret, data_len; + ssize_t ret, data_len = -EINVAL; int halt; /* Are we still active? */ @@ -787,13 +798,30 @@ static ssize_t ffs_epfile_io(struct file *file, struct ffs_io_data *io_data) /* Fire the request */ struct usb_request *req; + /* + * Sanity Check: even though data_len can't be used + * uninitialized at the time I write this comment, some + * compilers complain about this situation. + * In order to keep the code clean from warnings, data_len is + * being initialized to -EINVAL during its declaration, which + * means we can't rely on compiler anymore to warn no future + * changes won't result in data_len being used uninitialized. + * For such reason, we're adding this redundant sanity check + * here. + */ + if (unlikely(data_len == -EINVAL)) { + WARN(1, "%s: data_len == -EINVAL\n", __func__); + ret = -EINVAL; + goto error_lock; + } + if (io_data->aio) { req = usb_ep_alloc_request(ep->ep, GFP_KERNEL); if (unlikely(!req)) goto error_lock; req->buf = data; - req->length = io_data->len; + req->length = data_len; io_data->buf = data; io_data->ep = ep->ep; @@ -815,7 +843,7 @@ static ssize_t ffs_epfile_io(struct file *file, struct ffs_io_data *io_data) req = ep->req; req->buf = data; - req->length = io_data->len; + req->length = data_len; req->context = &done; req->complete = ffs_epfile_io_complete; -- cgit v1.2.3 From e0857ce58e8658657f5f12fe25272b93cfeb16aa Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Mon, 13 Oct 2014 15:30:52 -0500 Subject: usb: gadget: loopback: don't queue requests to bogus endpoints A request allocated from e.g. ep1out cannot be queued to any other endpoint. This bug has been in f_loopback at least since mid-2011 and since nobody has really screamed about it, we're not backporting to any stable kernels. Debugged with MUSB since that's the only controller which complains about this case. Signed-off-by: Felipe Balbi --- drivers/usb/gadget/function/f_loopback.c | 87 +++++++++++++++----------------- 1 file changed, 42 insertions(+), 45 deletions(-) (limited to 'drivers/usb/gadget') diff --git a/drivers/usb/gadget/function/f_loopback.c b/drivers/usb/gadget/function/f_loopback.c index bf04389137e6..298b46112b1a 100644 --- a/drivers/usb/gadget/function/f_loopback.c +++ b/drivers/usb/gadget/function/f_loopback.c @@ -253,22 +253,13 @@ static void loopback_complete(struct usb_ep *ep, struct usb_request *req) case 0: /* normal completion? */ if (ep == loop->out_ep) { - /* loop this OUT packet back IN to the host */ req->zero = (req->actual < req->length); req->length = req->actual; - status = usb_ep_queue(loop->in_ep, req, GFP_ATOMIC); - if (status == 0) - return; - - /* "should never get here" */ - ERROR(cdev, "can't loop %s to %s: %d\n", - ep->name, loop->in_ep->name, - status); } /* queue the buffer for some later OUT packet */ req->length = buflen; - status = usb_ep_queue(loop->out_ep, req, GFP_ATOMIC); + status = usb_ep_queue(ep, req, GFP_ATOMIC); if (status == 0) return; @@ -308,60 +299,66 @@ static inline struct usb_request *lb_alloc_ep_req(struct usb_ep *ep, int len) return alloc_ep_req(ep, len, buflen); } -static int -enable_loopback(struct usb_composite_dev *cdev, struct f_loopback *loop) +static int enable_endpoint(struct usb_composite_dev *cdev, struct f_loopback *loop, + struct usb_ep *ep) { - int result = 0; - struct usb_ep *ep; struct usb_request *req; unsigned i; + int result; - /* one endpoint writes data back IN to the host */ - ep = loop->in_ep; + /* + * one endpoint writes data back IN to the host while another endpoint + * just reads OUT packets + */ result = config_ep_by_speed(cdev->gadget, &(loop->function), ep); if (result) - return result; + goto fail0; result = usb_ep_enable(ep); if (result < 0) - return result; - ep->driver_data = loop; - - /* one endpoint just reads OUT packets */ - ep = loop->out_ep; - result = config_ep_by_speed(cdev->gadget, &(loop->function), ep); - if (result) goto fail0; - - result = usb_ep_enable(ep); - if (result < 0) { -fail0: - ep = loop->in_ep; - usb_ep_disable(ep); - ep->driver_data = NULL; - return result; - } ep->driver_data = loop; - /* allocate a bunch of read buffers and queue them all at once. + /* + * allocate a bunch of read buffers and queue them all at once. * we buffer at most 'qlen' transfers; fewer if any need more * than 'buflen' bytes each. */ for (i = 0; i < qlen && result == 0; i++) { req = lb_alloc_ep_req(ep, 0); - if (req) { - req->complete = loopback_complete; - result = usb_ep_queue(ep, req, GFP_ATOMIC); - if (result) - ERROR(cdev, "%s queue req --> %d\n", - ep->name, result); - } else { - usb_ep_disable(ep); - ep->driver_data = NULL; - result = -ENOMEM; - goto fail0; + if (!req) + goto fail1; + + req->complete = loopback_complete; + result = usb_ep_queue(ep, req, GFP_ATOMIC); + if (result) { + ERROR(cdev, "%s queue req --> %d\n", + ep->name, result); + goto fail1; } } + return 0; + +fail1: + usb_ep_disable(ep); + +fail0: + return result; +} + +static int +enable_loopback(struct usb_composite_dev *cdev, struct f_loopback *loop) +{ + int result = 0; + + result = enable_endpoint(cdev, loop, loop->in_ep); + if (result) + return result; + + result = enable_endpoint(cdev, loop, loop->out_ep); + if (result) + return result; + DBG(cdev, "%s enabled\n", loop->function.name); return result; } -- cgit v1.2.3 From bfa6b18c680450c17512c741ed1d818695747621 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Fri, 17 Oct 2014 11:10:25 -0500 Subject: usb: gadget: udc: core: fix kernel oops with soft-connect Currently, there's no guarantee that udc->driver will be valid when using soft_connect sysfs interface. In fact, we can very easily trigger a NULL pointer dereference by trying to disconnect when a gadget driver isn't loaded. Fix this bug: ~# echo disconnect > soft_connect [ 33.685743] Unable to handle kernel NULL pointer dereference at virtual address 00000014 [ 33.694221] pgd = ed0cc000 [ 33.697174] [00000014] *pgd=ae351831, *pte=00000000, *ppte=00000000 [ 33.703766] Internal error: Oops: 17 [#1] SMP ARM [ 33.708697] Modules linked in: xhci_plat_hcd xhci_hcd snd_soc_davinci_mcasp snd_soc_tlv320aic3x snd_soc_edma snd_soc_omap snd_soc_evm snd_soc_core dwc3 snd_compress snd_pcm_dmaengine snd_pcm snd_timer snd lis3lv02d_i2c matrix_keypad lis3lv02d dwc3_omap input_polldev soundcore [ 33.734372] CPU: 0 PID: 1457 Comm: bash Not tainted 3.17.0-09740-ga93416e-dirty #345 [ 33.742457] task: ee71ce00 ti: ee68a000 task.ti: ee68a000 [ 33.748116] PC is at usb_udc_softconn_store+0xa4/0xec [ 33.753416] LR is at mark_held_locks+0x78/0x90 [ 33.758057] pc : [] lr : [] psr: 20000013 [ 33.758057] sp : ee68bec8 ip : c0c00008 fp : ee68bee4 [ 33.770050] r10: ee6b394c r9 : ee68bf80 r8 : ee6062c0 [ 33.775508] r7 : 00000000 r6 : ee6062c0 r5 : 0000000b r4 : ee739408 [ 33.782346] r3 : 00000000 r2 : 00000000 r1 : ee71d390 r0 : ee664170 [ 33.789168] Flags: nzCv IRQs on FIQs on Mode SVC_32 ISA ARM Segment user [ 33.796636] Control: 10c5387d Table: ad0cc059 DAC: 00000015 [ 33.802638] Process bash (pid: 1457, stack limit = 0xee68a248) [ 33.808740] Stack: (0xee68bec8 to 0xee68c000) [ 33.813299] bec0: 0000000b c0411284 ee6062c0 00000000 ee68bef4 ee68bee8 [ 33.821862] bee0: c04112ac c04df090 ee68bf14 ee68bef8 c01c2868 c0411290 0000000b ee6b3940 [ 33.830419] bf00: 00000000 00000000 ee68bf4c ee68bf18 c01c1a24 c01c2818 00000000 00000000 [ 33.838990] bf20: ee61b940 ee2f47c0 0000000b 000ce408 ee68bf80 c000f304 ee68a000 00000000 [ 33.847544] bf40: ee68bf7c ee68bf50 c0152dd8 c01c1960 ee68bf7c c0170af8 ee68bf7c ee2f47c0 [ 33.856099] bf60: ee2f47c0 000ce408 0000000b c000f304 ee68bfa4 ee68bf80 c0153330 c0152d34 [ 33.864653] bf80: 00000000 00000000 0000000b 000ce408 b6e7fb50 00000004 00000000 ee68bfa8 [ 33.873204] bfa0: c000f080 c01532e8 0000000b 000ce408 00000001 000ce408 0000000b 00000000 [ 33.881763] bfc0: 0000000b 000ce408 b6e7fb50 00000004 0000000b 00000000 000c5758 00000000 [ 33.890319] bfe0: 00000000 bec2c924 b6de422d b6e1d226 40000030 00000001 75716d2f 00657565 [ 33.898890] [] (usb_udc_softconn_store) from [] (dev_attr_store+0x28/0x34) [ 33.907920] [] (dev_attr_store) from [] (sysfs_kf_write+0x5c/0x60) [ 33.916200] [] (sysfs_kf_write) from [] (kernfs_fop_write+0xd0/0x194) [ 33.924773] [] (kernfs_fop_write) from [] (vfs_write+0xb0/0x1bc) [ 33.932874] [] (vfs_write) from [] (SyS_write+0x54/0xb0) [ 33.940247] [] (SyS_write) from [] (ret_fast_syscall+0x0/0x48) [ 33.948160] Code: e1a01007 e12fff33 e5140004 e5143008 (e5933014) [ 33.954625] ---[ end trace f849bead94eab7ea ]--- Fixes: 2ccea03 (usb: gadget: introduce UDC Class) Cc: # v3.1+ Signed-off-by: Felipe Balbi --- drivers/usb/gadget/udc/udc-core.c | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'drivers/usb/gadget') diff --git a/drivers/usb/gadget/udc/udc-core.c b/drivers/usb/gadget/udc/udc-core.c index f107bb60a5ab..f2054659f25b 100644 --- a/drivers/usb/gadget/udc/udc-core.c +++ b/drivers/usb/gadget/udc/udc-core.c @@ -507,6 +507,11 @@ static ssize_t usb_udc_softconn_store(struct device *dev, { struct usb_udc *udc = container_of(dev, struct usb_udc, dev); + if (!udc->driver) { + dev_err(dev, "soft-connect without a gadget driver\n"); + return -EOPNOTSUPP; + } + if (sysfs_streq(buf, "connect")) { usb_gadget_udc_start(udc->gadget, udc->driver); usb_gadget_connect(udc->gadget); -- cgit v1.2.3 From d12a8727171c770990c246f0682f0af7859bb245 Mon Sep 17 00:00:00 2001 From: Pavitrakumar Managutte Date: Wed, 22 Oct 2014 19:24:58 +0530 Subject: usb: gadget: function: Remove redundant usb_free_all_descriptors Removed usb_free_all_descriptors in the bind functions, which results in double-free corruption of the descriptors on error path. The usb descriptors are allocated by usb_assign_descriptors. Signed-off-by: Pavitrakumar Managutte Reviewed-by: Robert Baldyga Reviewed-by: Sebastian Andrzej Siewior Signed-off-by: Felipe Balbi --- drivers/usb/gadget/function/f_eem.c | 1 - drivers/usb/gadget/function/f_hid.c | 5 +++-- drivers/usb/gadget/function/f_ncm.c | 1 - drivers/usb/gadget/function/f_obex.c | 1 - drivers/usb/gadget/function/f_phonet.c | 2 +- drivers/usb/gadget/function/f_rndis.c | 5 +++-- drivers/usb/gadget/function/f_subset.c | 1 - drivers/usb/gadget/function/f_uac2.c | 10 ++++++---- 8 files changed, 13 insertions(+), 13 deletions(-) (limited to 'drivers/usb/gadget') diff --git a/drivers/usb/gadget/function/f_eem.c b/drivers/usb/gadget/function/f_eem.c index 4d8b236ea608..c9e90de5bdd9 100644 --- a/drivers/usb/gadget/function/f_eem.c +++ b/drivers/usb/gadget/function/f_eem.c @@ -325,7 +325,6 @@ static int eem_bind(struct usb_configuration *c, struct usb_function *f) return 0; fail: - usb_free_all_descriptors(f); if (eem->port.out_ep) eem->port.out_ep->driver_data = NULL; if (eem->port.in_ep) diff --git a/drivers/usb/gadget/function/f_hid.c b/drivers/usb/gadget/function/f_hid.c index a95290a1289f..59ab62c92b66 100644 --- a/drivers/usb/gadget/function/f_hid.c +++ b/drivers/usb/gadget/function/f_hid.c @@ -621,12 +621,14 @@ static int __init hidg_bind(struct usb_configuration *c, struct usb_function *f) dev = MKDEV(major, hidg->minor); status = cdev_add(&hidg->cdev, dev, 1); if (status) - goto fail; + goto fail_free_descs; device_create(hidg_class, NULL, dev, NULL, "%s%d", "hidg", hidg->minor); return 0; +fail_free_descs: + usb_free_all_descriptors(f); fail: ERROR(f->config->cdev, "hidg_bind FAILED\n"); if (hidg->req != NULL) { @@ -635,7 +637,6 @@ fail: usb_ep_free_request(hidg->in_ep, hidg->req); } - usb_free_all_descriptors(f); return status; } diff --git a/drivers/usb/gadget/function/f_ncm.c b/drivers/usb/gadget/function/f_ncm.c index 146f48cc65d7..16361b0a8b46 100644 --- a/drivers/usb/gadget/function/f_ncm.c +++ b/drivers/usb/gadget/function/f_ncm.c @@ -1461,7 +1461,6 @@ static int ncm_bind(struct usb_configuration *c, struct usb_function *f) return 0; fail: - usb_free_all_descriptors(f); if (ncm->notify_req) { kfree(ncm->notify_req->buf); usb_ep_free_request(ncm->notify, ncm->notify_req); diff --git a/drivers/usb/gadget/function/f_obex.c b/drivers/usb/gadget/function/f_obex.c index 1a1a490415f4..a1b79c53499c 100644 --- a/drivers/usb/gadget/function/f_obex.c +++ b/drivers/usb/gadget/function/f_obex.c @@ -397,7 +397,6 @@ static int obex_bind(struct usb_configuration *c, struct usb_function *f) return 0; fail: - usb_free_all_descriptors(f); /* we might as well release our claims on endpoints */ if (obex->port.out) obex->port.out->driver_data = NULL; diff --git a/drivers/usb/gadget/function/f_phonet.c b/drivers/usb/gadget/function/f_phonet.c index b9cfc1571d71..1ec8b7ffdccd 100644 --- a/drivers/usb/gadget/function/f_phonet.c +++ b/drivers/usb/gadget/function/f_phonet.c @@ -570,8 +570,8 @@ static int pn_bind(struct usb_configuration *c, struct usb_function *f) err_req: for (i = 0; i < phonet_rxq_size && fp->out_reqv[i]; i++) usb_ep_free_request(fp->out_ep, fp->out_reqv[i]); -err: usb_free_all_descriptors(f); +err: if (fp->out_ep) fp->out_ep->driver_data = NULL; if (fp->in_ep) diff --git a/drivers/usb/gadget/function/f_rndis.c b/drivers/usb/gadget/function/f_rndis.c index ddb09dc6d1f2..2f0517f5bae3 100644 --- a/drivers/usb/gadget/function/f_rndis.c +++ b/drivers/usb/gadget/function/f_rndis.c @@ -803,7 +803,7 @@ rndis_bind(struct usb_configuration *c, struct usb_function *f) if (rndis->manufacturer && rndis->vendorID && rndis_set_param_vendor(rndis->config, rndis->vendorID, rndis->manufacturer)) - goto fail; + goto fail_free_descs; /* NOTE: all that is done without knowing or caring about * the network link ... which is unavailable to this code @@ -817,10 +817,11 @@ rndis_bind(struct usb_configuration *c, struct usb_function *f) rndis->notify->name); return 0; +fail_free_descs: + usb_free_all_descriptors(f); fail: kfree(f->os_desc_table); f->os_desc_n = 0; - usb_free_all_descriptors(f); if (rndis->notify_req) { kfree(rndis->notify_req->buf); diff --git a/drivers/usb/gadget/function/f_subset.c b/drivers/usb/gadget/function/f_subset.c index 1ea8baf33333..e3dfa675ff06 100644 --- a/drivers/usb/gadget/function/f_subset.c +++ b/drivers/usb/gadget/function/f_subset.c @@ -380,7 +380,6 @@ geth_bind(struct usb_configuration *c, struct usb_function *f) return 0; fail: - usb_free_all_descriptors(f); /* we might as well release our claims on endpoints */ if (geth->port.out_ep) geth->port.out_ep->driver_data = NULL; diff --git a/drivers/usb/gadget/function/f_uac2.c b/drivers/usb/gadget/function/f_uac2.c index 9296e598428c..33e16658e5cf 100644 --- a/drivers/usb/gadget/function/f_uac2.c +++ b/drivers/usb/gadget/function/f_uac2.c @@ -1084,7 +1084,7 @@ afunc_bind(struct usb_configuration *cfg, struct usb_function *fn) prm->rbuf = kzalloc(prm->max_psize * USB_XFERS, GFP_KERNEL); if (!prm->rbuf) { prm->max_psize = 0; - goto err; + goto err_free_descs; } prm = &agdev->uac2.p_prm; @@ -1092,17 +1092,19 @@ afunc_bind(struct usb_configuration *cfg, struct usb_function *fn) prm->rbuf = kzalloc(prm->max_psize * USB_XFERS, GFP_KERNEL); if (!prm->rbuf) { prm->max_psize = 0; - goto err; + goto err_free_descs; } ret = alsa_uac2_init(agdev); if (ret) - goto err; + goto err_free_descs; return 0; + +err_free_descs: + usb_free_all_descriptors(fn); err: kfree(agdev->uac2.p_prm.rbuf); kfree(agdev->uac2.c_prm.rbuf); - usb_free_all_descriptors(fn); if (agdev->in_ep) agdev->in_ep->driver_data = NULL; if (agdev->out_ep) -- cgit v1.2.3 From 9b1763553a89b2a84881119eeabfccdb803bb468 Mon Sep 17 00:00:00 2001 From: Pavitrakumar Managutte Date: Wed, 22 Oct 2014 19:33:22 +0530 Subject: usb: gadget: function: Fixed the return value on error path Fixed the return value on failure. status variable is set to 0 at usb_assign_descriptors call and the same is returned on error which is incorrect. Acked-by: Sebastian Andrzej Siewior Signed-off-by: Pavitrakumar Managutte Signed-off-by: Felipe Balbi --- drivers/usb/gadget/function/f_rndis.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'drivers/usb/gadget') diff --git a/drivers/usb/gadget/function/f_rndis.c b/drivers/usb/gadget/function/f_rndis.c index 2f0517f5bae3..f13fc6a58565 100644 --- a/drivers/usb/gadget/function/f_rndis.c +++ b/drivers/usb/gadget/function/f_rndis.c @@ -802,8 +802,10 @@ rndis_bind(struct usb_configuration *c, struct usb_function *f) if (rndis->manufacturer && rndis->vendorID && rndis_set_param_vendor(rndis->config, rndis->vendorID, - rndis->manufacturer)) + rndis->manufacturer)) { + status = -EINVAL; goto fail_free_descs; + } /* NOTE: all that is done without knowing or caring about * the network link ... which is unavailable to this code -- cgit v1.2.3