From 734d5a5393ed8eedf70f13c7078cb4a6134f49f2 Mon Sep 17 00:00:00 2001 From: Jingoo Han Date: Thu, 17 Jul 2014 12:45:11 +0900 Subject: usb: dwc3: remove unnecessary OOM messages The site-specific OOM messages are unnecessary, because they duplicate the MM subsystem generic OOM message. Signed-off-by: Jingoo Han Signed-off-by: Felipe Balbi --- drivers/usb/dwc3/core.c | 9 +++------ drivers/usb/dwc3/dwc3-exynos.c | 4 +--- drivers/usb/dwc3/dwc3-omap.c | 4 +--- drivers/usb/dwc3/dwc3-pci.c | 4 +--- drivers/usb/dwc3/gadget.c | 11 ++--------- 5 files changed, 8 insertions(+), 24 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c index b769c1f..f2050e8 100644 --- a/drivers/usb/dwc3/core.c +++ b/drivers/usb/dwc3/core.c @@ -186,10 +186,8 @@ static int dwc3_alloc_event_buffers(struct dwc3 *dwc, unsigned length) dwc->ev_buffs = devm_kzalloc(dwc->dev, sizeof(*dwc->ev_buffs) * num, GFP_KERNEL); - if (!dwc->ev_buffs) { - dev_err(dwc->dev, "can't allocate event buffers array\n"); + if (!dwc->ev_buffs) return -ENOMEM; - } for (i = 0; i < num; i++) { struct dwc3_event_buffer *evt; @@ -639,10 +637,9 @@ static int dwc3_probe(struct platform_device *pdev) void *mem; mem = devm_kzalloc(dev, sizeof(*dwc) + DWC3_ALIGN_MASK, GFP_KERNEL); - if (!mem) { - dev_err(dev, "not enough memory\n"); + if (!mem) return -ENOMEM; - } + dwc = PTR_ALIGN(mem, DWC3_ALIGN_MASK + 1); dwc->mem = mem; dwc->dev = dev; diff --git a/drivers/usb/dwc3/dwc3-exynos.c b/drivers/usb/dwc3/dwc3-exynos.c index f9fb8ad..3951a65 100644 --- a/drivers/usb/dwc3/dwc3-exynos.c +++ b/drivers/usb/dwc3/dwc3-exynos.c @@ -113,10 +113,8 @@ static int dwc3_exynos_probe(struct platform_device *pdev) int ret; exynos = devm_kzalloc(dev, sizeof(*exynos), GFP_KERNEL); - if (!exynos) { - dev_err(dev, "not enough memory\n"); + if (!exynos) return -ENOMEM; - } /* * Right now device-tree probed devices don't get dma_mask set. diff --git a/drivers/usb/dwc3/dwc3-omap.c b/drivers/usb/dwc3/dwc3-omap.c index ef4936f..76345ac 100644 --- a/drivers/usb/dwc3/dwc3-omap.c +++ b/drivers/usb/dwc3/dwc3-omap.c @@ -481,10 +481,8 @@ static int dwc3_omap_probe(struct platform_device *pdev) } omap = devm_kzalloc(dev, sizeof(*omap), GFP_KERNEL); - if (!omap) { - dev_err(dev, "not enough memory\n"); + if (!omap) return -ENOMEM; - } platform_set_drvdata(pdev, omap); diff --git a/drivers/usb/dwc3/dwc3-pci.c b/drivers/usb/dwc3/dwc3-pci.c index a60bab7..436fb08 100644 --- a/drivers/usb/dwc3/dwc3-pci.c +++ b/drivers/usb/dwc3/dwc3-pci.c @@ -103,10 +103,8 @@ static int dwc3_pci_probe(struct pci_dev *pci, struct device *dev = &pci->dev; glue = devm_kzalloc(dev, sizeof(*glue), GFP_KERNEL); - if (!glue) { - dev_err(dev, "not enough memory\n"); + if (!glue) return -ENOMEM; - } glue->dev = dev; diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c index 349cacc..307b513 100644 --- a/drivers/usb/dwc3/gadget.c +++ b/drivers/usb/dwc3/gadget.c @@ -789,13 +789,10 @@ static struct usb_request *dwc3_gadget_ep_alloc_request(struct usb_ep *ep, { struct dwc3_request *req; struct dwc3_ep *dep = to_dwc3_ep(ep); - struct dwc3 *dwc = dep->dwc; req = kzalloc(sizeof(*req), gfp_flags); - if (!req) { - dev_err(dwc->dev, "not enough memory\n"); + if (!req) return NULL; - } req->epnum = dep->number; req->dep = dep; @@ -1743,11 +1740,8 @@ static int dwc3_gadget_init_hw_endpoints(struct dwc3 *dwc, u8 epnum = (i << 1) | (!!direction); dep = kzalloc(sizeof(*dep), GFP_KERNEL); - if (!dep) { - dev_err(dwc->dev, "can't allocate endpoint %d\n", - epnum); + if (!dep) return -ENOMEM; - } dep->dwc = dwc; dep->number = epnum; @@ -2759,7 +2753,6 @@ int dwc3_gadget_init(struct dwc3 *dwc) dwc->setup_buf = kzalloc(DWC3_EP0_BOUNCE_SIZE, GFP_KERNEL); if (!dwc->setup_buf) { - dev_err(dwc->dev, "failed to allocate setup buffer\n"); ret = -ENOMEM; goto err2; } -- cgit v1.1 From 17c128e8c8b06138bb088e48be5a89c27257d405 Mon Sep 17 00:00:00 2001 From: Wei Yongjun Date: Sun, 20 Jul 2014 20:30:14 +0800 Subject: usb: gadget: Remove redundant dev_err call in r8a66597_sudmac_ioremap() There is a error message within devm_ioremap_resource already, so remove the dev_err call to avoid redundant error message. Acked-by: Laurent Pinchart Signed-off-by: Wei Yongjun Signed-off-by: Felipe Balbi --- drivers/usb/gadget/udc/r8a66597-udc.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/gadget/udc/r8a66597-udc.c b/drivers/usb/gadget/udc/r8a66597-udc.c index 4600842..68a7d20 100644 --- a/drivers/usb/gadget/udc/r8a66597-udc.c +++ b/drivers/usb/gadget/udc/r8a66597-udc.c @@ -1846,10 +1846,8 @@ static int r8a66597_sudmac_ioremap(struct r8a66597 *r8a66597, res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "sudmac"); r8a66597->sudmac_reg = devm_ioremap_resource(&pdev->dev, res); - if (IS_ERR(r8a66597->sudmac_reg)) { - dev_err(&pdev->dev, "ioremap error(sudmac).\n"); + if (IS_ERR(r8a66597->sudmac_reg)) return PTR_ERR(r8a66597->sudmac_reg); - } return 0; } -- cgit v1.1 From 0dafc3d94596522787e216711d305add1c1dce99 Mon Sep 17 00:00:00 2001 From: Jingoo Han Date: Mon, 4 Aug 2014 10:44:31 +0900 Subject: usb: phy: samsung: Remove unnecessary lines of register bit definitions Remove unnecessary lines of register bit definitions in order to enhance the readability. In this case, there are lines per register offset definitions. There is no functional change. Signed-off-by: Jingoo Han Signed-off-by: Felipe Balbi --- drivers/usb/phy/phy-samsung-usb.h | 36 ++---------------------------------- 1 file changed, 2 insertions(+), 34 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/phy/phy-samsung-usb.h b/drivers/usb/phy/phy-samsung-usb.h index 68771bf..4eef455 100644 --- a/drivers/usb/phy/phy-samsung-usb.h +++ b/drivers/usb/phy/phy-samsung-usb.h @@ -21,7 +21,6 @@ /* Register definitions */ #define SAMSUNG_PHYPWR (0x00) - #define PHYPWR_NORMAL_MASK (0x19 << 0) #define PHYPWR_OTG_DISABLE (0x1 << 4) #define PHYPWR_ANALOG_POWERDOWN (0x1 << 3) @@ -31,7 +30,6 @@ #define PHYPWR_SLEEP_PHY0 (0x1 << 5) #define SAMSUNG_PHYCLK (0x04) - #define PHYCLK_MODE_USB11 (0x1 << 6) #define PHYCLK_EXT_OSC (0x1 << 5) #define PHYCLK_COMMON_ON_N (0x1 << 4) @@ -42,7 +40,6 @@ #define PHYCLK_CLKSEL_24M (0x3 << 0) #define SAMSUNG_RSTCON (0x08) - #define RSTCON_PHYLINK_SWRST (0x1 << 2) #define RSTCON_HLINK_SWRST (0x1 << 1) #define RSTCON_SWRST (0x1 << 0) @@ -50,26 +47,20 @@ /* EXYNOS4X12 */ #define EXYNOS4X12_PHY_HSIC_CTRL0 (0x04) #define EXYNOS4X12_PHY_HSIC_CTRL1 (0x08) - #define PHYPWR_NORMAL_MASK_HSIC1 (0x7 << 12) #define PHYPWR_NORMAL_MASK_HSIC0 (0x7 << 9) #define PHYPWR_NORMAL_MASK_PHY1 (0x7 << 6) - #define RSTCON_HOSTPHY_SWRST (0xf << 3) /* EXYNOS5 */ #define EXYNOS5_PHY_HOST_CTRL0 (0x00) - #define HOST_CTRL0_PHYSWRSTALL (0x1 << 31) - #define HOST_CTRL0_REFCLKSEL_MASK (0x3 << 19) #define HOST_CTRL0_REFCLKSEL_XTAL (0x0 << 19) #define HOST_CTRL0_REFCLKSEL_EXTL (0x1 << 19) #define HOST_CTRL0_REFCLKSEL_CLKCORE (0x2 << 19) - #define HOST_CTRL0_FSEL_MASK (0x7 << 16) #define HOST_CTRL0_FSEL(_x) ((_x) << 16) - #define FSEL_CLKSEL_50M (0x7) #define FSEL_CLKSEL_24M (0x5) #define FSEL_CLKSEL_20M (0x4) @@ -77,7 +68,6 @@ #define FSEL_CLKSEL_12M (0x2) #define FSEL_CLKSEL_10M (0x1) #define FSEL_CLKSEL_9600K (0x0) - #define HOST_CTRL0_TESTBURNIN (0x1 << 11) #define HOST_CTRL0_RETENABLE (0x1 << 10) #define HOST_CTRL0_COMMONON_N (0x1 << 9) @@ -98,10 +88,8 @@ #define EXYNOS5_PHY_HSIC_CTRL2 (0x20) #define EXYNOS5_PHY_HSIC_TUNE2 (0x24) - #define HSIC_CTRL_REFCLKSEL_MASK (0x3 << 23) #define HSIC_CTRL_REFCLKSEL (0x2 << 23) - #define HSIC_CTRL_REFCLKDIV_MASK (0x7f << 16) #define HSIC_CTRL_REFCLKDIV(_x) ((_x) << 16) #define HSIC_CTRL_REFCLKDIV_12 (0x24 << 16) @@ -109,7 +97,6 @@ #define HSIC_CTRL_REFCLKDIV_16 (0x1a << 16) #define HSIC_CTRL_REFCLKDIV_19_2 (0x15 << 16) #define HSIC_CTRL_REFCLKDIV_20 (0x14 << 16) - #define HSIC_CTRL_SIDDQ (0x1 << 6) #define HSIC_CTRL_FORCESLEEP (0x1 << 5) #define HSIC_CTRL_FORCESUSPEND (0x1 << 4) @@ -118,36 +105,29 @@ #define HSIC_CTRL_PHYSWRST (0x1 << 0) #define EXYNOS5_PHY_HOST_EHCICTRL (0x30) - #define HOST_EHCICTRL_ENAINCRXALIGN (0x1 << 29) #define HOST_EHCICTRL_ENAINCR4 (0x1 << 28) #define HOST_EHCICTRL_ENAINCR8 (0x1 << 27) #define HOST_EHCICTRL_ENAINCR16 (0x1 << 26) #define EXYNOS5_PHY_HOST_OHCICTRL (0x34) - #define HOST_OHCICTRL_SUSPLGCY (0x1 << 3) #define HOST_OHCICTRL_APPSTARTCLK (0x1 << 2) #define HOST_OHCICTRL_CNTSEL (0x1 << 1) #define HOST_OHCICTRL_CLKCKTRST (0x1 << 0) #define EXYNOS5_PHY_OTG_SYS (0x38) - #define OTG_SYS_PHYLINK_SWRESET (0x1 << 14) #define OTG_SYS_LINKSWRST_UOTG (0x1 << 13) #define OTG_SYS_PHY0_SWRST (0x1 << 12) - #define OTG_SYS_REFCLKSEL_MASK (0x3 << 9) #define OTG_SYS_REFCLKSEL_XTAL (0x0 << 9) #define OTG_SYS_REFCLKSEL_EXTL (0x1 << 9) #define OTG_SYS_REFCLKSEL_CLKCORE (0x2 << 9) - #define OTG_SYS_IDPULLUP_UOTG (0x1 << 8) #define OTG_SYS_COMMON_ON (0x1 << 7) - #define OTG_SYS_FSEL_MASK (0x7 << 4) #define OTG_SYS_FSEL(_x) ((_x) << 4) - #define OTG_SYS_FORCESLEEP (0x1 << 3) #define OTG_SYS_OTGDISABLE (0x1 << 2) #define OTG_SYS_SIDDQ_UOTG (0x1 << 1) @@ -157,13 +137,11 @@ /* EXYNOS5: USB 3.0 DRD */ #define EXYNOS5_DRD_LINKSYSTEM (0x04) - #define LINKSYSTEM_FLADJ_MASK (0x3f << 1) #define LINKSYSTEM_FLADJ(_x) ((_x) << 1) #define LINKSYSTEM_XHCI_VERSION_CONTROL (0x1 << 27) #define EXYNOS5_DRD_PHYUTMI (0x08) - #define PHYUTMI_OTGDISABLE (0x1 << 6) #define PHYUTMI_FORCESUSPEND (0x1 << 1) #define PHYUTMI_FORCESLEEP (0x1 << 0) @@ -171,68 +149,58 @@ #define EXYNOS5_DRD_PHYPIPE (0x0c) #define EXYNOS5_DRD_PHYCLKRST (0x10) - #define PHYCLKRST_SSC_REFCLKSEL_MASK (0xff << 23) #define PHYCLKRST_SSC_REFCLKSEL(_x) ((_x) << 23) - #define PHYCLKRST_SSC_RANGE_MASK (0x03 << 21) #define PHYCLKRST_SSC_RANGE(_x) ((_x) << 21) - #define PHYCLKRST_SSC_EN (0x1 << 20) #define PHYCLKRST_REF_SSP_EN (0x1 << 19) #define PHYCLKRST_REF_CLKDIV2 (0x1 << 18) - #define PHYCLKRST_MPLL_MULTIPLIER_MASK (0x7f << 11) #define PHYCLKRST_MPLL_MULTIPLIER_100MHZ_REF (0x19 << 11) #define PHYCLKRST_MPLL_MULTIPLIER_50M_REF (0x02 << 11) #define PHYCLKRST_MPLL_MULTIPLIER_24MHZ_REF (0x68 << 11) #define PHYCLKRST_MPLL_MULTIPLIER_20MHZ_REF (0x7d << 11) #define PHYCLKRST_MPLL_MULTIPLIER_19200KHZ_REF (0x02 << 11) - #define PHYCLKRST_FSEL_MASK (0x3f << 5) #define PHYCLKRST_FSEL(_x) ((_x) << 5) #define PHYCLKRST_FSEL_PAD_100MHZ (0x27 << 5) #define PHYCLKRST_FSEL_PAD_24MHZ (0x2a << 5) #define PHYCLKRST_FSEL_PAD_20MHZ (0x31 << 5) #define PHYCLKRST_FSEL_PAD_19_2MHZ (0x38 << 5) - #define PHYCLKRST_RETENABLEN (0x1 << 4) - #define PHYCLKRST_REFCLKSEL_MASK (0x03 << 2) #define PHYCLKRST_REFCLKSEL_PAD_REFCLK (0x2 << 2) #define PHYCLKRST_REFCLKSEL_EXT_REFCLK (0x3 << 2) - #define PHYCLKRST_PORTRESET (0x1 << 1) #define PHYCLKRST_COMMONONN (0x1 << 0) #define EXYNOS5_DRD_PHYREG0 (0x14) + #define EXYNOS5_DRD_PHYREG1 (0x18) #define EXYNOS5_DRD_PHYPARAM0 (0x1c) - #define PHYPARAM0_REF_USE_PAD (0x1 << 31) #define PHYPARAM0_REF_LOSLEVEL_MASK (0x1f << 26) #define PHYPARAM0_REF_LOSLEVEL (0x9 << 26) #define EXYNOS5_DRD_PHYPARAM1 (0x20) - #define PHYPARAM1_PCS_TXDEEMPH_MASK (0x1f << 0) #define PHYPARAM1_PCS_TXDEEMPH (0x1c) #define EXYNOS5_DRD_PHYTERM (0x24) #define EXYNOS5_DRD_PHYTEST (0x28) - #define PHYTEST_POWERDOWN_SSP (0x1 << 3) #define PHYTEST_POWERDOWN_HSP (0x1 << 2) #define EXYNOS5_DRD_PHYADP (0x2c) #define EXYNOS5_DRD_PHYBATCHG (0x30) - #define PHYBATCHG_UTMI_CLKSEL (0x1 << 2) #define EXYNOS5_DRD_PHYRESUME (0x34) + #define EXYNOS5_DRD_LINKPORT (0x44) #ifndef MHZ -- cgit v1.1 From 8f90afd918886f10ac82aded9a30edfd80f2f69b Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Wed, 20 Aug 2014 13:38:18 -0500 Subject: usb: phy: msm: mark msm_otg_mode_fops static that declaration is only used inside this driver, marking it static. Signed-off-by: Felipe Balbi --- drivers/usb/phy/phy-msm-usb.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/usb') diff --git a/drivers/usb/phy/phy-msm-usb.c b/drivers/usb/phy/phy-msm-usb.c index e4108ee..df8e1ba 100644 --- a/drivers/usb/phy/phy-msm-usb.c +++ b/drivers/usb/phy/phy-msm-usb.c @@ -1394,7 +1394,7 @@ out: return status; } -const struct file_operations msm_otg_mode_fops = { +static const struct file_operations msm_otg_mode_fops = { .open = msm_otg_mode_open, .read = seq_read, .write = msm_otg_mode_write, -- cgit v1.1 From 5d73abf2a77a090ca4c920ac99c8ec0e272398a9 Mon Sep 17 00:00:00 2001 From: Andrzej Pietrasiewicz Date: Tue, 22 Jul 2014 19:58:29 +0200 Subject: usb: gadget: audio: Use container_of to free audio_dev Eliminate static struct *agdev_g from f_uac2.c. It is used for freeing its memory, but the same address can be found by calling container_of in afunc_unbind(). This implies eliminating uac2_unbind_config(). The audio_config_driver in audio.c does not have its unbind method any more. It has been used only when uac2 is used, so uac2 itself can handle unbinding in afunc_unbind(). Tested-by: Sebastian Reimers Signed-off-by: Andrzej Pietrasiewicz Signed-off-by: Felipe Balbi --- drivers/usb/gadget/function/f_uac2.c | 35 ++++++++++++++--------------------- drivers/usb/gadget/legacy/audio.c | 3 --- 2 files changed, 14 insertions(+), 24 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/gadget/function/f_uac2.c b/drivers/usb/gadget/function/f_uac2.c index 3ed89ec..9161561 100644 --- a/drivers/usb/gadget/function/f_uac2.c +++ b/drivers/usb/gadget/function/f_uac2.c @@ -149,8 +149,6 @@ struct audio_dev { struct snd_uac2_chip uac2; }; -static struct audio_dev *agdev_g; - static inline struct audio_dev *func_to_agdev(struct usb_function *f) { @@ -1039,6 +1037,7 @@ afunc_unbind(struct usb_configuration *cfg, struct usb_function *fn) agdev->in_ep->driver_data = NULL; if (agdev->out_ep) agdev->out_ep->driver_data = NULL; + kfree(agdev); } static int @@ -1311,10 +1310,11 @@ afunc_setup(struct usb_function *fn, const struct usb_ctrlrequest *cr) static int audio_bind_config(struct usb_configuration *cfg) { + struct audio_dev *agdev; int res; - agdev_g = kzalloc(sizeof *agdev_g, GFP_KERNEL); - if (agdev_g == NULL) + agdev = kzalloc(sizeof(*agdev), GFP_KERNEL); + if (agdev == NULL) return -ENOMEM; res = usb_string_ids_tab(cfg->cdev, strings_fn); @@ -1333,14 +1333,14 @@ static int audio_bind_config(struct usb_configuration *cfg) std_as_in_if0_desc.iInterface = strings_fn[STR_AS_IN_ALT0].id; std_as_in_if1_desc.iInterface = strings_fn[STR_AS_IN_ALT1].id; - agdev_g->func.name = "uac2_func"; - agdev_g->func.strings = fn_strings; - agdev_g->func.bind = afunc_bind; - agdev_g->func.unbind = afunc_unbind; - agdev_g->func.set_alt = afunc_set_alt; - agdev_g->func.get_alt = afunc_get_alt; - agdev_g->func.disable = afunc_disable; - agdev_g->func.setup = afunc_setup; + agdev->func.name = "uac2_func"; + agdev->func.strings = fn_strings; + agdev->func.bind = afunc_bind; + agdev->func.unbind = afunc_unbind; + agdev->func.set_alt = afunc_set_alt; + agdev->func.get_alt = afunc_get_alt; + agdev->func.disable = afunc_disable; + agdev->func.setup = afunc_setup; /* Initialize the configurable parameters */ usb_out_it_desc.bNrChannels = num_channels(c_chmask); @@ -1359,16 +1359,9 @@ static int audio_bind_config(struct usb_configuration *cfg) snprintf(clksrc_in, sizeof(clksrc_in), "%uHz", p_srate); snprintf(clksrc_out, sizeof(clksrc_out), "%uHz", c_srate); - res = usb_add_function(cfg, &agdev_g->func); + res = usb_add_function(cfg, &agdev->func); if (res < 0) - kfree(agdev_g); + kfree(agdev); return res; } - -static void -uac2_unbind_config(struct usb_configuration *cfg) -{ - kfree(agdev_g); - agdev_g = NULL; -} diff --git a/drivers/usb/gadget/legacy/audio.c b/drivers/usb/gadget/legacy/audio.c index 6eb695e..3c23283 100644 --- a/drivers/usb/gadget/legacy/audio.c +++ b/drivers/usb/gadget/legacy/audio.c @@ -126,9 +126,6 @@ static struct usb_configuration audio_config_driver = { .bConfigurationValue = 1, /* .iConfiguration = DYNAMIC */ .bmAttributes = USB_CONFIG_ATT_SELFPOWER, -#ifndef CONFIG_GADGET_UAC1 - .unbind = uac2_unbind_config, -#endif }; /*-------------------------------------------------------------------------*/ -- cgit v1.1 From f8f93d244afad804e09595fcb14320fe2896fef5 Mon Sep 17 00:00:00 2001 From: Andrzej Pietrasiewicz Date: Tue, 22 Jul 2014 19:58:30 +0200 Subject: usb: gadget: f_uac2: convert to new function interface with backward compatibility Converting uac2 to the new function interface requires converting the USB uac2's function code and its users. This patch converts the f_uac2.c to the new function interface. The file is now compiled into a separate usb_f_uac2.ko module. The old function interface is provided by means of a preprocessor conditional directives. After all users are converted, the old interface can be removed. Tested-by: Sebastian Reimers Signed-off-by: Andrzej Pietrasiewicz Signed-off-by: Felipe Balbi --- drivers/usb/gadget/Kconfig | 3 + drivers/usb/gadget/function/Makefile | 2 + drivers/usb/gadget/function/f_uac2.c | 244 +++++++++++++++++++++++++++++------ drivers/usb/gadget/function/u_uac2.h | 32 +++++ drivers/usb/gadget/legacy/audio.c | 1 + 5 files changed, 242 insertions(+), 40 deletions(-) create mode 100644 drivers/usb/gadget/function/u_uac2.h (limited to 'drivers/usb') diff --git a/drivers/usb/gadget/Kconfig b/drivers/usb/gadget/Kconfig index 5c822af..ea8ebce 100644 --- a/drivers/usb/gadget/Kconfig +++ b/drivers/usb/gadget/Kconfig @@ -181,6 +181,9 @@ config USB_F_MASS_STORAGE config USB_F_FS tristate +config USB_F_UAC2 + tristate + choice tristate "USB Gadget Drivers" default USB_ETH diff --git a/drivers/usb/gadget/function/Makefile b/drivers/usb/gadget/function/Makefile index 6d91f21..20775a8 100644 --- a/drivers/usb/gadget/function/Makefile +++ b/drivers/usb/gadget/function/Makefile @@ -32,3 +32,5 @@ usb_f_mass_storage-y := f_mass_storage.o storage_common.o obj-$(CONFIG_USB_F_MASS_STORAGE)+= usb_f_mass_storage.o usb_f_fs-y := f_fs.o obj-$(CONFIG_USB_F_FS) += usb_f_fs.o +usb_f_uac2-y := f_uac2.o +obj-$(CONFIG_USB_F_UAC2) += usb_f_uac2.o diff --git a/drivers/usb/gadget/function/f_uac2.c b/drivers/usb/gadget/function/f_uac2.c index 9161561..29b477f 100644 --- a/drivers/usb/gadget/function/f_uac2.c +++ b/drivers/usb/gadget/function/f_uac2.c @@ -20,6 +20,10 @@ #include #include +#include "u_uac2.h" + +#ifdef USB_FUAC2_INCLUDED + /* Playback(USB-IN) Default Stereo - Fl/Fr */ static int p_chmask = 0x3; module_param(p_chmask, uint, S_IRUGO); @@ -50,6 +54,8 @@ static int c_ssize = 2; module_param(c_ssize, uint, S_IRUGO); MODULE_PARM_DESC(c_ssize, "Capture Sample Size(bytes)"); +#endif + /* Keep everyone on toes */ #define USB_XFERS 2 @@ -340,6 +346,22 @@ static int uac2_pcm_open(struct snd_pcm_substream *substream) { struct snd_uac2_chip *uac2 = snd_pcm_substream_chip(substream); struct snd_pcm_runtime *runtime = substream->runtime; +#ifndef USB_FUAC2_INCLUDED + struct audio_dev *audio_dev; + struct f_uac2_opts *opts; + int p_ssize, c_ssize; + int p_srate, c_srate; + int p_chmask, c_chmask; + + audio_dev = uac2_to_agdev(uac2); + opts = container_of(audio_dev->func.fi, struct f_uac2_opts, func_inst); + p_ssize = opts->p_ssize; + c_ssize = opts->c_ssize; + p_srate = opts->p_srate; + c_srate = opts->c_srate; + p_chmask = opts->p_chmask; + c_chmask = opts->c_chmask; +#endif runtime->hw = uac2_pcm_hardware; @@ -409,7 +431,19 @@ static int snd_uac2_probe(struct platform_device *pdev) struct snd_uac2_chip *uac2 = pdev_to_uac2(pdev); struct snd_card *card; struct snd_pcm *pcm; +#ifndef USB_FUAC2_INCLUDED + struct audio_dev *audio_dev; + struct f_uac2_opts *opts; +#endif int err; +#ifndef USB_FUAC2_INCLUDED + int p_chmask, c_chmask; + + audio_dev = uac2_to_agdev(uac2); + opts = container_of(audio_dev->func.fi, struct f_uac2_opts, func_inst); + p_chmask = opts->p_chmask; + c_chmask = opts->c_chmask; +#endif /* Choose any slot, with no id */ err = snd_card_new(&pdev->dev, -1, NULL, THIS_MODULE, 0, &card); @@ -917,7 +951,7 @@ free_ep(struct uac2_rtd_params *prm, struct usb_ep *ep) "%s:%d Error!\n", __func__, __LINE__); } -static int __init +static int afunc_bind(struct usb_configuration *cfg, struct usb_function *fn) { struct audio_dev *agdev = func_to_agdev(fn); @@ -925,8 +959,50 @@ afunc_bind(struct usb_configuration *cfg, struct usb_function *fn) struct usb_composite_dev *cdev = cfg->cdev; struct usb_gadget *gadget = cdev->gadget; struct uac2_rtd_params *prm; +#ifndef USB_FUAC2_INCLUDED + struct f_uac2_opts *uac2_opts; +#endif int ret; +#ifndef USB_FUAC2_INCLUDED + uac2_opts = container_of(fn->fi, struct f_uac2_opts, func_inst); +#endif + + ret = usb_string_ids_tab(cfg->cdev, strings_fn); + if (ret) + return ret; + iad_desc.iFunction = strings_fn[STR_ASSOC].id; + std_ac_if_desc.iInterface = strings_fn[STR_IF_CTRL].id; + in_clk_src_desc.iClockSource = strings_fn[STR_CLKSRC_IN].id; + out_clk_src_desc.iClockSource = strings_fn[STR_CLKSRC_OUT].id; + usb_out_it_desc.iTerminal = strings_fn[STR_USB_IT].id; + io_in_it_desc.iTerminal = strings_fn[STR_IO_IT].id; + usb_in_ot_desc.iTerminal = strings_fn[STR_USB_OT].id; + io_out_ot_desc.iTerminal = strings_fn[STR_IO_OT].id; + std_as_out_if0_desc.iInterface = strings_fn[STR_AS_OUT_ALT0].id; + std_as_out_if1_desc.iInterface = strings_fn[STR_AS_OUT_ALT1].id; + std_as_in_if0_desc.iInterface = strings_fn[STR_AS_IN_ALT0].id; + std_as_in_if1_desc.iInterface = strings_fn[STR_AS_IN_ALT1].id; + +#ifndef USB_FUAC2_INCLUDED + /* Initialize the configurable parameters */ + usb_out_it_desc.bNrChannels = num_channels(uac2_opts->c_chmask); + usb_out_it_desc.bmChannelConfig = cpu_to_le32(uac2_opts->c_chmask); + io_in_it_desc.bNrChannels = num_channels(uac2_opts->p_chmask); + io_in_it_desc.bmChannelConfig = cpu_to_le32(uac2_opts->p_chmask); + as_out_hdr_desc.bNrChannels = num_channels(uac2_opts->c_chmask); + as_out_hdr_desc.bmChannelConfig = cpu_to_le32(uac2_opts->c_chmask); + as_in_hdr_desc.bNrChannels = num_channels(uac2_opts->p_chmask); + as_in_hdr_desc.bmChannelConfig = cpu_to_le32(uac2_opts->p_chmask); + as_out_fmt1_desc.bSubslotSize = uac2_opts->c_ssize; + as_out_fmt1_desc.bBitResolution = uac2_opts->c_ssize * 8; + as_in_fmt1_desc.bSubslotSize = uac2_opts->p_ssize; + as_in_fmt1_desc.bBitResolution = uac2_opts->p_ssize * 8; + + snprintf(clksrc_in, sizeof(clksrc_in), "%uHz", uac2_opts->p_srate); + snprintf(clksrc_out, sizeof(clksrc_out), "%uHz", uac2_opts->c_srate); +#endif + ret = usb_interface_id(cfg, fn); if (ret < 0) { dev_err(&uac2->pdev.dev, @@ -1018,28 +1094,6 @@ err: return -EINVAL; } -static void -afunc_unbind(struct usb_configuration *cfg, struct usb_function *fn) -{ - struct audio_dev *agdev = func_to_agdev(fn); - struct uac2_rtd_params *prm; - - alsa_uac2_exit(agdev); - - prm = &agdev->uac2.p_prm; - kfree(prm->rbuf); - - prm = &agdev->uac2.c_prm; - kfree(prm->rbuf); - usb_free_all_descriptors(fn); - - if (agdev->in_ep) - agdev->in_ep->driver_data = NULL; - if (agdev->out_ep) - agdev->out_ep->driver_data = NULL; - kfree(agdev); -} - static int afunc_set_alt(struct usb_function *fn, unsigned intf, unsigned alt) { @@ -1163,12 +1217,22 @@ in_rq_cur(struct usb_function *fn, const struct usb_ctrlrequest *cr) struct usb_request *req = fn->config->cdev->req; struct audio_dev *agdev = func_to_agdev(fn); struct snd_uac2_chip *uac2 = &agdev->uac2; +#ifndef USB_FUAC2_INCLUDED + struct f_uac2_opts *opts; +#endif u16 w_length = le16_to_cpu(cr->wLength); u16 w_index = le16_to_cpu(cr->wIndex); u16 w_value = le16_to_cpu(cr->wValue); u8 entity_id = (w_index >> 8) & 0xff; u8 control_selector = w_value >> 8; int value = -EOPNOTSUPP; +#ifndef USB_FUAC2_INCLUDED + int p_srate, c_srate; + + opts = container_of(agdev->func.fi, struct f_uac2_opts, func_inst); + p_srate = opts->p_srate; + c_srate = opts->c_srate; +#endif if (control_selector == UAC2_CS_CONTROL_SAM_FREQ) { struct cntrl_cur_lay3 c; @@ -1198,6 +1262,9 @@ in_rq_range(struct usb_function *fn, const struct usb_ctrlrequest *cr) struct usb_request *req = fn->config->cdev->req; struct audio_dev *agdev = func_to_agdev(fn); struct snd_uac2_chip *uac2 = &agdev->uac2; +#ifndef USB_FUAC2_INCLUDED + struct f_uac2_opts *opts; +#endif u16 w_length = le16_to_cpu(cr->wLength); u16 w_index = le16_to_cpu(cr->wIndex); u16 w_value = le16_to_cpu(cr->wValue); @@ -1205,6 +1272,13 @@ in_rq_range(struct usb_function *fn, const struct usb_ctrlrequest *cr) u8 control_selector = w_value >> 8; struct cntrl_range_lay3 r; int value = -EOPNOTSUPP; +#ifndef USB_FUAC2_INCLUDED + int p_srate, c_srate; + + opts = container_of(agdev->func.fi, struct f_uac2_opts, func_inst); + p_srate = opts->p_srate; + c_srate = opts->c_srate; +#endif if (control_selector == UAC2_CS_CONTROL_SAM_FREQ) { if (entity_id == USB_IN_CLK_ID) @@ -1308,6 +1382,30 @@ afunc_setup(struct usb_function *fn, const struct usb_ctrlrequest *cr) return value; } +#ifdef USB_FUAC2_INCLUDED + +static void +old_afunc_unbind(struct usb_configuration *cfg, struct usb_function *fn) +{ + struct audio_dev *agdev = func_to_agdev(fn); + struct uac2_rtd_params *prm; + + alsa_uac2_exit(agdev); + + prm = &agdev->uac2.p_prm; + kfree(prm->rbuf); + + prm = &agdev->uac2.c_prm; + kfree(prm->rbuf); + usb_free_all_descriptors(fn); + + if (agdev->in_ep) + agdev->in_ep->driver_data = NULL; + if (agdev->out_ep) + agdev->out_ep->driver_data = NULL; + kfree(agdev); +} + static int audio_bind_config(struct usb_configuration *cfg) { struct audio_dev *agdev; @@ -1317,26 +1415,10 @@ static int audio_bind_config(struct usb_configuration *cfg) if (agdev == NULL) return -ENOMEM; - res = usb_string_ids_tab(cfg->cdev, strings_fn); - if (res) - return res; - iad_desc.iFunction = strings_fn[STR_ASSOC].id; - std_ac_if_desc.iInterface = strings_fn[STR_IF_CTRL].id; - in_clk_src_desc.iClockSource = strings_fn[STR_CLKSRC_IN].id; - out_clk_src_desc.iClockSource = strings_fn[STR_CLKSRC_OUT].id; - usb_out_it_desc.iTerminal = strings_fn[STR_USB_IT].id; - io_in_it_desc.iTerminal = strings_fn[STR_IO_IT].id; - usb_in_ot_desc.iTerminal = strings_fn[STR_USB_OT].id; - io_out_ot_desc.iTerminal = strings_fn[STR_IO_OT].id; - std_as_out_if0_desc.iInterface = strings_fn[STR_AS_OUT_ALT0].id; - std_as_out_if1_desc.iInterface = strings_fn[STR_AS_OUT_ALT1].id; - std_as_in_if0_desc.iInterface = strings_fn[STR_AS_IN_ALT0].id; - std_as_in_if1_desc.iInterface = strings_fn[STR_AS_IN_ALT1].id; - agdev->func.name = "uac2_func"; agdev->func.strings = fn_strings; agdev->func.bind = afunc_bind; - agdev->func.unbind = afunc_unbind; + agdev->func.unbind = old_afunc_unbind; agdev->func.set_alt = afunc_set_alt; agdev->func.get_alt = afunc_get_alt; agdev->func.disable = afunc_disable; @@ -1365,3 +1447,85 @@ static int audio_bind_config(struct usb_configuration *cfg) return res; } + +#else + +static void afunc_free_inst(struct usb_function_instance *f) +{ + struct f_uac2_opts *opts; + + opts = container_of(f, struct f_uac2_opts, func_inst); + kfree(opts); +} + +static struct usb_function_instance *afunc_alloc_inst(void) +{ + struct f_uac2_opts *opts; + + opts = kzalloc(sizeof(*opts), GFP_KERNEL); + if (!opts) + return ERR_PTR(-ENOMEM); + + opts->func_inst.free_func_inst = afunc_free_inst; + + return &opts->func_inst; +} + +static void afunc_free(struct usb_function *f) +{ + struct audio_dev *agdev; + + agdev = func_to_agdev(f); + kfree(agdev); +} + +static void afunc_unbind(struct usb_configuration *c, struct usb_function *f) +{ + struct audio_dev *agdev = func_to_agdev(f); + struct uac2_rtd_params *prm; + + alsa_uac2_exit(agdev); + + prm = &agdev->uac2.p_prm; + kfree(prm->rbuf); + + prm = &agdev->uac2.c_prm; + kfree(prm->rbuf); + usb_free_all_descriptors(f); + + if (agdev->in_ep) + agdev->in_ep->driver_data = NULL; + if (agdev->out_ep) + agdev->out_ep->driver_data = NULL; +} + +struct usb_function *afunc_alloc(struct usb_function_instance *fi) +{ + struct audio_dev *agdev; + struct f_uac2_opts *opts; + + agdev = kzalloc(sizeof(*agdev), GFP_KERNEL); + if (agdev == NULL) + return ERR_PTR(-ENOMEM); + + opts = container_of(fi, struct f_uac2_opts, func_inst); + + agdev->func.name = "uac2_func"; + agdev->func.strings = fn_strings; + agdev->func.bind = afunc_bind; + agdev->func.unbind = afunc_unbind; + agdev->func.set_alt = afunc_set_alt; + agdev->func.get_alt = afunc_get_alt; + agdev->func.disable = afunc_disable; + agdev->func.setup = afunc_setup; + agdev->func.free_func = afunc_free; + + return &agdev->func; +} + +DECLARE_USB_FUNCTION_INIT(uac2, afunc_alloc_inst, afunc_alloc); +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Yadwinder Singh"); +MODULE_AUTHOR("Jaswinder Singh"); + +#endif diff --git a/drivers/usb/gadget/function/u_uac2.h b/drivers/usb/gadget/function/u_uac2.h new file mode 100644 index 0000000..290b83a --- /dev/null +++ b/drivers/usb/gadget/function/u_uac2.h @@ -0,0 +1,32 @@ +/* + * u_uac2.h + * + * Utility definitions for UAC2 function + * + * Copyright (c) 2014 Samsung Electronics Co., Ltd. + * http://www.samsung.com + * + * Author: Andrzej Pietrasiewicz + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef U_UAC2_H +#define U_UAC2_H + +#include + +struct f_uac2_opts { + struct usb_function_instance func_inst; + int p_chmask; + int p_srate; + int p_ssize; + int c_chmask; + int c_srate; + int c_ssize; + bool bound; +}; + +#endif diff --git a/drivers/usb/gadget/legacy/audio.c b/drivers/usb/gadget/legacy/audio.c index 3c23283..a41316b 100644 --- a/drivers/usb/gadget/legacy/audio.c +++ b/drivers/usb/gadget/legacy/audio.c @@ -45,6 +45,7 @@ static struct usb_gadget_strings *audio_strings[] = { #include "u_uac1.c" #include "f_uac1.c" #else +#define USB_FUAC2_INCLUDED #include "f_uac2.c" #endif -- cgit v1.1 From ad94ac0cfdb6e28a2b0da740d2482a7306e947c3 Mon Sep 17 00:00:00 2001 From: Andrzej Pietrasiewicz Date: Tue, 22 Jul 2014 19:58:31 +0200 Subject: usb: gadget: audio: convert to new interface of f_uac2 Use the new interface so that the old one can be removed. Tested-by: Sebastian Reimers Signed-off-by: Andrzej Pietrasiewicz Signed-off-by: Felipe Balbi --- drivers/usb/gadget/legacy/Kconfig | 1 + drivers/usb/gadget/legacy/audio.c | 85 ++++++++++++++++++++++++++++++++++++++- 2 files changed, 84 insertions(+), 2 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/gadget/legacy/Kconfig b/drivers/usb/gadget/legacy/Kconfig index aa376f0..172a6a3 100644 --- a/drivers/usb/gadget/legacy/Kconfig +++ b/drivers/usb/gadget/legacy/Kconfig @@ -54,6 +54,7 @@ config USB_AUDIO depends on SND select USB_LIBCOMPOSITE select SND_PCM + select USB_F_UAC2 if !GADGET_UAC1 help This Gadget Audio driver is compatible with USB Audio Class specification 2.0. It implements 1 AudioControl interface, diff --git a/drivers/usb/gadget/legacy/audio.c b/drivers/usb/gadget/legacy/audio.c index a41316b..159af03 100644 --- a/drivers/usb/gadget/legacy/audio.c +++ b/drivers/usb/gadget/legacy/audio.c @@ -21,6 +21,38 @@ USB_GADGET_COMPOSITE_OPTIONS(); +#ifndef CONFIG_GADGET_UAC1 +/* Playback(USB-IN) Default Stereo - Fl/Fr */ +static int p_chmask = 0x3; +module_param(p_chmask, uint, S_IRUGO); +MODULE_PARM_DESC(p_chmask, "Playback Channel Mask"); + +/* Playback Default 48 KHz */ +static int p_srate = 48000; +module_param(p_srate, uint, S_IRUGO); +MODULE_PARM_DESC(p_srate, "Playback Sampling Rate"); + +/* Playback Default 16bits/sample */ +static int p_ssize = 2; +module_param(p_ssize, uint, S_IRUGO); +MODULE_PARM_DESC(p_ssize, "Playback Sample Size(bytes)"); + +/* Capture(USB-OUT) Default Stereo - Fl/Fr */ +static int c_chmask = 0x3; +module_param(c_chmask, uint, S_IRUGO); +MODULE_PARM_DESC(c_chmask, "Capture Channel Mask"); + +/* Capture Default 64 KHz */ +static int c_srate = 64000; +module_param(c_srate, uint, S_IRUGO); +MODULE_PARM_DESC(c_srate, "Capture Sampling Rate"); + +/* Capture Default 16bits/sample */ +static int c_ssize = 2; +module_param(c_ssize, uint, S_IRUGO); +MODULE_PARM_DESC(c_ssize, "Capture Sample Size(bytes)"); +#endif + /* string IDs are assigned dynamically */ static struct usb_string strings_dev[] = { @@ -40,13 +72,17 @@ static struct usb_gadget_strings *audio_strings[] = { NULL, }; +#ifndef CONFIG_GADGET_UAC1 +static struct usb_function_instance *fi_uac2; +static struct usb_function *f_uac2; +#endif + #ifdef CONFIG_GADGET_UAC1 #include "u_uac1.h" #include "u_uac1.c" #include "f_uac1.c" #else -#define USB_FUAC2_INCLUDED -#include "f_uac2.c" +#include "u_uac2.h" #endif /*-------------------------------------------------------------------------*/ @@ -110,6 +146,10 @@ static const struct usb_descriptor_header *otg_desc[] = { static int __init audio_do_config(struct usb_configuration *c) { +#ifndef CONFIG_GADGET_UAC1 + int status; +#endif + /* FIXME alloc iConfiguration string, set it in c->strings */ if (gadget_is_otg(c->cdev->gadget)) { @@ -117,7 +157,21 @@ static int __init audio_do_config(struct usb_configuration *c) c->bmAttributes |= USB_CONFIG_ATT_WAKEUP; } +#ifdef CONFIG_GADGET_UAC1 audio_bind_config(c); +#else + f_uac2 = usb_get_function(fi_uac2); + if (IS_ERR(f_uac2)) { + status = PTR_ERR(f_uac2); + return status; + } + + status = usb_add_function(c, f_uac2); + if (status < 0) { + usb_put_function(f_uac2); + return status; + } +#endif return 0; } @@ -133,8 +187,27 @@ static struct usb_configuration audio_config_driver = { static int __init audio_bind(struct usb_composite_dev *cdev) { +#ifndef CONFIG_GADGET_UAC1 + struct f_uac2_opts *uac2_opts; +#endif int status; +#ifndef CONFIG_GADGET_UAC1 + fi_uac2 = usb_get_function_instance("uac2"); + if (IS_ERR(fi_uac2)) + return PTR_ERR(fi_uac2); +#endif + +#ifndef CONFIG_GADGET_UAC1 + uac2_opts = container_of(fi_uac2, struct f_uac2_opts, func_inst); + uac2_opts->p_chmask = p_chmask; + uac2_opts->p_srate = p_srate; + uac2_opts->p_ssize = p_ssize; + uac2_opts->c_chmask = c_chmask; + uac2_opts->c_srate = c_srate; + uac2_opts->c_ssize = c_ssize; +#endif + status = usb_string_ids_tab(cdev, strings_dev); if (status < 0) goto fail; @@ -150,6 +223,9 @@ static int __init audio_bind(struct usb_composite_dev *cdev) return 0; fail: +#ifndef CONFIG_GADGET_UAC1 + usb_put_function_instance(fi_uac2); +#endif return status; } @@ -157,6 +233,11 @@ static int __exit audio_unbind(struct usb_composite_dev *cdev) { #ifdef CONFIG_GADGET_UAC1 gaudio_cleanup(); +#else + if (!IS_ERR_OR_NULL(f_uac2)) + usb_put_function(f_uac2); + if (!IS_ERR_OR_NULL(fi_uac2)) + usb_put_function_instance(fi_uac2); #endif return 0; } -- cgit v1.1 From d980039a89fafe03829e4423d0da5d8fd119189d Mon Sep 17 00:00:00 2001 From: Andrzej Pietrasiewicz Date: Tue, 22 Jul 2014 19:58:32 +0200 Subject: usb: gadget: f_uac2: remove compatibility layer There are no users of the old interface left, so it can be removed. Tested-by: Sebastian Reimers Signed-off-by: Andrzej Pietrasiewicz Signed-off-by: Felipe Balbi --- drivers/usb/gadget/function/f_uac2.c | 124 ----------------------------------- 1 file changed, 124 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/gadget/function/f_uac2.c b/drivers/usb/gadget/function/f_uac2.c index 29b477f..2291037 100644 --- a/drivers/usb/gadget/function/f_uac2.c +++ b/drivers/usb/gadget/function/f_uac2.c @@ -22,40 +22,6 @@ #include "u_uac2.h" -#ifdef USB_FUAC2_INCLUDED - -/* Playback(USB-IN) Default Stereo - Fl/Fr */ -static int p_chmask = 0x3; -module_param(p_chmask, uint, S_IRUGO); -MODULE_PARM_DESC(p_chmask, "Playback Channel Mask"); - -/* Playback Default 48 KHz */ -static int p_srate = 48000; -module_param(p_srate, uint, S_IRUGO); -MODULE_PARM_DESC(p_srate, "Playback Sampling Rate"); - -/* Playback Default 16bits/sample */ -static int p_ssize = 2; -module_param(p_ssize, uint, S_IRUGO); -MODULE_PARM_DESC(p_ssize, "Playback Sample Size(bytes)"); - -/* Capture(USB-OUT) Default Stereo - Fl/Fr */ -static int c_chmask = 0x3; -module_param(c_chmask, uint, S_IRUGO); -MODULE_PARM_DESC(c_chmask, "Capture Channel Mask"); - -/* Capture Default 64 KHz */ -static int c_srate = 64000; -module_param(c_srate, uint, S_IRUGO); -MODULE_PARM_DESC(c_srate, "Capture Sampling Rate"); - -/* Capture Default 16bits/sample */ -static int c_ssize = 2; -module_param(c_ssize, uint, S_IRUGO); -MODULE_PARM_DESC(c_ssize, "Capture Sample Size(bytes)"); - -#endif - /* Keep everyone on toes */ #define USB_XFERS 2 @@ -346,7 +312,6 @@ static int uac2_pcm_open(struct snd_pcm_substream *substream) { struct snd_uac2_chip *uac2 = snd_pcm_substream_chip(substream); struct snd_pcm_runtime *runtime = substream->runtime; -#ifndef USB_FUAC2_INCLUDED struct audio_dev *audio_dev; struct f_uac2_opts *opts; int p_ssize, c_ssize; @@ -361,7 +326,6 @@ static int uac2_pcm_open(struct snd_pcm_substream *substream) c_srate = opts->c_srate; p_chmask = opts->p_chmask; c_chmask = opts->c_chmask; -#endif runtime->hw = uac2_pcm_hardware; @@ -431,19 +395,15 @@ static int snd_uac2_probe(struct platform_device *pdev) struct snd_uac2_chip *uac2 = pdev_to_uac2(pdev); struct snd_card *card; struct snd_pcm *pcm; -#ifndef USB_FUAC2_INCLUDED struct audio_dev *audio_dev; struct f_uac2_opts *opts; -#endif int err; -#ifndef USB_FUAC2_INCLUDED int p_chmask, c_chmask; audio_dev = uac2_to_agdev(uac2); opts = container_of(audio_dev->func.fi, struct f_uac2_opts, func_inst); p_chmask = opts->p_chmask; c_chmask = opts->c_chmask; -#endif /* Choose any slot, with no id */ err = snd_card_new(&pdev->dev, -1, NULL, THIS_MODULE, 0, &card); @@ -959,14 +919,10 @@ afunc_bind(struct usb_configuration *cfg, struct usb_function *fn) struct usb_composite_dev *cdev = cfg->cdev; struct usb_gadget *gadget = cdev->gadget; struct uac2_rtd_params *prm; -#ifndef USB_FUAC2_INCLUDED struct f_uac2_opts *uac2_opts; -#endif int ret; -#ifndef USB_FUAC2_INCLUDED uac2_opts = container_of(fn->fi, struct f_uac2_opts, func_inst); -#endif ret = usb_string_ids_tab(cfg->cdev, strings_fn); if (ret) @@ -984,7 +940,6 @@ afunc_bind(struct usb_configuration *cfg, struct usb_function *fn) std_as_in_if0_desc.iInterface = strings_fn[STR_AS_IN_ALT0].id; std_as_in_if1_desc.iInterface = strings_fn[STR_AS_IN_ALT1].id; -#ifndef USB_FUAC2_INCLUDED /* Initialize the configurable parameters */ usb_out_it_desc.bNrChannels = num_channels(uac2_opts->c_chmask); usb_out_it_desc.bmChannelConfig = cpu_to_le32(uac2_opts->c_chmask); @@ -1001,7 +956,6 @@ afunc_bind(struct usb_configuration *cfg, struct usb_function *fn) snprintf(clksrc_in, sizeof(clksrc_in), "%uHz", uac2_opts->p_srate); snprintf(clksrc_out, sizeof(clksrc_out), "%uHz", uac2_opts->c_srate); -#endif ret = usb_interface_id(cfg, fn); if (ret < 0) { @@ -1217,22 +1171,18 @@ in_rq_cur(struct usb_function *fn, const struct usb_ctrlrequest *cr) struct usb_request *req = fn->config->cdev->req; struct audio_dev *agdev = func_to_agdev(fn); struct snd_uac2_chip *uac2 = &agdev->uac2; -#ifndef USB_FUAC2_INCLUDED struct f_uac2_opts *opts; -#endif u16 w_length = le16_to_cpu(cr->wLength); u16 w_index = le16_to_cpu(cr->wIndex); u16 w_value = le16_to_cpu(cr->wValue); u8 entity_id = (w_index >> 8) & 0xff; u8 control_selector = w_value >> 8; int value = -EOPNOTSUPP; -#ifndef USB_FUAC2_INCLUDED int p_srate, c_srate; opts = container_of(agdev->func.fi, struct f_uac2_opts, func_inst); p_srate = opts->p_srate; c_srate = opts->c_srate; -#endif if (control_selector == UAC2_CS_CONTROL_SAM_FREQ) { struct cntrl_cur_lay3 c; @@ -1262,9 +1212,7 @@ in_rq_range(struct usb_function *fn, const struct usb_ctrlrequest *cr) struct usb_request *req = fn->config->cdev->req; struct audio_dev *agdev = func_to_agdev(fn); struct snd_uac2_chip *uac2 = &agdev->uac2; -#ifndef USB_FUAC2_INCLUDED struct f_uac2_opts *opts; -#endif u16 w_length = le16_to_cpu(cr->wLength); u16 w_index = le16_to_cpu(cr->wIndex); u16 w_value = le16_to_cpu(cr->wValue); @@ -1272,13 +1220,11 @@ in_rq_range(struct usb_function *fn, const struct usb_ctrlrequest *cr) u8 control_selector = w_value >> 8; struct cntrl_range_lay3 r; int value = -EOPNOTSUPP; -#ifndef USB_FUAC2_INCLUDED int p_srate, c_srate; opts = container_of(agdev->func.fi, struct f_uac2_opts, func_inst); p_srate = opts->p_srate; c_srate = opts->c_srate; -#endif if (control_selector == UAC2_CS_CONTROL_SAM_FREQ) { if (entity_id == USB_IN_CLK_ID) @@ -1382,74 +1328,6 @@ afunc_setup(struct usb_function *fn, const struct usb_ctrlrequest *cr) return value; } -#ifdef USB_FUAC2_INCLUDED - -static void -old_afunc_unbind(struct usb_configuration *cfg, struct usb_function *fn) -{ - struct audio_dev *agdev = func_to_agdev(fn); - struct uac2_rtd_params *prm; - - alsa_uac2_exit(agdev); - - prm = &agdev->uac2.p_prm; - kfree(prm->rbuf); - - prm = &agdev->uac2.c_prm; - kfree(prm->rbuf); - usb_free_all_descriptors(fn); - - if (agdev->in_ep) - agdev->in_ep->driver_data = NULL; - if (agdev->out_ep) - agdev->out_ep->driver_data = NULL; - kfree(agdev); -} - -static int audio_bind_config(struct usb_configuration *cfg) -{ - struct audio_dev *agdev; - int res; - - agdev = kzalloc(sizeof(*agdev), GFP_KERNEL); - if (agdev == NULL) - return -ENOMEM; - - agdev->func.name = "uac2_func"; - agdev->func.strings = fn_strings; - agdev->func.bind = afunc_bind; - agdev->func.unbind = old_afunc_unbind; - agdev->func.set_alt = afunc_set_alt; - agdev->func.get_alt = afunc_get_alt; - agdev->func.disable = afunc_disable; - agdev->func.setup = afunc_setup; - - /* Initialize the configurable parameters */ - usb_out_it_desc.bNrChannels = num_channels(c_chmask); - usb_out_it_desc.bmChannelConfig = cpu_to_le32(c_chmask); - io_in_it_desc.bNrChannels = num_channels(p_chmask); - io_in_it_desc.bmChannelConfig = cpu_to_le32(p_chmask); - as_out_hdr_desc.bNrChannels = num_channels(c_chmask); - as_out_hdr_desc.bmChannelConfig = cpu_to_le32(c_chmask); - as_in_hdr_desc.bNrChannels = num_channels(p_chmask); - as_in_hdr_desc.bmChannelConfig = cpu_to_le32(p_chmask); - as_out_fmt1_desc.bSubslotSize = c_ssize; - as_out_fmt1_desc.bBitResolution = c_ssize * 8; - as_in_fmt1_desc.bSubslotSize = p_ssize; - as_in_fmt1_desc.bBitResolution = p_ssize * 8; - - snprintf(clksrc_in, sizeof(clksrc_in), "%uHz", p_srate); - snprintf(clksrc_out, sizeof(clksrc_out), "%uHz", c_srate); - - res = usb_add_function(cfg, &agdev->func); - if (res < 0) - kfree(agdev); - - return res; -} - -#else - static void afunc_free_inst(struct usb_function_instance *f) { struct f_uac2_opts *opts; @@ -1527,5 +1405,3 @@ DECLARE_USB_FUNCTION_INIT(uac2, afunc_alloc_inst, afunc_alloc); MODULE_LICENSE("GPL"); MODULE_AUTHOR("Yadwinder Singh"); MODULE_AUTHOR("Jaswinder Singh"); - -#endif -- cgit v1.1 From f408757f819a5792e6d27865a12f4da4ae802d28 Mon Sep 17 00:00:00 2001 From: Andrzej Pietrasiewicz Date: Tue, 22 Jul 2014 19:58:33 +0200 Subject: usb: gadget: f_uac2: use usb_gstrings_attach Use the new usb_gstring_attach interface. Tested-by: Sebastian Reimers Signed-off-by: Andrzej Pietrasiewicz Signed-off-by: Felipe Balbi --- drivers/usb/gadget/function/f_uac2.c | 33 +++++++++++++++++---------------- 1 file changed, 17 insertions(+), 16 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/gadget/function/f_uac2.c b/drivers/usb/gadget/function/f_uac2.c index 2291037..1b9671e 100644 --- a/drivers/usb/gadget/function/f_uac2.c +++ b/drivers/usb/gadget/function/f_uac2.c @@ -920,25 +920,27 @@ afunc_bind(struct usb_configuration *cfg, struct usb_function *fn) struct usb_gadget *gadget = cdev->gadget; struct uac2_rtd_params *prm; struct f_uac2_opts *uac2_opts; + struct usb_string *us; int ret; uac2_opts = container_of(fn->fi, struct f_uac2_opts, func_inst); - ret = usb_string_ids_tab(cfg->cdev, strings_fn); - if (ret) - return ret; - iad_desc.iFunction = strings_fn[STR_ASSOC].id; - std_ac_if_desc.iInterface = strings_fn[STR_IF_CTRL].id; - in_clk_src_desc.iClockSource = strings_fn[STR_CLKSRC_IN].id; - out_clk_src_desc.iClockSource = strings_fn[STR_CLKSRC_OUT].id; - usb_out_it_desc.iTerminal = strings_fn[STR_USB_IT].id; - io_in_it_desc.iTerminal = strings_fn[STR_IO_IT].id; - usb_in_ot_desc.iTerminal = strings_fn[STR_USB_OT].id; - io_out_ot_desc.iTerminal = strings_fn[STR_IO_OT].id; - std_as_out_if0_desc.iInterface = strings_fn[STR_AS_OUT_ALT0].id; - std_as_out_if1_desc.iInterface = strings_fn[STR_AS_OUT_ALT1].id; - std_as_in_if0_desc.iInterface = strings_fn[STR_AS_IN_ALT0].id; - std_as_in_if1_desc.iInterface = strings_fn[STR_AS_IN_ALT1].id; + us = usb_gstrings_attach(cdev, fn_strings, ARRAY_SIZE(strings_fn)); + if (IS_ERR(us)) + return PTR_ERR(us); + iad_desc.iFunction = us[STR_ASSOC].id; + std_ac_if_desc.iInterface = us[STR_IF_CTRL].id; + in_clk_src_desc.iClockSource = us[STR_CLKSRC_IN].id; + out_clk_src_desc.iClockSource = us[STR_CLKSRC_OUT].id; + usb_out_it_desc.iTerminal = us[STR_USB_IT].id; + io_in_it_desc.iTerminal = us[STR_IO_IT].id; + usb_in_ot_desc.iTerminal = us[STR_USB_OT].id; + io_out_ot_desc.iTerminal = us[STR_IO_OT].id; + std_as_out_if0_desc.iInterface = us[STR_AS_OUT_ALT0].id; + std_as_out_if1_desc.iInterface = us[STR_AS_OUT_ALT1].id; + std_as_in_if0_desc.iInterface = us[STR_AS_IN_ALT0].id; + std_as_in_if1_desc.iInterface = us[STR_AS_IN_ALT1].id; + /* Initialize the configurable parameters */ usb_out_it_desc.bNrChannels = num_channels(uac2_opts->c_chmask); @@ -1389,7 +1391,6 @@ struct usb_function *afunc_alloc(struct usb_function_instance *fi) opts = container_of(fi, struct f_uac2_opts, func_inst); agdev->func.name = "uac2_func"; - agdev->func.strings = fn_strings; agdev->func.bind = afunc_bind; agdev->func.unbind = afunc_unbind; agdev->func.set_alt = afunc_set_alt; -- cgit v1.1 From 065a107cdd70f0621011424009b3ecd4e42481b1 Mon Sep 17 00:00:00 2001 From: Andrzej Pietrasiewicz Date: Tue, 22 Jul 2014 19:58:34 +0200 Subject: usb: gadget: f_uac2: use defined constants as defaults When configfs is integrated the same values will have to be used as defaults. Use symbolic names in order not to duplicate magic numbers. Tested-by: Sebastian Reimers Signed-off-by: Andrzej Pietrasiewicz Signed-off-by: Felipe Balbi --- drivers/usb/gadget/function/u_uac2.h | 7 +++++++ drivers/usb/gadget/legacy/audio.c | 16 ++++++++-------- 2 files changed, 15 insertions(+), 8 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/gadget/function/u_uac2.h b/drivers/usb/gadget/function/u_uac2.h index 290b83a..ed7f736 100644 --- a/drivers/usb/gadget/function/u_uac2.h +++ b/drivers/usb/gadget/function/u_uac2.h @@ -18,6 +18,13 @@ #include +#define UAC2_DEF_PCHMASK 0x3 +#define UAC2_DEF_PSRATE 48000 +#define UAC2_DEF_PSSIZE 2 +#define UAC2_DEF_CCHMASK 0x3 +#define UAC2_DEF_CSRATE 64000 +#define UAC2_DEF_CSSIZE 2 + struct f_uac2_opts { struct usb_function_instance func_inst; int p_chmask; diff --git a/drivers/usb/gadget/legacy/audio.c b/drivers/usb/gadget/legacy/audio.c index 159af03..c28691f 100644 --- a/drivers/usb/gadget/legacy/audio.c +++ b/drivers/usb/gadget/legacy/audio.c @@ -22,33 +22,35 @@ USB_GADGET_COMPOSITE_OPTIONS(); #ifndef CONFIG_GADGET_UAC1 +#include "u_uac2.h" + /* Playback(USB-IN) Default Stereo - Fl/Fr */ -static int p_chmask = 0x3; +static int p_chmask = UAC2_DEF_PCHMASK; module_param(p_chmask, uint, S_IRUGO); MODULE_PARM_DESC(p_chmask, "Playback Channel Mask"); /* Playback Default 48 KHz */ -static int p_srate = 48000; +static int p_srate = UAC2_DEF_PSRATE; module_param(p_srate, uint, S_IRUGO); MODULE_PARM_DESC(p_srate, "Playback Sampling Rate"); /* Playback Default 16bits/sample */ -static int p_ssize = 2; +static int p_ssize = UAC2_DEF_PSSIZE; module_param(p_ssize, uint, S_IRUGO); MODULE_PARM_DESC(p_ssize, "Playback Sample Size(bytes)"); /* Capture(USB-OUT) Default Stereo - Fl/Fr */ -static int c_chmask = 0x3; +static int c_chmask = UAC2_DEF_CCHMASK; module_param(c_chmask, uint, S_IRUGO); MODULE_PARM_DESC(c_chmask, "Capture Channel Mask"); /* Capture Default 64 KHz */ -static int c_srate = 64000; +static int c_srate = UAC2_DEF_CSRATE; module_param(c_srate, uint, S_IRUGO); MODULE_PARM_DESC(c_srate, "Capture Sampling Rate"); /* Capture Default 16bits/sample */ -static int c_ssize = 2; +static int c_ssize = UAC2_DEF_CSSIZE; module_param(c_ssize, uint, S_IRUGO); MODULE_PARM_DESC(c_ssize, "Capture Sample Size(bytes)"); #endif @@ -81,8 +83,6 @@ static struct usb_function *f_uac2; #include "u_uac1.h" #include "u_uac1.c" #include "f_uac1.c" -#else -#include "u_uac2.h" #endif /*-------------------------------------------------------------------------*/ -- cgit v1.1 From 3aeea3c53e73b972ff07a1d03d6cc07f97de4f2f Mon Sep 17 00:00:00 2001 From: Andrzej Pietrasiewicz Date: Tue, 22 Jul 2014 19:58:35 +0200 Subject: usb: gadget: f_uac2: add configfs support Add support for using f_uac2 function as a component of a gadget composed with configfs. Tested-by: Sebastian Reimers Signed-off-by: Andrzej Pietrasiewicz Signed-off-by: Felipe Balbi --- drivers/usb/gadget/function/f_uac2.c | 105 +++++++++++++++++++++++++++++++++++ drivers/usb/gadget/function/u_uac2.h | 3 + 2 files changed, 108 insertions(+) (limited to 'drivers/usb') diff --git a/drivers/usb/gadget/function/f_uac2.c b/drivers/usb/gadget/function/f_uac2.c index 1b9671e..0d65e7c 100644 --- a/drivers/usb/gadget/function/f_uac2.c +++ b/drivers/usb/gadget/function/f_uac2.c @@ -1330,6 +1330,93 @@ afunc_setup(struct usb_function *fn, const struct usb_ctrlrequest *cr) return value; } +static inline struct f_uac2_opts *to_f_uac2_opts(struct config_item *item) +{ + return container_of(to_config_group(item), struct f_uac2_opts, + func_inst.group); +} + +CONFIGFS_ATTR_STRUCT(f_uac2_opts); +CONFIGFS_ATTR_OPS(f_uac2_opts); + +static void f_uac2_attr_release(struct config_item *item) +{ + struct f_uac2_opts *opts = to_f_uac2_opts(item); + + usb_put_function_instance(&opts->func_inst); +} + +static struct configfs_item_operations f_uac2_item_ops = { + .release = f_uac2_attr_release, + .show_attribute = f_uac2_opts_attr_show, + .store_attribute = f_uac2_opts_attr_store, +}; + +#define UAC2_ATTRIBUTE(name) \ +static ssize_t f_uac2_opts_##name##_show(struct f_uac2_opts *opts, \ + char *page) \ +{ \ + int result; \ + \ + mutex_lock(&opts->lock); \ + result = sprintf(page, "%u\n", opts->name); \ + mutex_unlock(&opts->lock); \ + \ + return result; \ +} \ + \ +static ssize_t f_uac2_opts_##name##_store(struct f_uac2_opts *opts, \ + const char *page, size_t len) \ +{ \ + int ret; \ + u32 num; \ + \ + mutex_lock(&opts->lock); \ + if (opts->refcnt) { \ + ret = -EBUSY; \ + goto end; \ + } \ + \ + ret = kstrtou32(page, 0, &num); \ + if (ret) \ + goto end; \ + \ + opts->name = num; \ + ret = len; \ + \ +end: \ + mutex_unlock(&opts->lock); \ + return ret; \ +} \ + \ +static struct f_uac2_opts_attribute f_uac2_opts_##name = \ + __CONFIGFS_ATTR(name, S_IRUGO | S_IWUSR, \ + f_uac2_opts_##name##_show, \ + f_uac2_opts_##name##_store) + +UAC2_ATTRIBUTE(p_chmask); +UAC2_ATTRIBUTE(p_srate); +UAC2_ATTRIBUTE(p_ssize); +UAC2_ATTRIBUTE(c_chmask); +UAC2_ATTRIBUTE(c_srate); +UAC2_ATTRIBUTE(c_ssize); + +static struct configfs_attribute *f_uac2_attrs[] = { + &f_uac2_opts_p_chmask.attr, + &f_uac2_opts_p_srate.attr, + &f_uac2_opts_p_ssize.attr, + &f_uac2_opts_c_chmask.attr, + &f_uac2_opts_c_srate.attr, + &f_uac2_opts_c_ssize.attr, + NULL, +}; + +static struct config_item_type f_uac2_func_type = { + .ct_item_ops = &f_uac2_item_ops, + .ct_attrs = f_uac2_attrs, + .ct_owner = THIS_MODULE, +}; + static void afunc_free_inst(struct usb_function_instance *f) { struct f_uac2_opts *opts; @@ -1346,17 +1433,32 @@ static struct usb_function_instance *afunc_alloc_inst(void) if (!opts) return ERR_PTR(-ENOMEM); + mutex_init(&opts->lock); opts->func_inst.free_func_inst = afunc_free_inst; + config_group_init_type_name(&opts->func_inst.group, "", + &f_uac2_func_type); + + opts->p_chmask = UAC2_DEF_PCHMASK; + opts->p_srate = UAC2_DEF_PSRATE; + opts->p_ssize = UAC2_DEF_PSSIZE; + opts->c_chmask = UAC2_DEF_CCHMASK; + opts->c_srate = UAC2_DEF_CSRATE; + opts->c_ssize = UAC2_DEF_CSSIZE; return &opts->func_inst; } static void afunc_free(struct usb_function *f) { struct audio_dev *agdev; + struct f_uac2_opts *opts; agdev = func_to_agdev(f); + opts = container_of(f->fi, struct f_uac2_opts, func_inst); kfree(agdev); + mutex_lock(&opts->lock); + --opts->refcnt; + mutex_unlock(&opts->lock); } static void afunc_unbind(struct usb_configuration *c, struct usb_function *f) @@ -1389,6 +1491,9 @@ struct usb_function *afunc_alloc(struct usb_function_instance *fi) return ERR_PTR(-ENOMEM); opts = container_of(fi, struct f_uac2_opts, func_inst); + mutex_lock(&opts->lock); + ++opts->refcnt; + mutex_unlock(&opts->lock); agdev->func.name = "uac2_func"; agdev->func.bind = afunc_bind; diff --git a/drivers/usb/gadget/function/u_uac2.h b/drivers/usb/gadget/function/u_uac2.h index ed7f736..78dd372 100644 --- a/drivers/usb/gadget/function/u_uac2.h +++ b/drivers/usb/gadget/function/u_uac2.h @@ -34,6 +34,9 @@ struct f_uac2_opts { int c_srate; int c_ssize; bool bound; + + struct mutex lock; + int refcnt; }; #endif -- cgit v1.1 From f73db69f95921512b7cba586066723b500770d1a Mon Sep 17 00:00:00 2001 From: Andrzej Pietrasiewicz Date: Tue, 22 Jul 2014 19:58:36 +0200 Subject: usb: gadget: f_uac1: add function strings uac1 function is missing strings. Add them. Tested-by: Sebastian Reimers Signed-off-by: Andrzej Pietrasiewicz Signed-off-by: Felipe Balbi --- drivers/usb/gadget/function/f_uac1.c | 51 +++++++++++++++++++++++++++++++++++- 1 file changed, 50 insertions(+), 1 deletion(-) (limited to 'drivers/usb') diff --git a/drivers/usb/gadget/function/f_uac1.c b/drivers/usb/gadget/function/f_uac1.c index 2b4c82d..1c0c4b8 100644 --- a/drivers/usb/gadget/function/f_uac1.c +++ b/drivers/usb/gadget/function/f_uac1.c @@ -216,6 +216,37 @@ static struct usb_descriptor_header *f_audio_desc[] __initdata = { NULL, }; +enum { + STR_AC_IF, + STR_INPUT_TERMINAL, + STR_INPUT_TERMINAL_CH_NAMES, + STR_FEAT_DESC_0, + STR_OUTPUT_TERMINAL, + STR_AS_IF_ALT0, + STR_AS_IF_ALT1, +}; + +static struct usb_string strings_uac1[] = { + [STR_AC_IF].s = "AC Interface", + [STR_INPUT_TERMINAL].s = "Input terminal", + [STR_INPUT_TERMINAL_CH_NAMES].s = "Channels", + [STR_FEAT_DESC_0].s = "Volume control & mute", + [STR_OUTPUT_TERMINAL].s = "Output terminal", + [STR_AS_IF_ALT0].s = "AS Interface", + [STR_AS_IF_ALT1].s = "AS Interface", + { }, +}; + +static struct usb_gadget_strings str_uac1 = { + .language = 0x0409, /* en-us */ + .strings = strings_uac1, +}; + +static struct usb_gadget_strings *uac1_strings[] = { + &str_uac1, + NULL, +}; + /* * This function is an ALSA sound card following USB Audio Class Spec 1.0. */ @@ -724,6 +755,24 @@ static int __init audio_bind_config(struct usb_configuration *c) struct f_audio *audio; int status; + if (strings_uac1[0].id == 0) { + status = usb_string_ids_tab(c->cdev, strings_uac1); + if (status < 0) + return status; + ac_interface_desc.iInterface = strings_uac1[STR_AC_IF].id; + input_terminal_desc.iTerminal = + strings_uac1[STR_INPUT_TERMINAL].id; + input_terminal_desc.iChannelNames = + strings_uac1[STR_INPUT_TERMINAL_CH_NAMES].id; + feature_unit_desc.iFeature = strings_uac1[STR_FEAT_DESC_0].id; + output_terminal_desc.iTerminal = + strings_uac1[STR_OUTPUT_TERMINAL].id; + as_interface_alt_0_desc.iInterface = + strings_uac1[STR_AS_IF_ALT0].id; + as_interface_alt_1_desc.iInterface = + strings_uac1[STR_AS_IF_ALT1].id; + } + /* allocate and initialize one new instance */ audio = kzalloc(sizeof *audio, GFP_KERNEL); if (!audio) @@ -740,7 +789,7 @@ static int __init audio_bind_config(struct usb_configuration *c) if (status < 0) goto setup_fail; - audio->card.func.strings = audio_strings; + audio->card.func.strings = uac1_strings; audio->card.func.bind = f_audio_bind; audio->card.func.unbind = f_audio_unbind; audio->card.func.set_alt = f_audio_set_alt; -- cgit v1.1 From af1a58ca00b3735275c453ebd0b811a71a377470 Mon Sep 17 00:00:00 2001 From: Andrzej Pietrasiewicz Date: Tue, 22 Jul 2014 19:58:37 +0200 Subject: usb: gadget: f_uac1: prepare for separate compilation Integrating configfs requires converting f_uac1 to new function interface, which in turn requires converting it to the new function interface, which involves separate compilation of f_uac1.c into usb_f_uac1.ko. u_uac1.c contains some module parameters. After this patch is applied they are still a part of the resulting g_audio.ko, but can be guarded with a compatiblity flag which will be removed when no users of the old function interface of f_uac1 are left. Tested-by: Sebastian Reimers Signed-off-by: Andrzej Pietrasiewicz Signed-off-by: Felipe Balbi --- drivers/usb/gadget/function/f_uac1.c | 1 + drivers/usb/gadget/legacy/audio.c | 1 - 2 files changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/usb') diff --git a/drivers/usb/gadget/function/f_uac1.c b/drivers/usb/gadget/function/f_uac1.c index 1c0c4b8..9cfaf1d 100644 --- a/drivers/usb/gadget/function/f_uac1.c +++ b/drivers/usb/gadget/function/f_uac1.c @@ -15,6 +15,7 @@ #include #include "u_uac1.h" +#include "u_uac1.c" #define OUT_EP_MAX_PACKET_SIZE 200 static int req_buf_size = OUT_EP_MAX_PACKET_SIZE; diff --git a/drivers/usb/gadget/legacy/audio.c b/drivers/usb/gadget/legacy/audio.c index c28691f..47a7de7 100644 --- a/drivers/usb/gadget/legacy/audio.c +++ b/drivers/usb/gadget/legacy/audio.c @@ -81,7 +81,6 @@ static struct usb_function *f_uac2; #ifdef CONFIG_GADGET_UAC1 #include "u_uac1.h" -#include "u_uac1.c" #include "f_uac1.c" #endif -- cgit v1.1 From f3a3406b3f562f8d15b89979c0ca9e184b269084 Mon Sep 17 00:00:00 2001 From: Andrzej Pietrasiewicz Date: Tue, 22 Jul 2014 19:58:38 +0200 Subject: usb: gadget: f_uac1: convert to new function interface with backward compatibility Converting uac1 to the new function interface requires converting the USB uac1's function code and its users. This patch converts the f_uac1.c to the new function interface. The file is now compiled into a separate usb_f_uac1.ko module. The old function interface is provided by means of a preprocessor conditional directives. After all users are converted, the old interface can be removed. Tested-by: Sebastian Reimers Signed-off-by: Andrzej Pietrasiewicz Signed-off-by: Felipe Balbi --- drivers/usb/gadget/Kconfig | 3 + drivers/usb/gadget/function/Makefile | 2 + drivers/usb/gadget/function/f_uac1.c | 169 +++++++++++++++++++++++++++++------ drivers/usb/gadget/function/u_uac1.c | 31 +++++-- drivers/usb/gadget/function/u_uac1.h | 20 +++++ drivers/usb/gadget/legacy/audio.c | 1 + 6 files changed, 195 insertions(+), 31 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/gadget/Kconfig b/drivers/usb/gadget/Kconfig index ea8ebce..68302aa 100644 --- a/drivers/usb/gadget/Kconfig +++ b/drivers/usb/gadget/Kconfig @@ -181,6 +181,9 @@ config USB_F_MASS_STORAGE config USB_F_FS tristate +config USB_F_UAC1 + tristate + config USB_F_UAC2 tristate diff --git a/drivers/usb/gadget/function/Makefile b/drivers/usb/gadget/function/Makefile index 20775a8..edb6dfa 100644 --- a/drivers/usb/gadget/function/Makefile +++ b/drivers/usb/gadget/function/Makefile @@ -32,5 +32,7 @@ usb_f_mass_storage-y := f_mass_storage.o storage_common.o obj-$(CONFIG_USB_F_MASS_STORAGE)+= usb_f_mass_storage.o usb_f_fs-y := f_fs.o obj-$(CONFIG_USB_F_FS) += usb_f_fs.o +usb_f_uac1-y := f_uac1.o u_uac1.o +obj-$(CONFIG_USB_F_UAC1) += usb_f_uac1.o usb_f_uac2-y := f_uac2.o obj-$(CONFIG_USB_F_UAC2) += usb_f_uac2.o diff --git a/drivers/usb/gadget/function/f_uac1.c b/drivers/usb/gadget/function/f_uac1.c index 9cfaf1d..787ed2b 100644 --- a/drivers/usb/gadget/function/f_uac1.c +++ b/drivers/usb/gadget/function/f_uac1.c @@ -11,13 +11,17 @@ #include #include +#include #include #include #include "u_uac1.h" +#ifdef USBF_UAC1_INCLUDED #include "u_uac1.c" +#endif #define OUT_EP_MAX_PACKET_SIZE 200 +#ifdef USBF_UAC1_INCLUDED static int req_buf_size = OUT_EP_MAX_PACKET_SIZE; module_param(req_buf_size, int, S_IRUGO); MODULE_PARM_DESC(req_buf_size, "ISO OUT endpoint request buffer size"); @@ -29,6 +33,7 @@ MODULE_PARM_DESC(req_count, "ISO OUT endpoint request count"); static int audio_buf_size = 48000; module_param(audio_buf_size, int, S_IRUGO); MODULE_PARM_DESC(audio_buf_size, "Audio buffer size"); +#endif static int generic_set_cmd(struct usb_audio_control *con, u8 cmd, int value); static int generic_get_cmd(struct usb_audio_control *con, u8 cmd); @@ -47,7 +52,7 @@ static int generic_get_cmd(struct usb_audio_control *con, u8 cmd); #define F_AUDIO_NUM_INTERFACES 2 /* B.3.1 Standard AC Interface Descriptor */ -static struct usb_interface_descriptor ac_interface_desc __initdata = { +static struct usb_interface_descriptor ac_interface_desc = { .bLength = USB_DT_INTERFACE_SIZE, .bDescriptorType = USB_DT_INTERFACE, .bNumEndpoints = 0, @@ -189,7 +194,7 @@ static struct usb_endpoint_descriptor as_out_ep_desc = { }; /* Class-specific AS ISO OUT Endpoint Descriptor */ -static struct uac_iso_endpoint_descriptor as_iso_out_desc __initdata = { +static struct uac_iso_endpoint_descriptor as_iso_out_desc = { .bLength = UAC_ISO_ENDPOINT_DESC_SIZE, .bDescriptorType = USB_DT_CS_ENDPOINT, .bDescriptorSubtype = UAC_EP_GENERAL, @@ -198,7 +203,7 @@ static struct uac_iso_endpoint_descriptor as_iso_out_desc __initdata = { .wLockDelay = __constant_cpu_to_le16(1), }; -static struct usb_descriptor_header *f_audio_desc[] __initdata = { +static struct usb_descriptor_header *f_audio_desc[] = { (struct usb_descriptor_header *)&ac_interface_desc, (struct usb_descriptor_header *)&ac_header_desc, @@ -332,8 +337,17 @@ static int f_audio_out_ep_complete(struct usb_ep *ep, struct usb_request *req) struct f_audio *audio = req->context; struct usb_composite_dev *cdev = audio->card.func.config->cdev; struct f_audio_buf *copy_buf = audio->copy_buf; +#ifndef USBF_UAC1_INCLUDED + struct f_uac1_opts *opts; + int audio_buf_size; +#endif int err; +#ifndef USBF_UAC1_INCLUDED + opts = container_of(audio->card.func.fi, struct f_uac1_opts, + func_inst); + audio_buf_size = opts->audio_buf_size; +#endif if (!copy_buf) return -EINVAL; @@ -578,10 +592,21 @@ static int f_audio_set_alt(struct usb_function *f, unsigned intf, unsigned alt) struct usb_composite_dev *cdev = f->config->cdev; struct usb_ep *out_ep = audio->out_ep; struct usb_request *req; +#ifndef USBF_UAC1_INCLUDED + struct f_uac1_opts *opts; + int req_buf_size, req_count, audio_buf_size; +#endif int i = 0, err = 0; DBG(cdev, "intf %d, alt %d\n", intf, alt); +#ifndef USBF_UAC1_INCLUDED + opts = container_of(f->fi, struct f_uac1_opts, func_inst); + req_buf_size = opts->req_buf_size; + req_count = opts->req_count; + audio_buf_size = opts->audio_buf_size; + +#endif if (intf == 1) { if (alt == 1) { usb_ep_enable(out_ep); @@ -657,13 +682,50 @@ static void f_audio_build_desc(struct f_audio *audio) } /* audio function driver setup/binding */ -static int __init +static int f_audio_bind(struct usb_configuration *c, struct usb_function *f) { struct usb_composite_dev *cdev = c->cdev; struct f_audio *audio = func_to_audio(f); int status; struct usb_ep *ep = NULL; +#ifndef USBF_UAC1_INCLUDED + struct f_uac1_opts *audio_opts; + + audio_opts = container_of(f->fi, struct f_uac1_opts, func_inst); + audio->card.gadget = c->cdev->gadget; + audio_opts->card = &audio->card; + /* set up ASLA audio devices */ + if (!audio_opts->bound) { + status = gaudio_setup(&audio->card); + if (status < 0) + return status; + audio_opts->bound = true; + } +#else + audio->card.gadget = c->cdev->gadget; +#endif + if (strings_uac1[0].id == 0) { + status = usb_string_ids_tab(c->cdev, strings_uac1); + if (status < 0) +#ifdef USBF_UAC1_INCLUDED + return status; +#else + goto fail; +#endif + ac_interface_desc.iInterface = strings_uac1[STR_AC_IF].id; + input_terminal_desc.iTerminal = + strings_uac1[STR_INPUT_TERMINAL].id; + input_terminal_desc.iChannelNames = + strings_uac1[STR_INPUT_TERMINAL_CH_NAMES].id; + feature_unit_desc.iFeature = strings_uac1[STR_FEAT_DESC_0].id; + output_terminal_desc.iTerminal = + strings_uac1[STR_OUTPUT_TERMINAL].id; + as_interface_alt_0_desc.iInterface = + strings_uac1[STR_AS_IF_ALT0].id; + as_interface_alt_1_desc.iInterface = + strings_uac1[STR_AS_IF_ALT1].id; + } f_audio_build_desc(audio); @@ -698,19 +760,24 @@ f_audio_bind(struct usb_configuration *c, struct usb_function *f) return 0; fail: +#ifndef USBF_UAC1_INCLUDED + gaudio_cleanup(&audio->card); +#endif if (ep) ep->driver_data = NULL; return status; } +#ifdef USBF_UAC1_INCLUDED static void -f_audio_unbind(struct usb_configuration *c, struct usb_function *f) +old_f_audio_unbind(struct usb_configuration *c, struct usb_function *f) { struct f_audio *audio = func_to_audio(f); usb_free_all_descriptors(f); kfree(audio); } +#endif /*-------------------------------------------------------------------------*/ @@ -727,7 +794,7 @@ static int generic_get_cmd(struct usb_audio_control *con, u8 cmd) } /* Todo: add more control selecotor dynamically */ -static int __init control_selector_init(struct f_audio *audio) +static int control_selector_init(struct f_audio *audio) { INIT_LIST_HEAD(&audio->cs); list_add(&feature_unit.list, &audio->cs); @@ -744,6 +811,7 @@ static int __init control_selector_init(struct f_audio *audio) return 0; } +#ifdef USBF_UAC1_INCLUDED /** * audio_bind_config - add USB audio function to a configuration * @c: the configuration to supcard the USB audio function @@ -756,24 +824,6 @@ static int __init audio_bind_config(struct usb_configuration *c) struct f_audio *audio; int status; - if (strings_uac1[0].id == 0) { - status = usb_string_ids_tab(c->cdev, strings_uac1); - if (status < 0) - return status; - ac_interface_desc.iInterface = strings_uac1[STR_AC_IF].id; - input_terminal_desc.iTerminal = - strings_uac1[STR_INPUT_TERMINAL].id; - input_terminal_desc.iChannelNames = - strings_uac1[STR_INPUT_TERMINAL_CH_NAMES].id; - feature_unit_desc.iFeature = strings_uac1[STR_FEAT_DESC_0].id; - output_terminal_desc.iTerminal = - strings_uac1[STR_OUTPUT_TERMINAL].id; - as_interface_alt_0_desc.iInterface = - strings_uac1[STR_AS_IF_ALT0].id; - as_interface_alt_1_desc.iInterface = - strings_uac1[STR_AS_IF_ALT1].id; - } - /* allocate and initialize one new instance */ audio = kzalloc(sizeof *audio, GFP_KERNEL); if (!audio) @@ -789,10 +839,9 @@ static int __init audio_bind_config(struct usb_configuration *c) status = gaudio_setup(&audio->card); if (status < 0) goto setup_fail; - audio->card.func.strings = uac1_strings; audio->card.func.bind = f_audio_bind; - audio->card.func.unbind = f_audio_unbind; + audio->card.func.unbind = old_f_audio_unbind; audio->card.func.set_alt = f_audio_set_alt; audio->card.func.setup = f_audio_setup; audio->card.func.disable = f_audio_disable; @@ -816,3 +865,71 @@ setup_fail: kfree(audio); return status; } +#else +static void f_audio_free_inst(struct usb_function_instance *f) +{ + struct f_uac1_opts *opts; + + opts = container_of(f, struct f_uac1_opts, func_inst); + gaudio_cleanup(opts->card); + kfree(opts); +} + +static struct usb_function_instance *f_audio_alloc_inst(void) +{ + struct f_uac1_opts *opts; + + opts = kzalloc(sizeof(*opts), GFP_KERNEL); + if (!opts) + return ERR_PTR(-ENOMEM); + + opts->func_inst.free_func_inst = f_audio_free_inst; + + return &opts->func_inst; +} + +static void f_audio_free(struct usb_function *f) +{ + struct f_audio *audio = func_to_audio(f); + + kfree(audio); +} + +static void f_audio_unbind(struct usb_configuration *c, struct usb_function *f) +{ + usb_free_all_descriptors(f); +} + +static struct usb_function *f_audio_alloc(struct usb_function_instance *fi) +{ + struct f_audio *audio; + + /* allocate and initialize one new instance */ + audio = kzalloc(sizeof(*audio), GFP_KERNEL); + if (!audio) + return ERR_PTR(-ENOMEM); + + audio->card.func.name = "g_audio"; + + INIT_LIST_HEAD(&audio->play_queue); + spin_lock_init(&audio->lock); + + audio->card.func.strings = uac1_strings; + audio->card.func.bind = f_audio_bind; + audio->card.func.unbind = f_audio_unbind; + audio->card.func.set_alt = f_audio_set_alt; + audio->card.func.setup = f_audio_setup; + audio->card.func.disable = f_audio_disable; + audio->card.func.free_func = f_audio_free; + + control_selector_init(audio); + + INIT_WORK(&audio->playback_work, f_audio_playback_work); + + return &audio->card.func; +} + +DECLARE_USB_FUNCTION_INIT(uac1, f_audio_alloc_inst, f_audio_alloc); +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Bryan Wu"); +#endif diff --git a/drivers/usb/gadget/function/u_uac1.c b/drivers/usb/gadget/function/u_uac1.c index 7a55fea..9a55e5c 100644 --- a/drivers/usb/gadget/function/u_uac1.c +++ b/drivers/usb/gadget/function/u_uac1.c @@ -10,6 +10,7 @@ */ #include +#include #include #include #include @@ -23,6 +24,7 @@ * This component encapsulates the ALSA devices for USB audio gadget */ +#ifdef USBF_UAC1_INCLUDED #define FILE_PCM_PLAYBACK "/dev/snd/pcmC0D0p" #define FILE_PCM_CAPTURE "/dev/snd/pcmC0D0c" #define FILE_CONTROL "/dev/snd/controlC0" @@ -38,7 +40,7 @@ MODULE_PARM_DESC(fn_cap, "Capture PCM device file name"); static char *fn_cntl = FILE_CONTROL; module_param(fn_cntl, charp, S_IRUGO); MODULE_PARM_DESC(fn_cntl, "Control device file name"); - +#endif /*-------------------------------------------------------------------------*/ /** @@ -167,7 +169,7 @@ static int playback_default_hw_params(struct gaudio_snd_dev *snd) /** * Playback audio buffer data by ALSA PCM device */ -static size_t u_audio_playback(struct gaudio *card, void *buf, size_t count) +size_t u_audio_playback(struct gaudio *card, void *buf, size_t count) { struct gaudio_snd_dev *snd = &card->playback; struct snd_pcm_substream *substream = snd->substream; @@ -202,12 +204,12 @@ try_again: return 0; } -static int u_audio_get_playback_channels(struct gaudio *card) +int u_audio_get_playback_channels(struct gaudio *card) { return card->playback.channels; } -static int u_audio_get_playback_rate(struct gaudio *card) +int u_audio_get_playback_rate(struct gaudio *card) { return card->playback.rate; } @@ -220,6 +222,15 @@ static int gaudio_open_snd_dev(struct gaudio *card) { struct snd_pcm_file *pcm_file; struct gaudio_snd_dev *snd; +#ifndef USBF_UAC1_INCLUDED + struct f_uac1_opts *opts; + char *fn_play, *fn_cap, *fn_cntl; + + opts = container_of(card->func.fi, struct f_uac1_opts, func_inst); + fn_play = opts->fn_play; + fn_cap = opts->fn_cap; + fn_cntl = opts->fn_cntl; +#endif if (!card) return -ENODEV; @@ -293,7 +304,9 @@ static int gaudio_close_snd_dev(struct gaudio *gau) return 0; } +#ifdef USBF_UAC1_INCLUDED static struct gaudio *the_card; +#endif /** * gaudio_setup - setup ALSA interface and preparing for USB transfer * @@ -301,15 +314,17 @@ static struct gaudio *the_card; * * Returns negative errno, or zero on success */ -int __init gaudio_setup(struct gaudio *card) +int gaudio_setup(struct gaudio *card) { int ret; ret = gaudio_open_snd_dev(card); if (ret) ERROR(card, "we need at least one control device\n"); +#ifdef USBF_UAC1_INCLUDED else if (!the_card) the_card = card; +#endif return ret; @@ -320,11 +335,17 @@ int __init gaudio_setup(struct gaudio *card) * * This is called to free all resources allocated by @gaudio_setup(). */ +#ifdef USBF_UAC1_INCLUDED void gaudio_cleanup(void) +#else +void gaudio_cleanup(struct gaudio *the_card) +#endif { if (the_card) { gaudio_close_snd_dev(the_card); +#ifdef USBF_UAC1_INCLUDED the_card = NULL; +#endif } } diff --git a/drivers/usb/gadget/function/u_uac1.h b/drivers/usb/gadget/function/u_uac1.h index 18c2e72..5b4fe9e 100644 --- a/drivers/usb/gadget/function/u_uac1.h +++ b/drivers/usb/gadget/function/u_uac1.h @@ -50,7 +50,27 @@ struct gaudio { /* TODO */ }; +struct f_uac1_opts { + struct usb_function_instance func_inst; + int req_buf_size; + int req_count; + int audio_buf_size; + char *fn_play; + char *fn_cap; + char *fn_cntl; + bool bound; + struct gaudio *card; +}; + int gaudio_setup(struct gaudio *card); +#ifdef USBF_UAC1_INCLUDED void gaudio_cleanup(void); +#else +void gaudio_cleanup(struct gaudio *the_card); +#endif + +size_t u_audio_playback(struct gaudio *card, void *buf, size_t count); +int u_audio_get_playback_channels(struct gaudio *card); +int u_audio_get_playback_rate(struct gaudio *card); #endif /* __U_AUDIO_H */ diff --git a/drivers/usb/gadget/legacy/audio.c b/drivers/usb/gadget/legacy/audio.c index 47a7de7..cf1a543 100644 --- a/drivers/usb/gadget/legacy/audio.c +++ b/drivers/usb/gadget/legacy/audio.c @@ -80,6 +80,7 @@ static struct usb_function *f_uac2; #endif #ifdef CONFIG_GADGET_UAC1 +#define USBF_UAC1_INCLUDED #include "u_uac1.h" #include "f_uac1.c" #endif -- cgit v1.1 From 0d992dec967d6edc97b3001598db7c4ac4e4b3c1 Mon Sep 17 00:00:00 2001 From: Andrzej Pietrasiewicz Date: Tue, 22 Jul 2014 19:58:39 +0200 Subject: usb: gadget: audio: convert to new interface of f_uac1 Use the new interface so that the old one can be removed. Tested-by: Sebastian Reimers Signed-off-by: Andrzej Pietrasiewicz Signed-off-by: Felipe Balbi --- drivers/usb/gadget/legacy/Kconfig | 1 + drivers/usb/gadget/legacy/audio.c | 69 +++++++++++++++++++++++++++++++++++---- 2 files changed, 64 insertions(+), 6 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/gadget/legacy/Kconfig b/drivers/usb/gadget/legacy/Kconfig index 172a6a3..bbd4b85 100644 --- a/drivers/usb/gadget/legacy/Kconfig +++ b/drivers/usb/gadget/legacy/Kconfig @@ -54,6 +54,7 @@ config USB_AUDIO depends on SND select USB_LIBCOMPOSITE select SND_PCM + select USB_F_UAC1 if GADGET_UAC1 select USB_F_UAC2 if !GADGET_UAC1 help This Gadget Audio driver is compatible with USB Audio Class diff --git a/drivers/usb/gadget/legacy/audio.c b/drivers/usb/gadget/legacy/audio.c index cf1a543..bb40b61 100644 --- a/drivers/usb/gadget/legacy/audio.c +++ b/drivers/usb/gadget/legacy/audio.c @@ -53,6 +53,35 @@ MODULE_PARM_DESC(c_srate, "Capture Sampling Rate"); static int c_ssize = UAC2_DEF_CSSIZE; module_param(c_ssize, uint, S_IRUGO); MODULE_PARM_DESC(c_ssize, "Capture Sample Size(bytes)"); +#else +#define FILE_PCM_PLAYBACK "/dev/snd/pcmC0D0p" +#define FILE_PCM_CAPTURE "/dev/snd/pcmC0D0c" +#define FILE_CONTROL "/dev/snd/controlC0" + +static char *fn_play = FILE_PCM_PLAYBACK; +module_param(fn_play, charp, S_IRUGO); +MODULE_PARM_DESC(fn_play, "Playback PCM device file name"); + +static char *fn_cap = FILE_PCM_CAPTURE; +module_param(fn_cap, charp, S_IRUGO); +MODULE_PARM_DESC(fn_cap, "Capture PCM device file name"); + +static char *fn_cntl = FILE_CONTROL; +module_param(fn_cntl, charp, S_IRUGO); +MODULE_PARM_DESC(fn_cntl, "Control device file name"); + +#define OUT_EP_MAX_PACKET_SIZE 200 +static int req_buf_size = OUT_EP_MAX_PACKET_SIZE; +module_param(req_buf_size, int, S_IRUGO); +MODULE_PARM_DESC(req_buf_size, "ISO OUT endpoint request buffer size"); + +static int req_count = 256; +module_param(req_count, int, S_IRUGO); +MODULE_PARM_DESC(req_count, "ISO OUT endpoint request count"); + +static int audio_buf_size = 48000; +module_param(audio_buf_size, int, S_IRUGO); +MODULE_PARM_DESC(audio_buf_size, "Audio buffer size"); #endif /* string IDs are assigned dynamically */ @@ -77,12 +106,13 @@ static struct usb_gadget_strings *audio_strings[] = { #ifndef CONFIG_GADGET_UAC1 static struct usb_function_instance *fi_uac2; static struct usb_function *f_uac2; +#else +static struct usb_function_instance *fi_uac1; +static struct usb_function *f_uac1; #endif #ifdef CONFIG_GADGET_UAC1 -#define USBF_UAC1_INCLUDED #include "u_uac1.h" -#include "f_uac1.c" #endif /*-------------------------------------------------------------------------*/ @@ -146,9 +176,7 @@ static const struct usb_descriptor_header *otg_desc[] = { static int __init audio_do_config(struct usb_configuration *c) { -#ifndef CONFIG_GADGET_UAC1 int status; -#endif /* FIXME alloc iConfiguration string, set it in c->strings */ @@ -158,7 +186,17 @@ static int __init audio_do_config(struct usb_configuration *c) } #ifdef CONFIG_GADGET_UAC1 - audio_bind_config(c); + f_uac1 = usb_get_function(fi_uac1); + if (IS_ERR(f_uac1)) { + status = PTR_ERR(f_uac1); + return status; + } + + status = usb_add_function(c, f_uac1); + if (status < 0) { + usb_put_function(f_uac1); + return status; + } #else f_uac2 = usb_get_function(fi_uac2); if (IS_ERR(f_uac2)) { @@ -189,6 +227,8 @@ static int __init audio_bind(struct usb_composite_dev *cdev) { #ifndef CONFIG_GADGET_UAC1 struct f_uac2_opts *uac2_opts; +#else + struct f_uac1_opts *uac1_opts; #endif int status; @@ -196,6 +236,10 @@ static int __init audio_bind(struct usb_composite_dev *cdev) fi_uac2 = usb_get_function_instance("uac2"); if (IS_ERR(fi_uac2)) return PTR_ERR(fi_uac2); +#else + fi_uac1 = usb_get_function_instance("uac1"); + if (IS_ERR(fi_uac1)) + return PTR_ERR(fi_uac1); #endif #ifndef CONFIG_GADGET_UAC1 @@ -206,6 +250,14 @@ static int __init audio_bind(struct usb_composite_dev *cdev) uac2_opts->c_chmask = c_chmask; uac2_opts->c_srate = c_srate; uac2_opts->c_ssize = c_ssize; +#else + uac1_opts = container_of(fi_uac1, struct f_uac1_opts, func_inst); + uac1_opts->fn_play = fn_play; + uac1_opts->fn_cap = fn_cap; + uac1_opts->fn_cntl = fn_cntl; + uac1_opts->req_buf_size = req_buf_size; + uac1_opts->req_count = req_count; + uac1_opts->audio_buf_size = audio_buf_size; #endif status = usb_string_ids_tab(cdev, strings_dev); @@ -225,6 +277,8 @@ static int __init audio_bind(struct usb_composite_dev *cdev) fail: #ifndef CONFIG_GADGET_UAC1 usb_put_function_instance(fi_uac2); +#else + usb_put_function_instance(fi_uac1); #endif return status; } @@ -232,7 +286,10 @@ fail: static int __exit audio_unbind(struct usb_composite_dev *cdev) { #ifdef CONFIG_GADGET_UAC1 - gaudio_cleanup(); + if (!IS_ERR_OR_NULL(f_uac1)) + usb_put_function(f_uac1); + if (!IS_ERR_OR_NULL(fi_uac1)) + usb_put_function_instance(fi_uac1); #else if (!IS_ERR_OR_NULL(f_uac2)) usb_put_function(f_uac2); -- cgit v1.1 From 605ef833f0c6f9e609e27ff1582a14a4dbc7d341 Mon Sep 17 00:00:00 2001 From: Andrzej Pietrasiewicz Date: Tue, 22 Jul 2014 19:58:40 +0200 Subject: usb: gadget: f_uac1: remove compatibility layer There are no users of the old interface left, so it can be removed. Tested-by: Sebastian Reimers Signed-off-by: Andrzej Pietrasiewicz Signed-off-by: Felipe Balbi --- drivers/usb/gadget/function/f_uac1.c | 102 +---------------------------------- drivers/usb/gadget/function/u_uac1.c | 33 ------------ drivers/usb/gadget/function/u_uac1.h | 4 -- 3 files changed, 1 insertion(+), 138 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/gadget/function/f_uac1.c b/drivers/usb/gadget/function/f_uac1.c index 787ed2b..e0399d2 100644 --- a/drivers/usb/gadget/function/f_uac1.c +++ b/drivers/usb/gadget/function/f_uac1.c @@ -16,24 +16,8 @@ #include #include "u_uac1.h" -#ifdef USBF_UAC1_INCLUDED -#include "u_uac1.c" -#endif #define OUT_EP_MAX_PACKET_SIZE 200 -#ifdef USBF_UAC1_INCLUDED -static int req_buf_size = OUT_EP_MAX_PACKET_SIZE; -module_param(req_buf_size, int, S_IRUGO); -MODULE_PARM_DESC(req_buf_size, "ISO OUT endpoint request buffer size"); - -static int req_count = 256; -module_param(req_count, int, S_IRUGO); -MODULE_PARM_DESC(req_count, "ISO OUT endpoint request count"); - -static int audio_buf_size = 48000; -module_param(audio_buf_size, int, S_IRUGO); -MODULE_PARM_DESC(audio_buf_size, "Audio buffer size"); -#endif static int generic_set_cmd(struct usb_audio_control *con, u8 cmd, int value); static int generic_get_cmd(struct usb_audio_control *con, u8 cmd); @@ -337,17 +321,14 @@ static int f_audio_out_ep_complete(struct usb_ep *ep, struct usb_request *req) struct f_audio *audio = req->context; struct usb_composite_dev *cdev = audio->card.func.config->cdev; struct f_audio_buf *copy_buf = audio->copy_buf; -#ifndef USBF_UAC1_INCLUDED struct f_uac1_opts *opts; int audio_buf_size; -#endif int err; -#ifndef USBF_UAC1_INCLUDED opts = container_of(audio->card.func.fi, struct f_uac1_opts, func_inst); audio_buf_size = opts->audio_buf_size; -#endif + if (!copy_buf) return -EINVAL; @@ -592,21 +573,17 @@ static int f_audio_set_alt(struct usb_function *f, unsigned intf, unsigned alt) struct usb_composite_dev *cdev = f->config->cdev; struct usb_ep *out_ep = audio->out_ep; struct usb_request *req; -#ifndef USBF_UAC1_INCLUDED struct f_uac1_opts *opts; int req_buf_size, req_count, audio_buf_size; -#endif int i = 0, err = 0; DBG(cdev, "intf %d, alt %d\n", intf, alt); -#ifndef USBF_UAC1_INCLUDED opts = container_of(f->fi, struct f_uac1_opts, func_inst); req_buf_size = opts->req_buf_size; req_count = opts->req_count; audio_buf_size = opts->audio_buf_size; -#endif if (intf == 1) { if (alt == 1) { usb_ep_enable(out_ep); @@ -689,7 +666,6 @@ f_audio_bind(struct usb_configuration *c, struct usb_function *f) struct f_audio *audio = func_to_audio(f); int status; struct usb_ep *ep = NULL; -#ifndef USBF_UAC1_INCLUDED struct f_uac1_opts *audio_opts; audio_opts = container_of(f->fi, struct f_uac1_opts, func_inst); @@ -702,17 +678,10 @@ f_audio_bind(struct usb_configuration *c, struct usb_function *f) return status; audio_opts->bound = true; } -#else - audio->card.gadget = c->cdev->gadget; -#endif if (strings_uac1[0].id == 0) { status = usb_string_ids_tab(c->cdev, strings_uac1); if (status < 0) -#ifdef USBF_UAC1_INCLUDED - return status; -#else goto fail; -#endif ac_interface_desc.iInterface = strings_uac1[STR_AC_IF].id; input_terminal_desc.iTerminal = strings_uac1[STR_INPUT_TERMINAL].id; @@ -760,25 +729,12 @@ f_audio_bind(struct usb_configuration *c, struct usb_function *f) return 0; fail: -#ifndef USBF_UAC1_INCLUDED gaudio_cleanup(&audio->card); -#endif if (ep) ep->driver_data = NULL; return status; } -#ifdef USBF_UAC1_INCLUDED -static void -old_f_audio_unbind(struct usb_configuration *c, struct usb_function *f) -{ - struct f_audio *audio = func_to_audio(f); - - usb_free_all_descriptors(f); - kfree(audio); -} -#endif - /*-------------------------------------------------------------------------*/ static int generic_set_cmd(struct usb_audio_control *con, u8 cmd, int value) @@ -811,61 +767,6 @@ static int control_selector_init(struct f_audio *audio) return 0; } -#ifdef USBF_UAC1_INCLUDED -/** - * audio_bind_config - add USB audio function to a configuration - * @c: the configuration to supcard the USB audio function - * Context: single threaded during gadget setup - * - * Returns zero on success, else negative errno. - */ -static int __init audio_bind_config(struct usb_configuration *c) -{ - struct f_audio *audio; - int status; - - /* allocate and initialize one new instance */ - audio = kzalloc(sizeof *audio, GFP_KERNEL); - if (!audio) - return -ENOMEM; - - audio->card.func.name = "g_audio"; - audio->card.gadget = c->cdev->gadget; - - INIT_LIST_HEAD(&audio->play_queue); - spin_lock_init(&audio->lock); - - /* set up ASLA audio devices */ - status = gaudio_setup(&audio->card); - if (status < 0) - goto setup_fail; - audio->card.func.strings = uac1_strings; - audio->card.func.bind = f_audio_bind; - audio->card.func.unbind = old_f_audio_unbind; - audio->card.func.set_alt = f_audio_set_alt; - audio->card.func.setup = f_audio_setup; - audio->card.func.disable = f_audio_disable; - - control_selector_init(audio); - - INIT_WORK(&audio->playback_work, f_audio_playback_work); - - status = usb_add_function(c, &audio->card.func); - if (status) - goto add_fail; - - INFO(c->cdev, "audio_buf_size %d, req_buf_size %d, req_count %d\n", - audio_buf_size, req_buf_size, req_count); - - return status; - -add_fail: - gaudio_cleanup(); -setup_fail: - kfree(audio); - return status; -} -#else static void f_audio_free_inst(struct usb_function_instance *f) { struct f_uac1_opts *opts; @@ -932,4 +833,3 @@ static struct usb_function *f_audio_alloc(struct usb_function_instance *fi) DECLARE_USB_FUNCTION_INIT(uac1, f_audio_alloc_inst, f_audio_alloc); MODULE_LICENSE("GPL"); MODULE_AUTHOR("Bryan Wu"); -#endif diff --git a/drivers/usb/gadget/function/u_uac1.c b/drivers/usb/gadget/function/u_uac1.c index 9a55e5c..a44a07f 100644 --- a/drivers/usb/gadget/function/u_uac1.c +++ b/drivers/usb/gadget/function/u_uac1.c @@ -24,23 +24,6 @@ * This component encapsulates the ALSA devices for USB audio gadget */ -#ifdef USBF_UAC1_INCLUDED -#define FILE_PCM_PLAYBACK "/dev/snd/pcmC0D0p" -#define FILE_PCM_CAPTURE "/dev/snd/pcmC0D0c" -#define FILE_CONTROL "/dev/snd/controlC0" - -static char *fn_play = FILE_PCM_PLAYBACK; -module_param(fn_play, charp, S_IRUGO); -MODULE_PARM_DESC(fn_play, "Playback PCM device file name"); - -static char *fn_cap = FILE_PCM_CAPTURE; -module_param(fn_cap, charp, S_IRUGO); -MODULE_PARM_DESC(fn_cap, "Capture PCM device file name"); - -static char *fn_cntl = FILE_CONTROL; -module_param(fn_cntl, charp, S_IRUGO); -MODULE_PARM_DESC(fn_cntl, "Control device file name"); -#endif /*-------------------------------------------------------------------------*/ /** @@ -222,7 +205,6 @@ static int gaudio_open_snd_dev(struct gaudio *card) { struct snd_pcm_file *pcm_file; struct gaudio_snd_dev *snd; -#ifndef USBF_UAC1_INCLUDED struct f_uac1_opts *opts; char *fn_play, *fn_cap, *fn_cntl; @@ -230,7 +212,6 @@ static int gaudio_open_snd_dev(struct gaudio *card) fn_play = opts->fn_play; fn_cap = opts->fn_cap; fn_cntl = opts->fn_cntl; -#endif if (!card) return -ENODEV; @@ -304,9 +285,6 @@ static int gaudio_close_snd_dev(struct gaudio *gau) return 0; } -#ifdef USBF_UAC1_INCLUDED -static struct gaudio *the_card; -#endif /** * gaudio_setup - setup ALSA interface and preparing for USB transfer * @@ -321,10 +299,6 @@ int gaudio_setup(struct gaudio *card) ret = gaudio_open_snd_dev(card); if (ret) ERROR(card, "we need at least one control device\n"); -#ifdef USBF_UAC1_INCLUDED - else if (!the_card) - the_card = card; -#endif return ret; @@ -335,17 +309,10 @@ int gaudio_setup(struct gaudio *card) * * This is called to free all resources allocated by @gaudio_setup(). */ -#ifdef USBF_UAC1_INCLUDED -void gaudio_cleanup(void) -#else void gaudio_cleanup(struct gaudio *the_card) -#endif { if (the_card) { gaudio_close_snd_dev(the_card); -#ifdef USBF_UAC1_INCLUDED - the_card = NULL; -#endif } } diff --git a/drivers/usb/gadget/function/u_uac1.h b/drivers/usb/gadget/function/u_uac1.h index 5b4fe9e..8507c27 100644 --- a/drivers/usb/gadget/function/u_uac1.h +++ b/drivers/usb/gadget/function/u_uac1.h @@ -63,11 +63,7 @@ struct f_uac1_opts { }; int gaudio_setup(struct gaudio *card); -#ifdef USBF_UAC1_INCLUDED -void gaudio_cleanup(void); -#else void gaudio_cleanup(struct gaudio *the_card); -#endif size_t u_audio_playback(struct gaudio *card, void *buf, size_t count); int u_audio_get_playback_channels(struct gaudio *card); -- cgit v1.1 From 807dccdba5c157c7131772bb6bd9a114a2ed9760 Mon Sep 17 00:00:00 2001 From: Andrzej Pietrasiewicz Date: Tue, 22 Jul 2014 19:58:41 +0200 Subject: usb: gadget: f_uac1: use usb_gstrings_attach Use the new usb_gstring_attach interface. Tested-by: Sebastian Reimers Signed-off-by: Andrzej Pietrasiewicz Signed-off-by: Felipe Balbi --- drivers/usb/gadget/function/f_uac1.c | 30 ++++++++++++------------------ 1 file changed, 12 insertions(+), 18 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/gadget/function/f_uac1.c b/drivers/usb/gadget/function/f_uac1.c index e0399d2..3457537 100644 --- a/drivers/usb/gadget/function/f_uac1.c +++ b/drivers/usb/gadget/function/f_uac1.c @@ -664,6 +664,7 @@ f_audio_bind(struct usb_configuration *c, struct usb_function *f) { struct usb_composite_dev *cdev = c->cdev; struct f_audio *audio = func_to_audio(f); + struct usb_string *us; int status; struct usb_ep *ep = NULL; struct f_uac1_opts *audio_opts; @@ -678,23 +679,17 @@ f_audio_bind(struct usb_configuration *c, struct usb_function *f) return status; audio_opts->bound = true; } - if (strings_uac1[0].id == 0) { - status = usb_string_ids_tab(c->cdev, strings_uac1); - if (status < 0) - goto fail; - ac_interface_desc.iInterface = strings_uac1[STR_AC_IF].id; - input_terminal_desc.iTerminal = - strings_uac1[STR_INPUT_TERMINAL].id; - input_terminal_desc.iChannelNames = - strings_uac1[STR_INPUT_TERMINAL_CH_NAMES].id; - feature_unit_desc.iFeature = strings_uac1[STR_FEAT_DESC_0].id; - output_terminal_desc.iTerminal = - strings_uac1[STR_OUTPUT_TERMINAL].id; - as_interface_alt_0_desc.iInterface = - strings_uac1[STR_AS_IF_ALT0].id; - as_interface_alt_1_desc.iInterface = - strings_uac1[STR_AS_IF_ALT1].id; - } + us = usb_gstrings_attach(cdev, uac1_strings, ARRAY_SIZE(strings_uac1)); + if (IS_ERR(us)) + return PTR_ERR(us); + ac_interface_desc.iInterface = us[STR_AC_IF].id; + input_terminal_desc.iTerminal = us[STR_INPUT_TERMINAL].id; + input_terminal_desc.iChannelNames = us[STR_INPUT_TERMINAL_CH_NAMES].id; + feature_unit_desc.iFeature = us[STR_FEAT_DESC_0].id; + output_terminal_desc.iTerminal = us[STR_OUTPUT_TERMINAL].id; + as_interface_alt_0_desc.iInterface = us[STR_AS_IF_ALT0].id; + as_interface_alt_1_desc.iInterface = us[STR_AS_IF_ALT1].id; + f_audio_build_desc(audio); @@ -815,7 +810,6 @@ static struct usb_function *f_audio_alloc(struct usb_function_instance *fi) INIT_LIST_HEAD(&audio->play_queue); spin_lock_init(&audio->lock); - audio->card.func.strings = uac1_strings; audio->card.func.bind = f_audio_bind; audio->card.func.unbind = f_audio_unbind; audio->card.func.set_alt = f_audio_set_alt; -- cgit v1.1 From bcec9784dd78abfa9d8ca8b7144f6e37ea6abfd5 Mon Sep 17 00:00:00 2001 From: Andrzej Pietrasiewicz Date: Tue, 22 Jul 2014 19:58:42 +0200 Subject: usb: gadget: f_uac1: use defined constants as defaults When configfs support is added the values in question will have to be used in two different places. Substitute them with defined constants to avoid duplicating magic numbers. Tested-by: Sebastian Reimers Signed-off-by: Andrzej Pietrasiewicz Signed-off-by: Felipe Balbi --- drivers/usb/gadget/function/f_uac1.c | 4 +--- drivers/usb/gadget/function/u_uac1.h | 8 ++++++++ drivers/usb/gadget/legacy/audio.c | 15 ++++----------- 3 files changed, 13 insertions(+), 14 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/gadget/function/f_uac1.c b/drivers/usb/gadget/function/f_uac1.c index 3457537..9a6c7ec 100644 --- a/drivers/usb/gadget/function/f_uac1.c +++ b/drivers/usb/gadget/function/f_uac1.c @@ -17,8 +17,6 @@ #include "u_uac1.h" -#define OUT_EP_MAX_PACKET_SIZE 200 - static int generic_set_cmd(struct usb_audio_control *con, u8 cmd, int value); static int generic_get_cmd(struct usb_audio_control *con, u8 cmd); @@ -173,7 +171,7 @@ static struct usb_endpoint_descriptor as_out_ep_desc = { .bEndpointAddress = USB_DIR_OUT, .bmAttributes = USB_ENDPOINT_SYNC_ADAPTIVE | USB_ENDPOINT_XFER_ISOC, - .wMaxPacketSize = __constant_cpu_to_le16(OUT_EP_MAX_PACKET_SIZE), + .wMaxPacketSize = cpu_to_le16(UAC1_OUT_EP_MAX_PACKET_SIZE), .bInterval = 4, }; diff --git a/drivers/usb/gadget/function/u_uac1.h b/drivers/usb/gadget/function/u_uac1.h index 8507c27..214441d 100644 --- a/drivers/usb/gadget/function/u_uac1.h +++ b/drivers/usb/gadget/function/u_uac1.h @@ -23,6 +23,14 @@ #include "gadget_chips.h" +#define FILE_PCM_PLAYBACK "/dev/snd/pcmC0D0p" +#define FILE_PCM_CAPTURE "/dev/snd/pcmC0D0c" +#define FILE_CONTROL "/dev/snd/controlC0" + +#define UAC1_OUT_EP_MAX_PACKET_SIZE 200 +#define UAC1_REQ_COUNT 256 +#define UAC1_AUDIO_BUF_SIZE 48000 + /* * This represents the USB side of an audio card device, managed by a USB * function which provides control and stream interfaces. diff --git a/drivers/usb/gadget/legacy/audio.c b/drivers/usb/gadget/legacy/audio.c index bb40b61..f46a395 100644 --- a/drivers/usb/gadget/legacy/audio.c +++ b/drivers/usb/gadget/legacy/audio.c @@ -54,9 +54,7 @@ static int c_ssize = UAC2_DEF_CSSIZE; module_param(c_ssize, uint, S_IRUGO); MODULE_PARM_DESC(c_ssize, "Capture Sample Size(bytes)"); #else -#define FILE_PCM_PLAYBACK "/dev/snd/pcmC0D0p" -#define FILE_PCM_CAPTURE "/dev/snd/pcmC0D0c" -#define FILE_CONTROL "/dev/snd/controlC0" +#include "u_uac1.h" static char *fn_play = FILE_PCM_PLAYBACK; module_param(fn_play, charp, S_IRUGO); @@ -70,16 +68,15 @@ static char *fn_cntl = FILE_CONTROL; module_param(fn_cntl, charp, S_IRUGO); MODULE_PARM_DESC(fn_cntl, "Control device file name"); -#define OUT_EP_MAX_PACKET_SIZE 200 -static int req_buf_size = OUT_EP_MAX_PACKET_SIZE; +static int req_buf_size = UAC1_OUT_EP_MAX_PACKET_SIZE; module_param(req_buf_size, int, S_IRUGO); MODULE_PARM_DESC(req_buf_size, "ISO OUT endpoint request buffer size"); -static int req_count = 256; +static int req_count = UAC1_REQ_COUNT; module_param(req_count, int, S_IRUGO); MODULE_PARM_DESC(req_count, "ISO OUT endpoint request count"); -static int audio_buf_size = 48000; +static int audio_buf_size = UAC1_AUDIO_BUF_SIZE; module_param(audio_buf_size, int, S_IRUGO); MODULE_PARM_DESC(audio_buf_size, "Audio buffer size"); #endif @@ -111,10 +108,6 @@ static struct usb_function_instance *fi_uac1; static struct usb_function *f_uac1; #endif -#ifdef CONFIG_GADGET_UAC1 -#include "u_uac1.h" -#endif - /*-------------------------------------------------------------------------*/ /* DO NOT REUSE THESE IDs with a protocol-incompatible driver!! Ever!! -- cgit v1.1 From 0854611a19ae4dfa56569e6f640017a1d2dd3312 Mon Sep 17 00:00:00 2001 From: Andrzej Pietrasiewicz Date: Tue, 22 Jul 2014 19:58:43 +0200 Subject: usb: gadget: f_uac1: add configfs support Add support for using f_uac1 function as a component of a gadget composed with configfs. Tested-by: Sebastian Reimers Signed-off-by: Andrzej Pietrasiewicz Signed-off-by: Felipe Balbi --- drivers/usb/gadget/function/f_uac1.c | 158 +++++++++++++++++++++++++++++++++++ drivers/usb/gadget/function/u_uac1.h | 7 +- 2 files changed, 164 insertions(+), 1 deletion(-) (limited to 'drivers/usb') diff --git a/drivers/usb/gadget/function/f_uac1.c b/drivers/usb/gadget/function/f_uac1.c index 9a6c7ec..f7b2032 100644 --- a/drivers/usb/gadget/function/f_uac1.c +++ b/drivers/usb/gadget/function/f_uac1.c @@ -760,12 +760,150 @@ static int control_selector_init(struct f_audio *audio) return 0; } +static inline struct f_uac1_opts *to_f_uac1_opts(struct config_item *item) +{ + return container_of(to_config_group(item), struct f_uac1_opts, + func_inst.group); +} + +CONFIGFS_ATTR_STRUCT(f_uac1_opts); +CONFIGFS_ATTR_OPS(f_uac1_opts); + +static void f_uac1_attr_release(struct config_item *item) +{ + struct f_uac1_opts *opts = to_f_uac1_opts(item); + + usb_put_function_instance(&opts->func_inst); +} + +static struct configfs_item_operations f_uac1_item_ops = { + .release = f_uac1_attr_release, + .show_attribute = f_uac1_opts_attr_show, + .store_attribute = f_uac1_opts_attr_store, +}; + +#define UAC1_INT_ATTRIBUTE(name) \ +static ssize_t f_uac1_opts_##name##_show(struct f_uac1_opts *opts, \ + char *page) \ +{ \ + int result; \ + \ + mutex_lock(&opts->lock); \ + result = sprintf(page, "%u\n", opts->name); \ + mutex_unlock(&opts->lock); \ + \ + return result; \ +} \ + \ +static ssize_t f_uac1_opts_##name##_store(struct f_uac1_opts *opts, \ + const char *page, size_t len) \ +{ \ + int ret; \ + u32 num; \ + \ + mutex_lock(&opts->lock); \ + if (opts->refcnt) { \ + ret = -EBUSY; \ + goto end; \ + } \ + \ + ret = kstrtou32(page, 0, &num); \ + if (ret) \ + goto end; \ + \ + opts->name = num; \ + ret = len; \ + \ +end: \ + mutex_unlock(&opts->lock); \ + return ret; \ +} \ + \ +static struct f_uac1_opts_attribute f_uac1_opts_##name = \ + __CONFIGFS_ATTR(name, S_IRUGO | S_IWUSR, \ + f_uac1_opts_##name##_show, \ + f_uac1_opts_##name##_store) + +UAC1_INT_ATTRIBUTE(req_buf_size); +UAC1_INT_ATTRIBUTE(req_count); +UAC1_INT_ATTRIBUTE(audio_buf_size); + +#define UAC1_STR_ATTRIBUTE(name) \ +static ssize_t f_uac1_opts_##name##_show(struct f_uac1_opts *opts, \ + char *page) \ +{ \ + int result; \ + \ + mutex_lock(&opts->lock); \ + result = sprintf(page, "%s\n", opts->name); \ + mutex_unlock(&opts->lock); \ + \ + return result; \ +} \ + \ +static ssize_t f_uac1_opts_##name##_store(struct f_uac1_opts *opts, \ + const char *page, size_t len) \ +{ \ + int ret = -EBUSY; \ + char *tmp; \ + \ + mutex_lock(&opts->lock); \ + if (opts->refcnt) \ + goto end; \ + \ + tmp = kstrndup(page, len, GFP_KERNEL); \ + if (tmp) { \ + ret = -ENOMEM; \ + goto end; \ + } \ + if (opts->name##_alloc) \ + kfree(opts->name); \ + opts->name##_alloc = true; \ + opts->name = tmp; \ + ret = len; \ + \ +end: \ + mutex_unlock(&opts->lock); \ + return ret; \ +} \ + \ +static struct f_uac1_opts_attribute f_uac1_opts_##name = \ + __CONFIGFS_ATTR(name, S_IRUGO | S_IWUSR, \ + f_uac1_opts_##name##_show, \ + f_uac1_opts_##name##_store) + +UAC1_STR_ATTRIBUTE(fn_play); +UAC1_STR_ATTRIBUTE(fn_cap); +UAC1_STR_ATTRIBUTE(fn_cntl); + +static struct configfs_attribute *f_uac1_attrs[] = { + &f_uac1_opts_req_buf_size.attr, + &f_uac1_opts_req_count.attr, + &f_uac1_opts_audio_buf_size.attr, + &f_uac1_opts_fn_play.attr, + &f_uac1_opts_fn_cap.attr, + &f_uac1_opts_fn_cntl.attr, + NULL, +}; + +static struct config_item_type f_uac1_func_type = { + .ct_item_ops = &f_uac1_item_ops, + .ct_attrs = f_uac1_attrs, + .ct_owner = THIS_MODULE, +}; + static void f_audio_free_inst(struct usb_function_instance *f) { struct f_uac1_opts *opts; opts = container_of(f, struct f_uac1_opts, func_inst); gaudio_cleanup(opts->card); + if (opts->fn_play_alloc) + kfree(opts->fn_play); + if (opts->fn_cap_alloc) + kfree(opts->fn_cap); + if (opts->fn_cntl_alloc) + kfree(opts->fn_cntl); kfree(opts); } @@ -777,16 +915,31 @@ static struct usb_function_instance *f_audio_alloc_inst(void) if (!opts) return ERR_PTR(-ENOMEM); + mutex_init(&opts->lock); opts->func_inst.free_func_inst = f_audio_free_inst; + config_group_init_type_name(&opts->func_inst.group, "", + &f_uac1_func_type); + + opts->req_buf_size = UAC1_OUT_EP_MAX_PACKET_SIZE; + opts->req_count = UAC1_REQ_COUNT; + opts->audio_buf_size = UAC1_AUDIO_BUF_SIZE; + opts->fn_play = FILE_PCM_PLAYBACK; + opts->fn_cap = FILE_PCM_CAPTURE; + opts->fn_cntl = FILE_CONTROL; return &opts->func_inst; } static void f_audio_free(struct usb_function *f) { struct f_audio *audio = func_to_audio(f); + struct f_uac1_opts *opts; + opts = container_of(f->fi, struct f_uac1_opts, func_inst); kfree(audio); + mutex_lock(&opts->lock); + --opts->refcnt; + mutex_unlock(&opts->lock); } static void f_audio_unbind(struct usb_configuration *c, struct usb_function *f) @@ -797,6 +950,7 @@ static void f_audio_unbind(struct usb_configuration *c, struct usb_function *f) static struct usb_function *f_audio_alloc(struct usb_function_instance *fi) { struct f_audio *audio; + struct f_uac1_opts *opts; /* allocate and initialize one new instance */ audio = kzalloc(sizeof(*audio), GFP_KERNEL); @@ -805,6 +959,10 @@ static struct usb_function *f_audio_alloc(struct usb_function_instance *fi) audio->card.func.name = "g_audio"; + opts = container_of(fi, struct f_uac1_opts, func_inst); + mutex_lock(&opts->lock); + ++opts->refcnt; + mutex_unlock(&opts->lock); INIT_LIST_HEAD(&audio->play_queue); spin_lock_init(&audio->lock); diff --git a/drivers/usb/gadget/function/u_uac1.h b/drivers/usb/gadget/function/u_uac1.h index 214441d..f8b17fe 100644 --- a/drivers/usb/gadget/function/u_uac1.h +++ b/drivers/usb/gadget/function/u_uac1.h @@ -66,8 +66,13 @@ struct f_uac1_opts { char *fn_play; char *fn_cap; char *fn_cntl; - bool bound; + unsigned bound:1; + unsigned fn_play_alloc:1; + unsigned fn_cap_alloc:1; + unsigned fn_cntl_alloc:1; struct gaudio *card; + struct mutex lock; + int refcnt; }; int gaudio_setup(struct gaudio *card); -- cgit v1.1 From 6bc17375d2e787e5c7ef94bfb4e194b6c690a4a7 Mon Sep 17 00:00:00 2001 From: Michael Grzeschik Date: Thu, 21 Aug 2014 16:54:43 +0200 Subject: usb: gadget: uvc: Change KERN_INFO to KERN_DEBUG on request shutdown The disconnect of the USB Device is a common pattern for an UVC Camera. In many cases this will give us an meaningless information for all buffers that couldn't be enqueued. That patch changes this to KERN_DEBUG. Signed-off-by: Michael Grzeschik Signed-off-by: Laurent Pinchart Signed-off-by: Felipe Balbi --- drivers/usb/gadget/function/uvc_video.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/usb') diff --git a/drivers/usb/gadget/function/uvc_video.c b/drivers/usb/gadget/function/uvc_video.c index 71e896d..beba916 100644 --- a/drivers/usb/gadget/function/uvc_video.c +++ b/drivers/usb/gadget/function/uvc_video.c @@ -171,7 +171,7 @@ uvc_video_complete(struct usb_ep *ep, struct usb_request *req) break; case -ESHUTDOWN: /* disconnect from host. */ - printk(KERN_INFO "VS request cancelled.\n"); + printk(KERN_DEBUG "VS request cancelled.\n"); uvc_queue_cancel(queue, 1); goto requeue; -- cgit v1.1 From ee7ec7f6b39d2ae25dca000398929edaa2ce412d Mon Sep 17 00:00:00 2001 From: Philipp Zabel Date: Thu, 21 Aug 2014 16:54:44 +0200 Subject: usb: gadget: uvc: Add support for DMABUF importing Activate the videobuf2 DMABUF support. As vb2-vmalloc supports the importer role only, exporting buffers isn't supported yet. When the exporter role will be implemented in vb2-vmalloc the UVC gadget driver will automatically gain support for it. Signed-off-by: Philipp Zabel Signed-off-by: Laurent Pinchart Signed-off-by: Felipe Balbi --- drivers/usb/gadget/function/uvc_queue.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/usb') diff --git a/drivers/usb/gadget/function/uvc_queue.c b/drivers/usb/gadget/function/uvc_queue.c index 1c29bc9..8590f9f 100644 --- a/drivers/usb/gadget/function/uvc_queue.c +++ b/drivers/usb/gadget/function/uvc_queue.c @@ -132,7 +132,7 @@ static int uvc_queue_init(struct uvc_video_queue *queue, int ret; queue->queue.type = type; - queue->queue.io_modes = VB2_MMAP | VB2_USERPTR; + queue->queue.io_modes = VB2_MMAP | VB2_USERPTR | VB2_DMABUF; queue->queue.drv_priv = queue; queue->queue.buf_struct_size = sizeof(struct uvc_buffer); queue->queue.ops = &uvc_queue_qops; -- cgit v1.1 From e73798572e115f73066567f5840d4e5c21da70a8 Mon Sep 17 00:00:00 2001 From: Andrzej Pietrasiewicz Date: Thu, 21 Aug 2014 16:54:45 +0200 Subject: usb: gadget: f_uvc: fix potential memory leak If uvc->control_buf is successfuly allocated but uvc->control_req is not, uvc->control_buf is not freed in the error recovery path. With this patch applied uvc->control_buf is freed unconditionally; if it happens to be NULL kfree on it is safe anyway. Signed-off-by: Andrzej Pietrasiewicz Acked-by: Sebastian Andrzej Siewior Signed-off-by: Laurent Pinchart Signed-off-by: Felipe Balbi --- drivers/usb/gadget/function/f_uvc.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/gadget/function/f_uvc.c b/drivers/usb/gadget/function/f_uvc.c index e2a1f50..ff4340a 100644 --- a/drivers/usb/gadget/function/f_uvc.c +++ b/drivers/usb/gadget/function/f_uvc.c @@ -720,10 +720,9 @@ error: if (uvc->video.ep) uvc->video.ep->driver_data = NULL; - if (uvc->control_req) { + if (uvc->control_req) usb_ep_free_request(cdev->gadget->ep0, uvc->control_req); - kfree(uvc->control_buf); - } + kfree(uvc->control_buf); usb_free_all_descriptors(f); return ret; -- cgit v1.1 From 84d1b78af9b35d706de2d1c115b9194bcaaa97b0 Mon Sep 17 00:00:00 2001 From: Michael Grzeschik Date: Thu, 21 Aug 2014 16:54:46 +0200 Subject: usb: gadget: uvc: remove DRIVER_VERSION{,_NUMBER} As the driver is in mainline we can remove the version numbers. Signed-off-by: Michael Grzeschik Signed-off-by: Laurent Pinchart Signed-off-by: Felipe Balbi --- drivers/usb/gadget/function/uvc.h | 3 --- drivers/usb/gadget/function/uvc_v4l2.c | 2 +- 2 files changed, 1 insertion(+), 4 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/gadget/function/uvc.h b/drivers/usb/gadget/function/uvc.h index 7a9111d..0a283b1 100644 --- a/drivers/usb/gadget/function/uvc.h +++ b/drivers/usb/gadget/function/uvc.h @@ -96,9 +96,6 @@ extern unsigned int uvc_gadget_trace_param; * Driver specific constants */ -#define DRIVER_VERSION "0.1.0" -#define DRIVER_VERSION_NUMBER KERNEL_VERSION(0, 1, 0) - #define UVC_NUM_REQUESTS 4 #define UVC_MAX_REQUEST_SIZE 64 #define UVC_MAX_EVENTS 4 diff --git a/drivers/usb/gadget/function/uvc_v4l2.c b/drivers/usb/gadget/function/uvc_v4l2.c index ad48e81..bcd71ce 100644 --- a/drivers/usb/gadget/function/uvc_v4l2.c +++ b/drivers/usb/gadget/function/uvc_v4l2.c @@ -178,7 +178,7 @@ uvc_v4l2_do_ioctl(struct file *file, unsigned int cmd, void *arg) strlcpy(cap->card, cdev->gadget->name, sizeof(cap->card)); strlcpy(cap->bus_info, dev_name(&cdev->gadget->dev), sizeof cap->bus_info); - cap->version = DRIVER_VERSION_NUMBER; + cap->version = LINUX_VERSION_CODE; cap->capabilities = V4L2_CAP_VIDEO_OUTPUT | V4L2_CAP_STREAMING; break; } -- cgit v1.1 From d0ee68b59e6aa33221445dc555efac3736b89026 Mon Sep 17 00:00:00 2001 From: Stefan Agner Date: Mon, 28 Jul 2014 16:57:29 +0200 Subject: usb: phy: mxs: Add VF610 USB PHY support This adds support for the USB PHY in Vybrid VF610. We assume that the disconnection without VBUS is also needed for Vybrid. Tests showed, without MXS_PHY_NEED_IP_FIX, enumeration of devices behind a USB Hub fails with errors: [ 215.163507] usb usb1-port1: cannot reset (err = -32) [ 215.170498] usb usb1-port1: cannot reset (err = -32) [ 215.185120] usb usb1-port1: cannot reset (err = -32) [ 215.191345] usb usb1-port1: cannot reset (err = -32) [ 215.202487] usb usb1-port1: cannot reset (err = -32) [ 215.207718] usb usb1-port1: Cannot enable. Maybe the USB cable is bad? [ 215.219317] usb usb1-port1: unable to enumerate USB device Hence we also enable the MXS_PHY_NEED_IP_FIX flag. Acked-by: Peter Chen Signed-off-by: Stefan Agner Signed-off-by: Felipe Balbi --- drivers/usb/phy/phy-mxs-usb.c | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'drivers/usb') diff --git a/drivers/usb/phy/phy-mxs-usb.c b/drivers/usb/phy/phy-mxs-usb.c index c42bdf0..8c2f23b 100644 --- a/drivers/usb/phy/phy-mxs-usb.c +++ b/drivers/usb/phy/phy-mxs-usb.c @@ -125,10 +125,16 @@ static const struct mxs_phy_data imx6sl_phy_data = { MXS_PHY_NEED_IP_FIX, }; +static const struct mxs_phy_data vf610_phy_data = { + .flags = MXS_PHY_DISCONNECT_LINE_WITHOUT_VBUS | + MXS_PHY_NEED_IP_FIX, +}; + static const struct of_device_id mxs_phy_dt_ids[] = { { .compatible = "fsl,imx6sl-usbphy", .data = &imx6sl_phy_data, }, { .compatible = "fsl,imx6q-usbphy", .data = &imx6q_phy_data, }, { .compatible = "fsl,imx23-usbphy", .data = &imx23_phy_data, }, + { .compatible = "fsl,vf610-usbphy", .data = &vf610_phy_data, }, { /* sentinel */ } }; MODULE_DEVICE_TABLE(of, mxs_phy_dt_ids); -- cgit v1.1 From a1a4caf41ed8154c4e7b75b4e12c1a7d851e2137 Mon Sep 17 00:00:00 2001 From: Srinivas Kandagatla Date: Thu, 21 Aug 2014 07:45:10 +0100 Subject: usb: phy: msm: Make phy_reset clk and reset line optional. This patch makes the phy reset clk and reset line optional as this clk is not available on boards like IFC6410 with APQ8064. phy-reset clk is only used as argument to the mach level callbacks, so this patch adds condition before clk_get calls so that the driver wouldn't fail on SOCs which do not have this support. Signed-off-by: Srinivas Kandagatla Signed-off-by: Felipe Balbi --- drivers/usb/phy/phy-msm-usb.c | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/phy/phy-msm-usb.c b/drivers/usb/phy/phy-msm-usb.c index df8e1ba..45b9594 100644 --- a/drivers/usb/phy/phy-msm-usb.c +++ b/drivers/usb/phy/phy-msm-usb.c @@ -281,7 +281,7 @@ static int msm_otg_phy_clk_reset(struct msm_otg *motg) { int ret = 0; - if (motg->pdata->phy_clk_reset && motg->phy_reset_clk) + if (motg->pdata->phy_clk_reset) ret = motg->pdata->phy_clk_reset(motg->phy_reset_clk); else if (motg->phy_rst) ret = reset_control_reset(motg->phy_rst); @@ -1554,11 +1554,14 @@ static int msm_otg_probe(struct platform_device *pdev) phy = &motg->phy; phy->dev = &pdev->dev; - motg->phy_reset_clk = devm_clk_get(&pdev->dev, + if (motg->pdata->phy_clk_reset) { + motg->phy_reset_clk = devm_clk_get(&pdev->dev, np ? "phy" : "usb_phy_clk"); - if (IS_ERR(motg->phy_reset_clk)) { - dev_err(&pdev->dev, "failed to get usb_phy_clk\n"); - motg->phy_reset_clk = NULL; + + if (IS_ERR(motg->phy_reset_clk)) { + dev_err(&pdev->dev, "failed to get usb_phy_clk\n"); + return PTR_ERR(motg->phy_reset_clk); + } } motg->clk = devm_clk_get(&pdev->dev, np ? "core" : "usb_hs_clk"); -- cgit v1.1 From b8b0ea51b381a43c3179281a7aaf95b49f9f5f7b Mon Sep 17 00:00:00 2001 From: Richard Leitner Date: Thu, 21 Aug 2014 08:31:39 +0200 Subject: usb: gadget: serial: replace {V,}DBG macro with dev_{v,}dbg Replace the VDBG and DBG macro with the kernels "proper" debug macros (dev_vdbg and dev_dbg) in f_acm.c, f_obex.c & f_serial.c Signed-off-by: Richard Leitner Signed-off-by: Felipe Balbi --- drivers/usb/gadget/function/f_acm.c | 49 +++++++++++++++++++--------------- drivers/usb/gadget/function/f_obex.c | 28 +++++++++++-------- drivers/usb/gadget/function/f_serial.c | 19 +++++++------ 3 files changed, 56 insertions(+), 40 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/gadget/function/f_acm.c b/drivers/usb/gadget/function/f_acm.c index ab1065a..6da4685 100644 --- a/drivers/usb/gadget/function/f_acm.c +++ b/drivers/usb/gadget/function/f_acm.c @@ -313,15 +313,15 @@ static void acm_complete_set_line_coding(struct usb_ep *ep, struct usb_composite_dev *cdev = acm->port.func.config->cdev; if (req->status != 0) { - DBG(cdev, "acm ttyGS%d completion, err %d\n", - acm->port_num, req->status); + dev_dbg(&cdev->gadget->dev, "acm ttyGS%d completion, err %d\n", + acm->port_num, req->status); return; } /* normal completion */ if (req->actual != sizeof(acm->port_line_coding)) { - DBG(cdev, "acm ttyGS%d short resp, len %d\n", - acm->port_num, req->actual); + dev_dbg(&cdev->gadget->dev, "acm ttyGS%d short resp, len %d\n", + acm->port_num, req->actual); usb_ep_set_halt(ep); } else { struct usb_cdc_line_coding *value = req->buf; @@ -397,14 +397,16 @@ static int acm_setup(struct usb_function *f, const struct usb_ctrlrequest *ctrl) default: invalid: - VDBG(cdev, "invalid control req%02x.%02x v%04x i%04x l%d\n", - ctrl->bRequestType, ctrl->bRequest, - w_value, w_index, w_length); + dev_vdbg(&cdev->gadget->dev, + "invalid control req%02x.%02x v%04x i%04x l%d\n", + ctrl->bRequestType, ctrl->bRequest, + w_value, w_index, w_length); } /* respond with data transfer or status phase? */ if (value >= 0) { - DBG(cdev, "acm ttyGS%d req%02x.%02x v%04x i%04x l%d\n", + dev_dbg(&cdev->gadget->dev, + "acm ttyGS%d req%02x.%02x v%04x i%04x l%d\n", acm->port_num, ctrl->bRequestType, ctrl->bRequest, w_value, w_index, w_length); req->zero = 0; @@ -428,10 +430,12 @@ static int acm_set_alt(struct usb_function *f, unsigned intf, unsigned alt) if (intf == acm->ctrl_id) { if (acm->notify->driver_data) { - VDBG(cdev, "reset acm control interface %d\n", intf); + dev_vdbg(&cdev->gadget->dev, + "reset acm control interface %d\n", intf); usb_ep_disable(acm->notify); } else { - VDBG(cdev, "init acm ctrl interface %d\n", intf); + dev_vdbg(&cdev->gadget->dev, + "init acm ctrl interface %d\n", intf); if (config_ep_by_speed(cdev->gadget, f, acm->notify)) return -EINVAL; } @@ -440,11 +444,13 @@ static int acm_set_alt(struct usb_function *f, unsigned intf, unsigned alt) } else if (intf == acm->data_id) { if (acm->port.in->driver_data) { - DBG(cdev, "reset acm ttyGS%d\n", acm->port_num); + dev_dbg(&cdev->gadget->dev, + "reset acm ttyGS%d\n", acm->port_num); gserial_disconnect(&acm->port); } if (!acm->port.in->desc || !acm->port.out->desc) { - DBG(cdev, "activate acm ttyGS%d\n", acm->port_num); + dev_dbg(&cdev->gadget->dev, + "activate acm ttyGS%d\n", acm->port_num); if (config_ep_by_speed(cdev->gadget, f, acm->port.in) || config_ep_by_speed(cdev->gadget, f, @@ -467,7 +473,7 @@ static void acm_disable(struct usb_function *f) struct f_acm *acm = func_to_acm(f); struct usb_composite_dev *cdev = f->config->cdev; - DBG(cdev, "acm ttyGS%d deactivated\n", acm->port_num); + dev_dbg(&cdev->gadget->dev, "acm ttyGS%d deactivated\n", acm->port_num); gserial_disconnect(&acm->port); usb_ep_disable(acm->notify); acm->notify->driver_data = NULL; @@ -537,8 +543,8 @@ static int acm_notify_serial_state(struct f_acm *acm) spin_lock(&acm->lock); if (acm->notify_req) { - DBG(cdev, "acm ttyGS%d serial state %04x\n", - acm->port_num, acm->serial_state); + dev_dbg(&cdev->gadget->dev, "acm ttyGS%d serial state %04x\n", + acm->port_num, acm->serial_state); status = acm_cdc_notify(acm, USB_CDC_NOTIFY_SERIAL_STATE, 0, &acm->serial_state, sizeof(acm->serial_state)); } else { @@ -691,12 +697,13 @@ acm_bind(struct usb_configuration *c, struct usb_function *f) if (status) goto fail; - DBG(cdev, "acm ttyGS%d: %s speed IN/%s OUT/%s NOTIFY/%s\n", - acm->port_num, - gadget_is_superspeed(c->cdev->gadget) ? "super" : - gadget_is_dualspeed(c->cdev->gadget) ? "dual" : "full", - acm->port.in->name, acm->port.out->name, - acm->notify->name); + dev_dbg(&cdev->gadget->dev, + "acm ttyGS%d: %s speed IN/%s OUT/%s NOTIFY/%s\n", + acm->port_num, + gadget_is_superspeed(c->cdev->gadget) ? "super" : + gadget_is_dualspeed(c->cdev->gadget) ? "dual" : "full", + acm->port.in->name, acm->port.out->name, + acm->notify->name); return 0; fail: diff --git a/drivers/usb/gadget/function/f_obex.c b/drivers/usb/gadget/function/f_obex.c index aebae18..5f40080 100644 --- a/drivers/usb/gadget/function/f_obex.c +++ b/drivers/usb/gadget/function/f_obex.c @@ -200,19 +200,22 @@ static int obex_set_alt(struct usb_function *f, unsigned intf, unsigned alt) if (alt != 0) goto fail; /* NOP */ - DBG(cdev, "reset obex ttyGS%d control\n", obex->port_num); + dev_dbg(&cdev->gadget->dev, + "reset obex ttyGS%d control\n", obex->port_num); } else if (intf == obex->data_id) { if (alt > 1) goto fail; if (obex->port.in->driver_data) { - DBG(cdev, "reset obex ttyGS%d\n", obex->port_num); + dev_dbg(&cdev->gadget->dev, + "reset obex ttyGS%d\n", obex->port_num); gserial_disconnect(&obex->port); } if (!obex->port.in->desc || !obex->port.out->desc) { - DBG(cdev, "init obex ttyGS%d\n", obex->port_num); + dev_dbg(&cdev->gadget->dev, + "init obex ttyGS%d\n", obex->port_num); if (config_ep_by_speed(cdev->gadget, f, obex->port.in) || config_ep_by_speed(cdev->gadget, f, @@ -224,7 +227,8 @@ static int obex_set_alt(struct usb_function *f, unsigned intf, unsigned alt) } if (alt == 1) { - DBG(cdev, "activate obex ttyGS%d\n", obex->port_num); + dev_dbg(&cdev->gadget->dev, + "activate obex ttyGS%d\n", obex->port_num); gserial_connect(&obex->port, obex->port_num); } @@ -252,7 +256,7 @@ static void obex_disable(struct usb_function *f) struct f_obex *obex = func_to_obex(f); struct usb_composite_dev *cdev = f->config->cdev; - DBG(cdev, "obex ttyGS%d disable\n", obex->port_num); + dev_dbg(&cdev->gadget->dev, "obex ttyGS%d disable\n", obex->port_num); gserial_disconnect(&obex->port); } @@ -269,7 +273,8 @@ static void obex_connect(struct gserial *g) status = usb_function_activate(&g->func); if (status) - DBG(cdev, "obex ttyGS%d function activate --> %d\n", + dev_dbg(&cdev->gadget->dev, + "obex ttyGS%d function activate --> %d\n", obex->port_num, status); } @@ -284,7 +289,8 @@ static void obex_disconnect(struct gserial *g) status = usb_function_deactivate(&g->func); if (status) - DBG(cdev, "obex ttyGS%d function deactivate --> %d\n", + dev_dbg(&cdev->gadget->dev, + "obex ttyGS%d function deactivate --> %d\n", obex->port_num, status); } @@ -383,10 +389,10 @@ static int obex_bind(struct usb_configuration *c, struct usb_function *f) obex->can_activate = true; - DBG(cdev, "obex ttyGS%d: %s speed IN/%s OUT/%s\n", - obex->port_num, - gadget_is_dualspeed(c->cdev->gadget) ? "dual" : "full", - obex->port.in->name, obex->port.out->name); + dev_dbg(&cdev->gadget->dev, "obex ttyGS%d: %s speed IN/%s OUT/%s\n", + obex->port_num, + gadget_is_dualspeed(c->cdev->gadget) ? "dual" : "full", + obex->port.in->name, obex->port.out->name); return 0; diff --git a/drivers/usb/gadget/function/f_serial.c b/drivers/usb/gadget/function/f_serial.c index 9ecbcbf..2e02dfa 100644 --- a/drivers/usb/gadget/function/f_serial.c +++ b/drivers/usb/gadget/function/f_serial.c @@ -155,11 +155,13 @@ static int gser_set_alt(struct usb_function *f, unsigned intf, unsigned alt) /* we know alt == 0, so this is an activation or a reset */ if (gser->port.in->driver_data) { - DBG(cdev, "reset generic ttyGS%d\n", gser->port_num); + dev_dbg(&cdev->gadget->dev, + "reset generic ttyGS%d\n", gser->port_num); gserial_disconnect(&gser->port); } if (!gser->port.in->desc || !gser->port.out->desc) { - DBG(cdev, "activate generic ttyGS%d\n", gser->port_num); + dev_dbg(&cdev->gadget->dev, + "activate generic ttyGS%d\n", gser->port_num); if (config_ep_by_speed(cdev->gadget, f, gser->port.in) || config_ep_by_speed(cdev->gadget, f, gser->port.out)) { gser->port.in->desc = NULL; @@ -176,7 +178,8 @@ static void gser_disable(struct usb_function *f) struct f_gser *gser = func_to_gser(f); struct usb_composite_dev *cdev = f->config->cdev; - DBG(cdev, "generic ttyGS%d deactivated\n", gser->port_num); + dev_dbg(&cdev->gadget->dev, + "generic ttyGS%d deactivated\n", gser->port_num); gserial_disconnect(&gser->port); } @@ -239,11 +242,11 @@ static int gser_bind(struct usb_configuration *c, struct usb_function *f) gser_ss_function); if (status) goto fail; - DBG(cdev, "generic ttyGS%d: %s speed IN/%s OUT/%s\n", - gser->port_num, - gadget_is_superspeed(c->cdev->gadget) ? "super" : - gadget_is_dualspeed(c->cdev->gadget) ? "dual" : "full", - gser->port.in->name, gser->port.out->name); + dev_dbg(&cdev->gadget->dev, "generic ttyGS%d: %s speed IN/%s OUT/%s\n", + gser->port_num, + gadget_is_superspeed(c->cdev->gadget) ? "super" : + gadget_is_dualspeed(c->cdev->gadget) ? "dual" : "full", + gser->port.in->name, gser->port.out->name); return 0; fail: -- cgit v1.1 From c572a217d1b81209ae5a4fe09a96db758f86f10b Mon Sep 17 00:00:00 2001 From: Richard Leitner Date: Thu, 21 Aug 2014 08:57:28 +0200 Subject: usb: gadget: serial: remove PREFIX macro Remove the ttyGS PREFIX macro from u_serial.c and replace all occurences with the hardcoded ttyGS string. This macro was mostly used in a few debug/warning messages and a lot of hardcoded ttyGS existed beneath. It may have been used for renaming the tty, but if done so most debug messages would have ignored this. Due to the fact the usage of this PREFIX in all debug calls would have resulted in a hard to read/grep code it is removed completely. Signed-off-by: Richard Leitner Signed-off-by: Felipe Balbi --- drivers/usb/gadget/function/u_serial.c | 30 +++++++++++++----------------- 1 file changed, 13 insertions(+), 17 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/gadget/function/u_serial.c b/drivers/usb/gadget/function/u_serial.c index ad0aca8..491082a 100644 --- a/drivers/usb/gadget/function/u_serial.c +++ b/drivers/usb/gadget/function/u_serial.c @@ -55,11 +55,8 @@ * for a telephone or fax link. And ttyGS2 might be something that just * needs a simple byte stream interface for some messaging protocol that * is managed in userspace ... OBEX, PTP, and MTP have been mentioned. - */ - -#define PREFIX "ttyGS" - -/* + * + * * gserial is the lifecycle interface, used by USB functions * gs_port is the I/O nexus, used by the tty driver * tty_struct links to the tty/filesystem framework @@ -385,9 +382,9 @@ __acquires(&port->port_lock) list_del(&req->list); req->zero = (gs_buf_data_avail(&port->port_write_buf) == 0); - pr_vdebug(PREFIX "%d: tx len=%d, 0x%02x 0x%02x 0x%02x ...\n", - port->port_num, len, *((u8 *)req->buf), - *((u8 *)req->buf+1), *((u8 *)req->buf+2)); + pr_vdebug("ttyGS%d: tx len=%d, 0x%02x 0x%02x 0x%02x ...\n", + port->port_num, len, *((u8 *)req->buf), + *((u8 *)req->buf+1), *((u8 *)req->buf+2)); /* Drop lock while we call out of driver; completions * could be issued while we do so. Disconnection may @@ -503,13 +500,13 @@ static void gs_rx_push(unsigned long _port) switch (req->status) { case -ESHUTDOWN: disconnect = true; - pr_vdebug(PREFIX "%d: shutdown\n", port->port_num); + pr_vdebug("ttyGS%d: shutdown\n", port->port_num); break; default: /* presumably a transient fault */ - pr_warning(PREFIX "%d: unexpected RX status %d\n", - port->port_num, req->status); + pr_warn("ttyGS%d: unexpected RX status %d\n", + port->port_num, req->status); /* FALLTHROUGH */ case 0: /* normal completion */ @@ -537,9 +534,8 @@ static void gs_rx_push(unsigned long _port) if (count != size) { /* stop pushing; TTY layer can't handle more */ port->n_read += count; - pr_vdebug(PREFIX "%d: rx block %d/%d\n", - port->port_num, - count, req->actual); + pr_vdebug("ttyGS%d: rx block %d/%d\n", + port->port_num, count, req->actual); break; } port->n_read = 0; @@ -569,7 +565,7 @@ static void gs_rx_push(unsigned long _port) if (do_push) tasklet_schedule(&port->push); else - pr_warning(PREFIX "%d: RX not scheduled?\n", + pr_warn("ttyGS%d: RX not scheduled?\n", port->port_num); } } @@ -985,7 +981,7 @@ static void gs_unthrottle(struct tty_struct *tty) * read queue backs up enough we'll be NAKing OUT packets. */ tasklet_schedule(&port->push); - pr_vdebug(PREFIX "%d: unthrottle\n", port->port_num); + pr_vdebug("ttyGS%d: unthrottle\n", port->port_num); } spin_unlock_irqrestore(&port->port_lock, flags); } @@ -1295,7 +1291,7 @@ static int userial_init(void) return -ENOMEM; gs_tty_driver->driver_name = "g_serial"; - gs_tty_driver->name = PREFIX; + gs_tty_driver->name = "ttyGS"; /* uses dynamically assigned dev_t values */ gs_tty_driver->type = TTY_DRIVER_TYPE_SERIAL; -- cgit v1.1 From ef11982dd7a657512c362242508bb4021e0d67b6 Mon Sep 17 00:00:00 2001 From: Amit Virdi Date: Fri, 22 Aug 2014 14:36:36 +0530 Subject: usb: gadget: zero: Add support for interrupt EP Interrupt endpoints behave quite similar to the bulk endpoints with the difference that the endpoints expect data sending/reception request at particular intervals till the whole data has not been transmitted. The interrupt EP support is added to gadget zero. A new alternate setting (=2) has been added. It has 2 interrupt endpoints. The default parameters are set as: bInterval: 1 ms for FS and 8 uFrames (implying 1 ms) for HS/SS wMaxPacketSize: 64 bytes for FS and 1024 bytes for HS/SS However, the same can be overridden through the module parameter interface. The code is tested for HS and SS on a platform having DWC3 controller. Signed-off-by: Amit Virdi Signed-off-by: Felipe Balbi --- drivers/usb/gadget/function/f_loopback.c | 3 +- drivers/usb/gadget/function/f_sourcesink.c | 511 +++++++++++++++++++++++++++-- drivers/usb/gadget/function/g_zero.h | 13 +- drivers/usb/gadget/legacy/zero.c | 21 ++ 4 files changed, 526 insertions(+), 22 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/gadget/function/f_loopback.c b/drivers/usb/gadget/function/f_loopback.c index 4557cd0..bf043891 100644 --- a/drivers/usb/gadget/function/f_loopback.c +++ b/drivers/usb/gadget/function/f_loopback.c @@ -298,7 +298,8 @@ static void disable_loopback(struct f_loopback *loop) struct usb_composite_dev *cdev; cdev = loop->function.config->cdev; - disable_endpoints(cdev, loop->in_ep, loop->out_ep, NULL, NULL); + disable_endpoints(cdev, loop->in_ep, loop->out_ep, NULL, NULL, NULL, + NULL); VDBG(cdev, "%s disabled\n", loop->function.name); } diff --git a/drivers/usb/gadget/function/f_sourcesink.c b/drivers/usb/gadget/function/f_sourcesink.c index d3cd52d..7c091a3 100644 --- a/drivers/usb/gadget/function/f_sourcesink.c +++ b/drivers/usb/gadget/function/f_sourcesink.c @@ -23,6 +23,15 @@ #include "gadget_chips.h" #include "u_f.h" +#define USB_MS_TO_SS_INTERVAL(x) USB_MS_TO_HS_INTERVAL(x) + +enum eptype { + EP_CONTROL = 0, + EP_BULK, + EP_ISOC, + EP_INTERRUPT, +}; + /* * SOURCE/SINK FUNCTION ... a primary testing vehicle for USB peripheral * controller drivers. @@ -55,6 +64,8 @@ struct f_sourcesink { struct usb_ep *out_ep; struct usb_ep *iso_in_ep; struct usb_ep *iso_out_ep; + struct usb_ep *int_in_ep; + struct usb_ep *int_out_ep; int cur_alt; }; @@ -68,6 +79,10 @@ static unsigned isoc_interval; static unsigned isoc_maxpacket; static unsigned isoc_mult; static unsigned isoc_maxburst; +static unsigned int_interval; /* In ms */ +static unsigned int_maxpacket; +static unsigned int_mult; +static unsigned int_maxburst; static unsigned buflen; /*-------------------------------------------------------------------------*/ @@ -92,6 +107,16 @@ static struct usb_interface_descriptor source_sink_intf_alt1 = { /* .iInterface = DYNAMIC */ }; +static struct usb_interface_descriptor source_sink_intf_alt2 = { + .bLength = USB_DT_INTERFACE_SIZE, + .bDescriptorType = USB_DT_INTERFACE, + + .bAlternateSetting = 2, + .bNumEndpoints = 2, + .bInterfaceClass = USB_CLASS_VENDOR_SPEC, + /* .iInterface = DYNAMIC */ +}; + /* full speed support: */ static struct usb_endpoint_descriptor fs_source_desc = { @@ -130,6 +155,26 @@ static struct usb_endpoint_descriptor fs_iso_sink_desc = { .bInterval = 4, }; +static struct usb_endpoint_descriptor fs_int_source_desc = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + + .bEndpointAddress = USB_DIR_IN, + .bmAttributes = USB_ENDPOINT_XFER_INT, + .wMaxPacketSize = cpu_to_le16(64), + .bInterval = GZERO_INT_INTERVAL, +}; + +static struct usb_endpoint_descriptor fs_int_sink_desc = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + + .bEndpointAddress = USB_DIR_OUT, + .bmAttributes = USB_ENDPOINT_XFER_INT, + .wMaxPacketSize = cpu_to_le16(64), + .bInterval = GZERO_INT_INTERVAL, +}; + static struct usb_descriptor_header *fs_source_sink_descs[] = { (struct usb_descriptor_header *) &source_sink_intf_alt0, (struct usb_descriptor_header *) &fs_sink_desc, @@ -140,6 +185,10 @@ static struct usb_descriptor_header *fs_source_sink_descs[] = { (struct usb_descriptor_header *) &fs_source_desc, (struct usb_descriptor_header *) &fs_iso_sink_desc, (struct usb_descriptor_header *) &fs_iso_source_desc, + (struct usb_descriptor_header *) &source_sink_intf_alt2, +#define FS_ALT_IFC_2_OFFSET 8 + (struct usb_descriptor_header *) &fs_int_sink_desc, + (struct usb_descriptor_header *) &fs_int_source_desc, NULL, }; @@ -179,6 +228,24 @@ static struct usb_endpoint_descriptor hs_iso_sink_desc = { .bInterval = 4, }; +static struct usb_endpoint_descriptor hs_int_source_desc = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + + .bmAttributes = USB_ENDPOINT_XFER_INT, + .wMaxPacketSize = cpu_to_le16(1024), + .bInterval = USB_MS_TO_HS_INTERVAL(GZERO_INT_INTERVAL), +}; + +static struct usb_endpoint_descriptor hs_int_sink_desc = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + + .bmAttributes = USB_ENDPOINT_XFER_INT, + .wMaxPacketSize = cpu_to_le16(1024), + .bInterval = USB_MS_TO_HS_INTERVAL(GZERO_INT_INTERVAL), +}; + static struct usb_descriptor_header *hs_source_sink_descs[] = { (struct usb_descriptor_header *) &source_sink_intf_alt0, (struct usb_descriptor_header *) &hs_source_desc, @@ -189,6 +256,10 @@ static struct usb_descriptor_header *hs_source_sink_descs[] = { (struct usb_descriptor_header *) &hs_sink_desc, (struct usb_descriptor_header *) &hs_iso_source_desc, (struct usb_descriptor_header *) &hs_iso_sink_desc, + (struct usb_descriptor_header *) &source_sink_intf_alt2, +#define HS_ALT_IFC_2_OFFSET 8 + (struct usb_descriptor_header *) &hs_int_source_desc, + (struct usb_descriptor_header *) &hs_int_sink_desc, NULL, }; @@ -264,6 +335,42 @@ static struct usb_ss_ep_comp_descriptor ss_iso_sink_comp_desc = { .wBytesPerInterval = cpu_to_le16(1024), }; +static struct usb_endpoint_descriptor ss_int_source_desc = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + + .bmAttributes = USB_ENDPOINT_XFER_INT, + .wMaxPacketSize = cpu_to_le16(1024), + .bInterval = USB_MS_TO_SS_INTERVAL(GZERO_INT_INTERVAL), +}; + +struct usb_ss_ep_comp_descriptor ss_int_source_comp_desc = { + .bLength = USB_DT_SS_EP_COMP_SIZE, + .bDescriptorType = USB_DT_SS_ENDPOINT_COMP, + + .bMaxBurst = 0, + .bmAttributes = 0, + .wBytesPerInterval = cpu_to_le16(1024), +}; + +static struct usb_endpoint_descriptor ss_int_sink_desc = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + + .bmAttributes = USB_ENDPOINT_XFER_INT, + .wMaxPacketSize = cpu_to_le16(1024), + .bInterval = USB_MS_TO_SS_INTERVAL(GZERO_INT_INTERVAL), +}; + +struct usb_ss_ep_comp_descriptor ss_int_sink_comp_desc = { + .bLength = USB_DT_SS_EP_COMP_SIZE, + .bDescriptorType = USB_DT_SS_ENDPOINT_COMP, + + .bMaxBurst = 0, + .bmAttributes = 0, + .wBytesPerInterval = cpu_to_le16(1024), +}; + static struct usb_descriptor_header *ss_source_sink_descs[] = { (struct usb_descriptor_header *) &source_sink_intf_alt0, (struct usb_descriptor_header *) &ss_source_desc, @@ -280,6 +387,12 @@ static struct usb_descriptor_header *ss_source_sink_descs[] = { (struct usb_descriptor_header *) &ss_iso_source_comp_desc, (struct usb_descriptor_header *) &ss_iso_sink_desc, (struct usb_descriptor_header *) &ss_iso_sink_comp_desc, + (struct usb_descriptor_header *) &source_sink_intf_alt2, +#define SS_ALT_IFC_2_OFFSET 14 + (struct usb_descriptor_header *) &ss_int_source_desc, + (struct usb_descriptor_header *) &ss_int_source_comp_desc, + (struct usb_descriptor_header *) &ss_int_sink_desc, + (struct usb_descriptor_header *) &ss_int_sink_comp_desc, NULL, }; @@ -301,6 +414,21 @@ static struct usb_gadget_strings *sourcesink_strings[] = { }; /*-------------------------------------------------------------------------*/ +static const char *get_ep_string(enum eptype ep_type) +{ + switch (ep_type) { + case EP_ISOC: + return "ISOC-"; + case EP_INTERRUPT: + return "INTERRUPT-"; + case EP_CONTROL: + return "CTRL-"; + case EP_BULK: + return "BULK-"; + default: + return "UNKNOWN-"; + } +} static inline struct usb_request *ss_alloc_ep_req(struct usb_ep *ep, int len) { @@ -328,7 +456,8 @@ static void disable_ep(struct usb_composite_dev *cdev, struct usb_ep *ep) void disable_endpoints(struct usb_composite_dev *cdev, struct usb_ep *in, struct usb_ep *out, - struct usb_ep *iso_in, struct usb_ep *iso_out) + struct usb_ep *iso_in, struct usb_ep *iso_out, + struct usb_ep *int_in, struct usb_ep *int_out) { disable_ep(cdev, in); disable_ep(cdev, out); @@ -336,6 +465,10 @@ void disable_endpoints(struct usb_composite_dev *cdev, disable_ep(cdev, iso_in); if (iso_out) disable_ep(cdev, iso_out); + if (int_in) + disable_ep(cdev, int_in); + if (int_out) + disable_ep(cdev, int_out); } static int @@ -352,6 +485,7 @@ sourcesink_bind(struct usb_configuration *c, struct usb_function *f) return id; source_sink_intf_alt0.bInterfaceNumber = id; source_sink_intf_alt1.bInterfaceNumber = id; + source_sink_intf_alt2.bInterfaceNumber = id; /* allocate bulk endpoints */ ss->in_ep = usb_ep_autoconfig(cdev->gadget, &fs_source_desc); @@ -412,14 +546,55 @@ no_iso: if (isoc_maxpacket > 1024) isoc_maxpacket = 1024; + /* sanity check the interrupt module parameters */ + if (int_interval < 1) + int_interval = 1; + if (int_interval > 4096) + int_interval = 4096; + if (int_mult > 2) + int_mult = 2; + if (int_maxburst > 15) + int_maxburst = 15; + + /* fill in the FS interrupt descriptors from the module parameters */ + fs_int_source_desc.wMaxPacketSize = int_maxpacket > 64 ? + 64 : int_maxpacket; + fs_int_source_desc.bInterval = int_interval > 255 ? + 255 : int_interval; + fs_int_sink_desc.wMaxPacketSize = int_maxpacket > 64 ? + 64 : int_maxpacket; + fs_int_sink_desc.bInterval = int_interval > 255 ? + 255 : int_interval; + + /* allocate int endpoints */ + ss->int_in_ep = usb_ep_autoconfig(cdev->gadget, &fs_int_source_desc); + if (!ss->int_in_ep) + goto no_int; + ss->int_in_ep->driver_data = cdev; /* claim */ + + ss->int_out_ep = usb_ep_autoconfig(cdev->gadget, &fs_int_sink_desc); + if (ss->int_out_ep) { + ss->int_out_ep->driver_data = cdev; /* claim */ + } else { + ss->int_in_ep->driver_data = NULL; + ss->int_in_ep = NULL; +no_int: + fs_source_sink_descs[FS_ALT_IFC_2_OFFSET] = NULL; + hs_source_sink_descs[HS_ALT_IFC_2_OFFSET] = NULL; + ss_source_sink_descs[SS_ALT_IFC_2_OFFSET] = NULL; + } + + if (int_maxpacket > 1024) + int_maxpacket = 1024; + /* support high speed hardware */ hs_source_desc.bEndpointAddress = fs_source_desc.bEndpointAddress; hs_sink_desc.bEndpointAddress = fs_sink_desc.bEndpointAddress; /* - * Fill in the HS isoc descriptors from the module parameters. - * We assume that the user knows what they are doing and won't - * give parameters that their UDC doesn't support. + * Fill in the HS isoc and interrupt descriptors from the module + * parameters. We assume that the user knows what they are doing and + * won't give parameters that their UDC doesn't support. */ hs_iso_source_desc.wMaxPacketSize = isoc_maxpacket; hs_iso_source_desc.wMaxPacketSize |= isoc_mult << 11; @@ -432,6 +607,17 @@ no_iso: hs_iso_sink_desc.bInterval = isoc_interval; hs_iso_sink_desc.bEndpointAddress = fs_iso_sink_desc.bEndpointAddress; + hs_int_source_desc.wMaxPacketSize = int_maxpacket; + hs_int_source_desc.wMaxPacketSize |= int_mult << 11; + hs_int_source_desc.bInterval = USB_MS_TO_HS_INTERVAL(int_interval); + hs_int_source_desc.bEndpointAddress = + fs_int_source_desc.bEndpointAddress; + + hs_int_sink_desc.wMaxPacketSize = int_maxpacket; + hs_int_sink_desc.wMaxPacketSize |= int_mult << 11; + hs_int_sink_desc.bInterval = USB_MS_TO_HS_INTERVAL(int_interval); + hs_int_sink_desc.bEndpointAddress = fs_int_sink_desc.bEndpointAddress; + /* support super speed hardware */ ss_source_desc.bEndpointAddress = fs_source_desc.bEndpointAddress; @@ -439,9 +625,9 @@ no_iso: fs_sink_desc.bEndpointAddress; /* - * Fill in the SS isoc descriptors from the module parameters. - * We assume that the user knows what they are doing and won't - * give parameters that their UDC doesn't support. + * Fill in the SS isoc and interrupt descriptors from the module + * parameters. We assume that the user knows what they are doing and + * won't give parameters that their UDC doesn't support. */ ss_iso_source_desc.wMaxPacketSize = isoc_maxpacket; ss_iso_source_desc.bInterval = isoc_interval; @@ -460,17 +646,37 @@ no_iso: isoc_maxpacket * (isoc_mult + 1) * (isoc_maxburst + 1); ss_iso_sink_desc.bEndpointAddress = fs_iso_sink_desc.bEndpointAddress; + ss_int_source_desc.wMaxPacketSize = int_maxpacket; + ss_int_source_desc.bInterval = USB_MS_TO_SS_INTERVAL(int_interval); + ss_int_source_comp_desc.bmAttributes = int_mult; + ss_int_source_comp_desc.bMaxBurst = int_maxburst; + ss_int_source_comp_desc.wBytesPerInterval = + int_maxpacket * (int_mult + 1) * (int_maxburst + 1); + ss_int_source_desc.bEndpointAddress = + fs_int_source_desc.bEndpointAddress; + + ss_int_sink_desc.wMaxPacketSize = int_maxpacket; + ss_int_sink_desc.bInterval = USB_MS_TO_SS_INTERVAL(int_interval); + ss_int_sink_comp_desc.bmAttributes = int_mult; + ss_int_sink_comp_desc.bMaxBurst = int_maxburst; + ss_int_sink_comp_desc.wBytesPerInterval = + int_maxpacket * (int_mult + 1) * (int_maxburst + 1); + ss_int_sink_desc.bEndpointAddress = fs_int_sink_desc.bEndpointAddress; + ret = usb_assign_descriptors(f, fs_source_sink_descs, hs_source_sink_descs, ss_source_sink_descs); if (ret) return ret; - DBG(cdev, "%s speed %s: IN/%s, OUT/%s, ISO-IN/%s, ISO-OUT/%s\n", + DBG(cdev, "%s speed %s: IN/%s, OUT/%s, ISO-IN/%s, ISO-OUT/%s, " + "INT-IN/%s, INT-OUT/%s\n", (gadget_is_superspeed(c->cdev->gadget) ? "super" : (gadget_is_dualspeed(c->cdev->gadget) ? "dual" : "full")), f->name, ss->in_ep->name, ss->out_ep->name, ss->iso_in_ep ? ss->iso_in_ep->name : "", - ss->iso_out_ep ? ss->iso_out_ep->name : ""); + ss->iso_out_ep ? ss->iso_out_ep->name : "", + ss->int_in_ep ? ss->int_in_ep->name : "", + ss->int_out_ep ? ss->int_out_ep->name : ""); return 0; } @@ -601,14 +807,15 @@ static void source_sink_complete(struct usb_ep *ep, struct usb_request *req) } static int source_sink_start_ep(struct f_sourcesink *ss, bool is_in, - bool is_iso, int speed) + enum eptype ep_type, int speed) { struct usb_ep *ep; struct usb_request *req; int i, size, status; for (i = 0; i < 8; i++) { - if (is_iso) { + switch (ep_type) { + case EP_ISOC: switch (speed) { case USB_SPEED_SUPER: size = isoc_maxpacket * (isoc_mult + 1) * @@ -624,9 +831,28 @@ static int source_sink_start_ep(struct f_sourcesink *ss, bool is_in, } ep = is_in ? ss->iso_in_ep : ss->iso_out_ep; req = ss_alloc_ep_req(ep, size); - } else { + break; + case EP_INTERRUPT: + switch (speed) { + case USB_SPEED_SUPER: + size = int_maxpacket * (int_mult + 1) * + (int_maxburst + 1); + break; + case USB_SPEED_HIGH: + size = int_maxpacket * (int_mult + 1); + break; + default: + size = int_maxpacket > 1023 ? + 1023 : int_maxpacket; + break; + } + ep = is_in ? ss->int_in_ep : ss->int_out_ep; + req = ss_alloc_ep_req(ep, size); + break; + default: ep = is_in ? ss->in_ep : ss->out_ep; req = ss_alloc_ep_req(ep, 0); + break; } if (!req) @@ -644,12 +870,12 @@ static int source_sink_start_ep(struct f_sourcesink *ss, bool is_in, cdev = ss->function.config->cdev; ERROR(cdev, "start %s%s %s --> %d\n", - is_iso ? "ISO-" : "", is_in ? "IN" : "OUT", - ep->name, status); + get_ep_string(ep_type), is_in ? "IN" : "OUT", + ep->name, status); free_ep_req(ep, req); } - if (!is_iso) + if (!(ep_type == EP_ISOC)) break; } @@ -662,7 +888,7 @@ static void disable_source_sink(struct f_sourcesink *ss) cdev = ss->function.config->cdev; disable_endpoints(cdev, ss->in_ep, ss->out_ep, ss->iso_in_ep, - ss->iso_out_ep); + ss->iso_out_ep, ss->int_in_ep, ss->int_out_ep); VDBG(cdev, "%s disabled\n", ss->function.name); } @@ -674,6 +900,62 @@ enable_source_sink(struct usb_composite_dev *cdev, struct f_sourcesink *ss, int speed = cdev->gadget->speed; struct usb_ep *ep; + if (alt == 2) { + /* Configure for periodic interrupt endpoint */ + ep = ss->int_in_ep; + if (ep) { + result = config_ep_by_speed(cdev->gadget, + &(ss->function), ep); + if (result) + return result; + + result = usb_ep_enable(ep); + if (result < 0) + return result; + + ep->driver_data = ss; + result = source_sink_start_ep(ss, true, EP_INTERRUPT, + speed); + if (result < 0) { +fail1: + ep = ss->int_in_ep; + if (ep) { + usb_ep_disable(ep); + ep->driver_data = NULL; + } + return result; + } + } + + /* + * one interrupt endpoint reads (sinks) anything OUT (from the + * host) + */ + ep = ss->int_out_ep; + if (ep) { + result = config_ep_by_speed(cdev->gadget, + &(ss->function), ep); + if (result) + goto fail1; + + result = usb_ep_enable(ep); + if (result < 0) + goto fail1; + + ep->driver_data = ss; + result = source_sink_start_ep(ss, false, EP_INTERRUPT, + speed); + if (result < 0) { + ep = ss->int_out_ep; + usb_ep_disable(ep); + ep->driver_data = NULL; + goto fail1; + } + } + + goto out; + } + /* one bulk endpoint writes (sources) zeroes IN (to the host) */ ep = ss->in_ep; result = config_ep_by_speed(cdev->gadget, &(ss->function), ep); @@ -684,7 +966,7 @@ enable_source_sink(struct usb_composite_dev *cdev, struct f_sourcesink *ss, return result; ep->driver_data = ss; - result = source_sink_start_ep(ss, true, false, speed); + result = source_sink_start_ep(ss, true, EP_BULK, speed); if (result < 0) { fail: ep = ss->in_ep; @@ -703,7 +985,7 @@ fail: goto fail; ep->driver_data = ss; - result = source_sink_start_ep(ss, false, false, speed); + result = source_sink_start_ep(ss, false, EP_BULK, speed); if (result < 0) { fail2: ep = ss->out_ep; @@ -726,7 +1008,7 @@ fail2: goto fail2; ep->driver_data = ss; - result = source_sink_start_ep(ss, true, true, speed); + result = source_sink_start_ep(ss, true, EP_ISOC, speed); if (result < 0) { fail3: ep = ss->iso_in_ep; @@ -749,13 +1031,14 @@ fail3: goto fail3; ep->driver_data = ss; - result = source_sink_start_ep(ss, false, true, speed); + result = source_sink_start_ep(ss, false, EP_ISOC, speed); if (result < 0) { usb_ep_disable(ep); ep->driver_data = NULL; goto fail3; } } + out: ss->cur_alt = alt; @@ -771,6 +1054,8 @@ static int sourcesink_set_alt(struct usb_function *f, if (ss->in_ep->driver_data) disable_source_sink(ss); + else if (alt == 2 && ss->int_in_ep->driver_data) + disable_source_sink(ss); return enable_source_sink(cdev, ss, alt); } @@ -883,6 +1168,10 @@ static struct usb_function *source_sink_alloc_func( isoc_maxpacket = ss_opts->isoc_maxpacket; isoc_mult = ss_opts->isoc_mult; isoc_maxburst = ss_opts->isoc_maxburst; + int_interval = ss_opts->int_interval; + int_maxpacket = ss_opts->int_maxpacket; + int_mult = ss_opts->int_mult; + int_maxburst = ss_opts->int_maxburst; buflen = ss_opts->bulk_buflen; ss->function.name = "source/sink"; @@ -1179,6 +1468,182 @@ static struct f_ss_opts_attribute f_ss_opts_bulk_buflen = f_ss_opts_bulk_buflen_show, f_ss_opts_bulk_buflen_store); +static ssize_t f_ss_opts_int_interval_show(struct f_ss_opts *opts, char *page) +{ + int result; + + mutex_lock(&opts->lock); + result = sprintf(page, "%d", opts->int_interval); + mutex_unlock(&opts->lock); + + return result; +} + +static ssize_t f_ss_opts_int_interval_store(struct f_ss_opts *opts, + const char *page, size_t len) +{ + int ret; + u8 num; + + mutex_lock(&opts->lock); + if (opts->refcnt) { + ret = -EBUSY; + goto end; + } + + ret = kstrtou8(page, 0, &num); + if (ret) + goto end; + + if (num > 4096) { + ret = -EINVAL; + goto end; + } + + opts->int_interval = num; + ret = len; +end: + mutex_unlock(&opts->lock); + return ret; +} + +static struct f_ss_opts_attribute f_ss_opts_int_interval = + __CONFIGFS_ATTR(int_interval, S_IRUGO | S_IWUSR, + f_ss_opts_int_interval_show, + f_ss_opts_int_interval_store); + +static ssize_t f_ss_opts_int_maxpacket_show(struct f_ss_opts *opts, char *page) +{ + int result; + + mutex_lock(&opts->lock); + result = sprintf(page, "%d", opts->int_maxpacket); + mutex_unlock(&opts->lock); + + return result; +} + +static ssize_t f_ss_opts_int_maxpacket_store(struct f_ss_opts *opts, + const char *page, size_t len) +{ + int ret; + u16 num; + + mutex_lock(&opts->lock); + if (opts->refcnt) { + ret = -EBUSY; + goto end; + } + + ret = kstrtou16(page, 0, &num); + if (ret) + goto end; + + if (num > 1024) { + ret = -EINVAL; + goto end; + } + + opts->int_maxpacket = num; + ret = len; +end: + mutex_unlock(&opts->lock); + return ret; +} + +static struct f_ss_opts_attribute f_ss_opts_int_maxpacket = + __CONFIGFS_ATTR(int_maxpacket, S_IRUGO | S_IWUSR, + f_ss_opts_int_maxpacket_show, + f_ss_opts_int_maxpacket_store); + +static ssize_t f_ss_opts_int_mult_show(struct f_ss_opts *opts, char *page) +{ + int result; + + mutex_lock(&opts->lock); + result = sprintf(page, "%d", opts->int_mult); + mutex_unlock(&opts->lock); + + return result; +} + +static ssize_t f_ss_opts_int_mult_store(struct f_ss_opts *opts, + const char *page, size_t len) +{ + int ret; + u8 num; + + mutex_lock(&opts->lock); + if (opts->refcnt) { + ret = -EBUSY; + goto end; + } + + ret = kstrtou8(page, 0, &num); + if (ret) + goto end; + + if (num > 2) { + ret = -EINVAL; + goto end; + } + + opts->int_mult = num; + ret = len; +end: + mutex_unlock(&opts->lock); + return ret; +} + +static struct f_ss_opts_attribute f_ss_opts_int_mult = + __CONFIGFS_ATTR(int_mult, S_IRUGO | S_IWUSR, + f_ss_opts_int_mult_show, + f_ss_opts_int_mult_store); + +static ssize_t f_ss_opts_int_maxburst_show(struct f_ss_opts *opts, char *page) +{ + int result; + + mutex_lock(&opts->lock); + result = sprintf(page, "%d", opts->int_maxburst); + mutex_unlock(&opts->lock); + + return result; +} + +static ssize_t f_ss_opts_int_maxburst_store(struct f_ss_opts *opts, + const char *page, size_t len) +{ + int ret; + u8 num; + + mutex_lock(&opts->lock); + if (opts->refcnt) { + ret = -EBUSY; + goto end; + } + + ret = kstrtou8(page, 0, &num); + if (ret) + goto end; + + if (num > 15) { + ret = -EINVAL; + goto end; + } + + opts->int_maxburst = num; + ret = len; +end: + mutex_unlock(&opts->lock); + return ret; +} + +static struct f_ss_opts_attribute f_ss_opts_int_maxburst = + __CONFIGFS_ATTR(int_maxburst, S_IRUGO | S_IWUSR, + f_ss_opts_int_maxburst_show, + f_ss_opts_int_maxburst_store); + static struct configfs_attribute *ss_attrs[] = { &f_ss_opts_pattern.attr, &f_ss_opts_isoc_interval.attr, @@ -1186,6 +1651,10 @@ static struct configfs_attribute *ss_attrs[] = { &f_ss_opts_isoc_mult.attr, &f_ss_opts_isoc_maxburst.attr, &f_ss_opts_bulk_buflen.attr, + &f_ss_opts_int_interval.attr, + &f_ss_opts_int_maxpacket.attr, + &f_ss_opts_int_mult.attr, + &f_ss_opts_int_maxburst.attr, NULL, }; @@ -1215,6 +1684,8 @@ static struct usb_function_instance *source_sink_alloc_inst(void) ss_opts->isoc_interval = GZERO_ISOC_INTERVAL; ss_opts->isoc_maxpacket = GZERO_ISOC_MAXPACKET; ss_opts->bulk_buflen = GZERO_BULK_BUFLEN; + ss_opts->int_interval = GZERO_INT_INTERVAL; + ss_opts->int_maxpacket = GZERO_INT_MAXPACKET; config_group_init_type_name(&ss_opts->func_inst.group, "", &ss_func_type); diff --git a/drivers/usb/gadget/function/g_zero.h b/drivers/usb/gadget/function/g_zero.h index 15f1809..2ce28b9d 100644 --- a/drivers/usb/gadget/function/g_zero.h +++ b/drivers/usb/gadget/function/g_zero.h @@ -10,6 +10,8 @@ #define GZERO_QLEN 32 #define GZERO_ISOC_INTERVAL 4 #define GZERO_ISOC_MAXPACKET 1024 +#define GZERO_INT_INTERVAL 1 /* Default interrupt interval = 1 ms */ +#define GZERO_INT_MAXPACKET 1024 struct usb_zero_options { unsigned pattern; @@ -17,6 +19,10 @@ struct usb_zero_options { unsigned isoc_maxpacket; unsigned isoc_mult; unsigned isoc_maxburst; + unsigned int_interval; /* In ms */ + unsigned int_maxpacket; + unsigned int_mult; + unsigned int_maxburst; unsigned bulk_buflen; unsigned qlen; }; @@ -28,6 +34,10 @@ struct f_ss_opts { unsigned isoc_maxpacket; unsigned isoc_mult; unsigned isoc_maxburst; + unsigned int_interval; /* In ms */ + unsigned int_maxpacket; + unsigned int_mult; + unsigned int_maxburst; unsigned bulk_buflen; /* @@ -62,6 +72,7 @@ int lb_modinit(void); void free_ep_req(struct usb_ep *ep, struct usb_request *req); void disable_endpoints(struct usb_composite_dev *cdev, struct usb_ep *in, struct usb_ep *out, - struct usb_ep *iso_in, struct usb_ep *iso_out); + struct usb_ep *iso_in, struct usb_ep *iso_out, + struct usb_ep *int_in, struct usb_ep *int_out); #endif /* __G_ZERO_H */ diff --git a/drivers/usb/gadget/legacy/zero.c b/drivers/usb/gadget/legacy/zero.c index c3d4968..ebf09f4 100644 --- a/drivers/usb/gadget/legacy/zero.c +++ b/drivers/usb/gadget/legacy/zero.c @@ -68,6 +68,8 @@ static struct usb_zero_options gzero_options = { .isoc_maxpacket = GZERO_ISOC_MAXPACKET, .bulk_buflen = GZERO_BULK_BUFLEN, .qlen = GZERO_QLEN, + .int_interval = GZERO_INT_INTERVAL, + .int_maxpacket = GZERO_INT_MAXPACKET, }; /*-------------------------------------------------------------------------*/ @@ -266,6 +268,21 @@ module_param_named(isoc_maxburst, gzero_options.isoc_maxburst, uint, S_IRUGO|S_IWUSR); MODULE_PARM_DESC(isoc_maxburst, "0 - 15 (ss only)"); +module_param_named(int_interval, gzero_options.int_interval, uint, + S_IRUGO|S_IWUSR); +MODULE_PARM_DESC(int_interval, "1 - 16"); + +module_param_named(int_maxpacket, gzero_options.int_maxpacket, uint, + S_IRUGO|S_IWUSR); +MODULE_PARM_DESC(int_maxpacket, "0 - 1023 (fs), 0 - 1024 (hs/ss)"); + +module_param_named(int_mult, gzero_options.int_mult, uint, S_IRUGO|S_IWUSR); +MODULE_PARM_DESC(int_mult, "0 - 2 (hs/ss only)"); + +module_param_named(int_maxburst, gzero_options.int_maxburst, uint, + S_IRUGO|S_IWUSR); +MODULE_PARM_DESC(int_maxburst, "0 - 15 (ss only)"); + static struct usb_function *func_lb; static struct usb_function_instance *func_inst_lb; @@ -301,6 +318,10 @@ static int __init zero_bind(struct usb_composite_dev *cdev) ss_opts->isoc_maxpacket = gzero_options.isoc_maxpacket; ss_opts->isoc_mult = gzero_options.isoc_mult; ss_opts->isoc_maxburst = gzero_options.isoc_maxburst; + ss_opts->int_interval = gzero_options.int_interval; + ss_opts->int_maxpacket = gzero_options.int_maxpacket; + ss_opts->int_mult = gzero_options.int_mult; + ss_opts->int_maxburst = gzero_options.int_maxburst; ss_opts->bulk_buflen = gzero_options.bulk_buflen; func_ss = usb_get_function(func_inst_ss); -- cgit v1.1 From 457a0955e152ac3b0de46ecbe7a8b434856fda67 Mon Sep 17 00:00:00 2001 From: Amit Virdi Date: Fri, 22 Aug 2014 14:36:37 +0530 Subject: usbtest: Add interrupt EP testcases Two simple test cases for interrupt endpoints are added to the usbtest.c file. These are simple non-queued interrupt IN and interrupt OUT transfers. Currently, only gadget zero is capable of executing the interrupt EP test cases. However, extending the same to other gadgets is extremely simple and can be done on-demand. The two new tests added are - Test 25: To verify Interrupt OUT transfer - Test 26: To verify Interrupt IN transfer Since the default value of wMaxPacketSize is set as 1024, so interrupt IN transfers must be specified with the size parameter = multiple of 1024. Otherwise the default value (512) in the usbtest application fails the transfer. See [RUN 4] for sample logs The application logs (usbtest) and corresponding kernel logs are as following: [Run 1] ./testusb -a -c 10 -s 2048 -t 26 -v 511 usbtest 7-1:3.0: TEST 26: read 2048 bytes 10 times [Run 2] ./testusb -a -c 10 -s 1024 -t 25 -v 511 usbtest 7-1:3.0: TEST 25: write 1024 bytes 10 times [Run 3] ./testusb -a -c 10 -s 1098 -t 25 -v 511 usbtest 7-1:3.0: TEST 25: write 1098 bytes 10 times [Run 4 - Failure case scenario] ./testusb -a -t 26 unknown speed /dev/bus/usb/007/004 0 /dev/bus/usb/007/004 test 26 --> 75 (Value too large for defined data type) usbtest 7-1:3.0: TEST 26: read 512 bytes 1000 times usb 7-1: test26 failed, iterations left 999, status -75 (not 0) Signed-off-by: Amit Virdi Signed-off-by: Felipe Balbi --- drivers/usb/misc/usbtest.c | 113 +++++++++++++++++++++++++++++++++++++++------ 1 file changed, 98 insertions(+), 15 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/misc/usbtest.c b/drivers/usb/misc/usbtest.c index 829f446..90e6644 100644 --- a/drivers/usb/misc/usbtest.c +++ b/drivers/usb/misc/usbtest.c @@ -54,6 +54,7 @@ struct usbtest_info { unsigned autoconf:1; unsigned ctrl_out:1; unsigned iso:1; /* try iso in/out */ + unsigned intr:1; /* try interrupt in/out */ int alt; }; @@ -70,7 +71,10 @@ struct usbtest_dev { int out_pipe; int in_iso_pipe; int out_iso_pipe; + int in_int_pipe; + int out_int_pipe; struct usb_endpoint_descriptor *iso_in, *iso_out; + struct usb_endpoint_descriptor *int_in, *int_out; struct mutex lock; #define TBUF_SIZE 256 @@ -101,6 +105,7 @@ get_endpoints(struct usbtest_dev *dev, struct usb_interface *intf) struct usb_host_interface *alt; struct usb_host_endpoint *in, *out; struct usb_host_endpoint *iso_in, *iso_out; + struct usb_host_endpoint *int_in, *int_out; struct usb_device *udev; for (tmp = 0; tmp < intf->num_altsetting; tmp++) { @@ -108,6 +113,7 @@ get_endpoints(struct usbtest_dev *dev, struct usb_interface *intf) in = out = NULL; iso_in = iso_out = NULL; + int_in = int_out = NULL; alt = intf->altsetting + tmp; if (override_alt >= 0 && @@ -124,6 +130,9 @@ get_endpoints(struct usbtest_dev *dev, struct usb_interface *intf) switch (usb_endpoint_type(&e->desc)) { case USB_ENDPOINT_XFER_BULK: break; + case USB_ENDPOINT_XFER_INT: + if (dev->info->intr) + goto try_intr; case USB_ENDPOINT_XFER_ISOC: if (dev->info->iso) goto try_iso; @@ -139,6 +148,15 @@ get_endpoints(struct usbtest_dev *dev, struct usb_interface *intf) out = e; } continue; +try_intr: + if (usb_endpoint_dir_in(&e->desc)) { + if (!int_in) + int_in = e; + } else { + if (!int_out) + int_out = e; + } + continue; try_iso: if (usb_endpoint_dir_in(&e->desc)) { if (!iso_in) @@ -148,7 +166,7 @@ try_iso: iso_out = e; } } - if ((in && out) || iso_in || iso_out) + if ((in && out) || iso_in || iso_out || int_in || int_out) goto found; } return -EINVAL; @@ -183,6 +201,20 @@ found: iso_out->desc.bEndpointAddress & USB_ENDPOINT_NUMBER_MASK); } + + if (int_in) { + dev->int_in = &int_in->desc; + dev->in_int_pipe = usb_rcvintpipe(udev, + int_in->desc.bEndpointAddress + & USB_ENDPOINT_NUMBER_MASK); + } + + if (int_out) { + dev->int_out = &int_out->desc; + dev->out_int_pipe = usb_sndintpipe(udev, + int_out->desc.bEndpointAddress + & USB_ENDPOINT_NUMBER_MASK); + } return 0; } @@ -205,14 +237,22 @@ static struct urb *usbtest_alloc_urb( int pipe, unsigned long bytes, unsigned transfer_flags, - unsigned offset) + unsigned offset, + u8 bInterval) { struct urb *urb; urb = usb_alloc_urb(0, GFP_KERNEL); if (!urb) return urb; - usb_fill_bulk_urb(urb, udev, pipe, NULL, bytes, simple_callback, NULL); + + if (bInterval) + usb_fill_int_urb(urb, udev, pipe, NULL, bytes, simple_callback, + NULL, bInterval); + else + usb_fill_bulk_urb(urb, udev, pipe, NULL, bytes, simple_callback, + NULL); + urb->interval = (udev->speed == USB_SPEED_HIGH) ? (INTERRUPT_RATE << 3) : INTERRUPT_RATE; @@ -251,9 +291,11 @@ static struct urb *usbtest_alloc_urb( static struct urb *simple_alloc_urb( struct usb_device *udev, int pipe, - unsigned long bytes) + unsigned long bytes, + u8 bInterval) { - return usbtest_alloc_urb(udev, pipe, bytes, URB_NO_TRANSFER_DMA_MAP, 0); + return usbtest_alloc_urb(udev, pipe, bytes, URB_NO_TRANSFER_DMA_MAP, 0, + bInterval); } static unsigned pattern; @@ -1255,7 +1297,7 @@ test_ctrl_queue(struct usbtest_dev *dev, struct usbtest_param *param) goto cleanup; } req.wLength = cpu_to_le16(len); - urb[i] = u = simple_alloc_urb(udev, pipe, len); + urb[i] = u = simple_alloc_urb(udev, pipe, len, 0); if (!u) goto cleanup; @@ -1328,7 +1370,7 @@ static int unlink1(struct usbtest_dev *dev, int pipe, int size, int async) int retval = 0; init_completion(&completion); - urb = simple_alloc_urb(testdev_to_usbdev(dev), pipe, size); + urb = simple_alloc_urb(testdev_to_usbdev(dev), pipe, size, 0); if (!urb) return -ENOMEM; urb->context = &completion; @@ -1616,9 +1658,9 @@ static int halt_simple(struct usbtest_dev *dev) struct usb_device *udev = testdev_to_usbdev(dev); if (udev->speed == USB_SPEED_SUPER) - urb = simple_alloc_urb(udev, 0, 1024); + urb = simple_alloc_urb(udev, 0, 1024, 0); else - urb = simple_alloc_urb(udev, 0, 512); + urb = simple_alloc_urb(udev, 0, 512, 0); if (urb == NULL) return -ENOMEM; @@ -1962,7 +2004,7 @@ static int test_unaligned_bulk( { int retval; struct urb *urb = usbtest_alloc_urb( - testdev_to_usbdev(tdev), pipe, length, transfer_flags, 1); + testdev_to_usbdev(tdev), pipe, length, transfer_flags, 1, 0); if (!urb) return -ENOMEM; @@ -2068,7 +2110,7 @@ usbtest_ioctl(struct usb_interface *intf, unsigned int code, void *buf) dev_info(&intf->dev, "TEST 1: write %d bytes %u times\n", param->length, param->iterations); - urb = simple_alloc_urb(udev, dev->out_pipe, param->length); + urb = simple_alloc_urb(udev, dev->out_pipe, param->length, 0); if (!urb) { retval = -ENOMEM; break; @@ -2083,7 +2125,7 @@ usbtest_ioctl(struct usb_interface *intf, unsigned int code, void *buf) dev_info(&intf->dev, "TEST 2: read %d bytes %u times\n", param->length, param->iterations); - urb = simple_alloc_urb(udev, dev->in_pipe, param->length); + urb = simple_alloc_urb(udev, dev->in_pipe, param->length, 0); if (!urb) { retval = -ENOMEM; break; @@ -2098,7 +2140,7 @@ usbtest_ioctl(struct usb_interface *intf, unsigned int code, void *buf) dev_info(&intf->dev, "TEST 3: write/%d 0..%d bytes %u times\n", param->vary, param->length, param->iterations); - urb = simple_alloc_urb(udev, dev->out_pipe, param->length); + urb = simple_alloc_urb(udev, dev->out_pipe, param->length, 0); if (!urb) { retval = -ENOMEM; break; @@ -2114,7 +2156,7 @@ usbtest_ioctl(struct usb_interface *intf, unsigned int code, void *buf) dev_info(&intf->dev, "TEST 4: read/%d 0..%d bytes %u times\n", param->vary, param->length, param->iterations); - urb = simple_alloc_urb(udev, dev->in_pipe, param->length); + urb = simple_alloc_urb(udev, dev->in_pipe, param->length, 0); if (!urb) { retval = -ENOMEM; break; @@ -2411,6 +2453,39 @@ usbtest_ioctl(struct usb_interface *intf, unsigned int code, void *buf) } break; + /* Simple non-queued interrupt I/O tests */ + case 25: + if (dev->out_int_pipe == 0) + break; + dev_info(&intf->dev, + "TEST 25: write %d bytes %u times\n", + param->length, param->iterations); + urb = simple_alloc_urb(udev, dev->out_int_pipe, param->length, + dev->int_out->bInterval); + if (!urb) { + retval = -ENOMEM; + break; + } + /* FIRMWARE: interrupt sink (maybe accepts short writes) */ + retval = simple_io(dev, urb, param->iterations, 0, 0, "test25"); + simple_free_urb(urb); + break; + case 26: + if (dev->in_int_pipe == 0) + break; + dev_info(&intf->dev, + "TEST 26: read %d bytes %u times\n", + param->length, param->iterations); + urb = simple_alloc_urb(udev, dev->in_int_pipe, param->length, + dev->int_in->bInterval); + if (!urb) { + retval = -ENOMEM; + break; + } + /* FIRMWARE: interrupt source (maybe generates short writes) */ + retval = simple_io(dev, urb, param->iterations, 0, 0, "test26"); + simple_free_urb(urb); + break; } do_gettimeofday(¶m->duration); param->duration.tv_sec -= start.tv_sec; @@ -2447,6 +2522,7 @@ usbtest_probe(struct usb_interface *intf, const struct usb_device_id *id) struct usbtest_info *info; char *rtest, *wtest; char *irtest, *iwtest; + char *intrtest, *intwtest; udev = interface_to_usbdev(intf); @@ -2487,6 +2563,7 @@ usbtest_probe(struct usb_interface *intf, const struct usb_device_id *id) */ rtest = wtest = ""; irtest = iwtest = ""; + intrtest = intwtest = ""; if (force_interrupt || udev->speed == USB_SPEED_LOW) { if (info->ep_in) { dev->in_pipe = usb_rcvintpipe(udev, info->ep_in); @@ -2525,15 +2602,20 @@ usbtest_probe(struct usb_interface *intf, const struct usb_device_id *id) irtest = " iso-in"; if (dev->out_iso_pipe) iwtest = " iso-out"; + if (dev->in_int_pipe) + intrtest = " int-in"; + if (dev->out_int_pipe) + intwtest = " int-out"; } usb_set_intfdata(intf, dev); dev_info(&intf->dev, "%s\n", info->name); - dev_info(&intf->dev, "%s {control%s%s%s%s%s} tests%s\n", + dev_info(&intf->dev, "%s {control%s%s%s%s%s%s%s} tests%s\n", usb_speed_string(udev->speed), info->ctrl_out ? " in/out" : "", rtest, wtest, irtest, iwtest, + intrtest, intwtest, info->alt >= 0 ? " (+alt)" : ""); return 0; } @@ -2607,6 +2689,7 @@ static struct usbtest_info gz_info = { .autoconf = 1, .ctrl_out = 1, .iso = 1, + .intr = 1, .alt = 0, }; -- cgit v1.1 From b8adc3d1d852be653905b24cd8efcaee342b96ae Mon Sep 17 00:00:00 2001 From: Himangi Saraogi Date: Sun, 24 Aug 2014 05:21:16 +0530 Subject: usb: gadget: udc: use USB API functions rather than constants This patch introduces the use of the functions usb_endpoint_type and usb_endpoint_num. The Coccinelle semantic patch that makes these changes is as follows: @@ struct usb_endpoint_descriptor *epd; @@ - (epd->bEndpointAddress & \(USB_ENDPOINT_NUMBER_MASK\|0x0f\)) + usb_endpoint_num(epd) @@ struct usb_endpoint_descriptor *epd; @@ - (epd->bmAttributes & \(USB_ENDPOINT_XFERTYPE_MASK\|3\)) + usb_endpoint_type(epd) Signed-off-by: Himangi Saraogi Acked-by: Julia Lawall Signed-off-by: Felipe Balbi --- drivers/usb/gadget/udc/r8a66597-udc.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/gadget/udc/r8a66597-udc.c b/drivers/usb/gadget/udc/r8a66597-udc.c index 68a7d20..00f7345 100644 --- a/drivers/usb/gadget/udc/r8a66597-udc.c +++ b/drivers/usb/gadget/udc/r8a66597-udc.c @@ -430,7 +430,7 @@ static void r8a66597_ep_setting(struct r8a66597 *r8a66597, ep->pipenum = pipenum; ep->ep.maxpacket = usb_endpoint_maxp(desc); r8a66597->pipenum2ep[pipenum] = ep; - r8a66597->epaddr2ep[desc->bEndpointAddress & USB_ENDPOINT_NUMBER_MASK] + r8a66597->epaddr2ep[usb_endpoint_num(desc)] = ep; INIT_LIST_HEAD(&ep->queue); } @@ -464,7 +464,7 @@ static int alloc_pipe_config(struct r8a66597_ep *ep, if (ep->pipenum) /* already allocated pipe */ return 0; - switch (desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) { + switch (usb_endpoint_type(desc)) { case USB_ENDPOINT_XFER_BULK: if (r8a66597->bulk >= R8A66597_MAX_NUM_BULK) { if (r8a66597->isochronous >= R8A66597_MAX_NUM_ISOC) { @@ -509,7 +509,7 @@ static int alloc_pipe_config(struct r8a66597_ep *ep, } ep->type = info.type; - info.epnum = desc->bEndpointAddress & USB_ENDPOINT_NUMBER_MASK; + info.epnum = usb_endpoint_num(desc); info.maxpacket = usb_endpoint_maxp(desc); info.interval = desc->bInterval; if (desc->bEndpointAddress & USB_ENDPOINT_DIR_MASK) -- cgit v1.1 From fa31409a82ee050e52caad9e4c483fe3edca163a Mon Sep 17 00:00:00 2001 From: Yegor Yefremov Date: Wed, 27 Aug 2014 10:42:53 +0200 Subject: usb: gadget: use $(srctree) instead of $(PWD) for includes Using $(PWD) breaks builds when make was invoked from outside of the kernel tree. Signed-off-by: Yegor Yefremov Signed-off-by: Felipe Balbi --- drivers/usb/gadget/Makefile | 2 +- drivers/usb/gadget/function/Makefile | 4 ++-- drivers/usb/gadget/legacy/Makefile | 6 +++--- 3 files changed, 6 insertions(+), 6 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/gadget/Makefile b/drivers/usb/gadget/Makefile index a186afe..598a67d 100644 --- a/drivers/usb/gadget/Makefile +++ b/drivers/usb/gadget/Makefile @@ -3,7 +3,7 @@ # subdir-ccflags-$(CONFIG_USB_GADGET_DEBUG) := -DDEBUG subdir-ccflags-$(CONFIG_USB_GADGET_VERBOSE) += -DVERBOSE_DEBUG -ccflags-y += -I$(PWD)/drivers/usb/gadget/udc +ccflags-y += -I$(srctree)/drivers/usb/gadget/udc obj-$(CONFIG_USB_LIBCOMPOSITE) += libcomposite.o libcomposite-y := usbstring.o config.o epautoconf.o diff --git a/drivers/usb/gadget/function/Makefile b/drivers/usb/gadget/function/Makefile index edb6dfa..a49fdf7b 100644 --- a/drivers/usb/gadget/function/Makefile +++ b/drivers/usb/gadget/function/Makefile @@ -2,8 +2,8 @@ # USB peripheral controller drivers # -ccflags-y := -I$(PWD)/drivers/usb/gadget/ -ccflags-y += -I$(PWD)/drivers/usb/gadget/udc/ +ccflags-y := -I$(srctree)/drivers/usb/gadget/ +ccflags-y += -I$(srctree)/drivers/usb/gadget/udc/ # USB Functions usb_f_acm-y := f_acm.o diff --git a/drivers/usb/gadget/legacy/Makefile b/drivers/usb/gadget/legacy/Makefile index a11aad5..7f485f2 100644 --- a/drivers/usb/gadget/legacy/Makefile +++ b/drivers/usb/gadget/legacy/Makefile @@ -2,9 +2,9 @@ # USB gadget drivers # -ccflags-y := -I$(PWD)/drivers/usb/gadget/ -ccflags-y += -I$(PWD)/drivers/usb/gadget/udc/ -ccflags-y += -I$(PWD)/drivers/usb/gadget/function/ +ccflags-y := -I$(srctree)/drivers/usb/gadget/ +ccflags-y += -I$(srctree)/drivers/usb/gadget/udc/ +ccflags-y += -I$(srctree)/drivers/usb/gadget/function/ g_zero-y := zero.o g_audio-y := audio.o -- cgit v1.1 From a8b5b12eff1510d701ac5ad321e215d7153c96c3 Mon Sep 17 00:00:00 2001 From: Bartlomiej Zolnierkiewicz Date: Fri, 22 Aug 2014 18:59:03 +0200 Subject: usb: phy: samsung: remove old USB 2.0 PHY driver drivers/usb/phy/phy-samsung-usb2 driver got replaced by drivers/phy/phy-samsung-usb2 one and is no longer used. Signed-off-by: Bartlomiej Zolnierkiewicz Acked-by: Kyungmin Park Reviewed-by: Vivek Gautam Reviewed-by: Jingoo Han Acked-by: Kishon Vijay Abraham I Cc: Kamil Debski Signed-off-by: Felipe Balbi --- drivers/usb/phy/Kconfig | 8 - drivers/usb/phy/Makefile | 1 - drivers/usb/phy/phy-samsung-usb2.c | 541 ------------------------------------- 3 files changed, 550 deletions(-) delete mode 100644 drivers/usb/phy/phy-samsung-usb2.c (limited to 'drivers/usb') diff --git a/drivers/usb/phy/Kconfig b/drivers/usb/phy/Kconfig index e253fa0..452e252 100644 --- a/drivers/usb/phy/Kconfig +++ b/drivers/usb/phy/Kconfig @@ -78,14 +78,6 @@ config SAMSUNG_USBPHY This driver provides common interface to interact, for Samsung USB 2.0 PHY driver and later for Samsung USB 3.0 PHY driver. -config SAMSUNG_USB2PHY - tristate "Samsung USB 2.0 PHY controller Driver" - select SAMSUNG_USBPHY - select USB_PHY - help - Enable this to support Samsung USB 2.0 (High Speed) PHY controller - driver for Samsung SoCs. - config SAMSUNG_USB3PHY tristate "Samsung USB 3.0 PHY controller Driver" select SAMSUNG_USBPHY diff --git a/drivers/usb/phy/Makefile b/drivers/usb/phy/Makefile index 24a9133..c651005 100644 --- a/drivers/usb/phy/Makefile +++ b/drivers/usb/phy/Makefile @@ -15,7 +15,6 @@ obj-$(CONFIG_AM335X_CONTROL_USB) += phy-am335x-control.o obj-$(CONFIG_AM335X_PHY_USB) += phy-am335x.o obj-$(CONFIG_OMAP_OTG) += phy-omap-otg.o obj-$(CONFIG_SAMSUNG_USBPHY) += phy-samsung-usb.o -obj-$(CONFIG_SAMSUNG_USB2PHY) += phy-samsung-usb2.o obj-$(CONFIG_SAMSUNG_USB3PHY) += phy-samsung-usb3.o obj-$(CONFIG_TWL6030_USB) += phy-twl6030-usb.o obj-$(CONFIG_USB_EHCI_TEGRA) += phy-tegra-usb.o diff --git a/drivers/usb/phy/phy-samsung-usb2.c b/drivers/usb/phy/phy-samsung-usb2.c deleted file mode 100644 index b3ba866..0000000 --- a/drivers/usb/phy/phy-samsung-usb2.c +++ /dev/null @@ -1,541 +0,0 @@ -/* linux/drivers/usb/phy/phy-samsung-usb2.c - * - * Copyright (c) 2012 Samsung Electronics Co., Ltd. - * http://www.samsung.com - * - * Author: Praveen Paneri - * - * Samsung USB2.0 PHY transceiver; talks to S3C HS OTG controller, EHCI-S5P and - * OHCI-EXYNOS controllers. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "phy-samsung-usb.h" - -static int samsung_usbphy_set_host(struct usb_otg *otg, struct usb_bus *host) -{ - if (!otg) - return -ENODEV; - - if (!otg->host) - otg->host = host; - - return 0; -} - -static bool exynos5_phyhost_is_on(void __iomem *regs) -{ - u32 reg; - - reg = readl(regs + EXYNOS5_PHY_HOST_CTRL0); - - return !(reg & HOST_CTRL0_SIDDQ); -} - -static void samsung_exynos5_usb2phy_enable(struct samsung_usbphy *sphy) -{ - void __iomem *regs = sphy->regs; - u32 phyclk = sphy->ref_clk_freq; - u32 phyhost; - u32 phyotg; - u32 phyhsic; - u32 ehcictrl; - u32 ohcictrl; - - /* - * phy_usage helps in keeping usage count for phy - * so that the first consumer enabling the phy is also - * the last consumer to disable it. - */ - - atomic_inc(&sphy->phy_usage); - - if (exynos5_phyhost_is_on(regs)) { - dev_info(sphy->dev, "Already power on PHY\n"); - return; - } - - /* Host configuration */ - phyhost = readl(regs + EXYNOS5_PHY_HOST_CTRL0); - - /* phy reference clock configuration */ - phyhost &= ~HOST_CTRL0_FSEL_MASK; - phyhost |= HOST_CTRL0_FSEL(phyclk); - - /* host phy reset */ - phyhost &= ~(HOST_CTRL0_PHYSWRST | - HOST_CTRL0_PHYSWRSTALL | - HOST_CTRL0_SIDDQ | - /* Enable normal mode of operation */ - HOST_CTRL0_FORCESUSPEND | - HOST_CTRL0_FORCESLEEP); - - /* Link reset */ - phyhost |= (HOST_CTRL0_LINKSWRST | - HOST_CTRL0_UTMISWRST | - /* COMMON Block configuration during suspend */ - HOST_CTRL0_COMMONON_N); - writel(phyhost, regs + EXYNOS5_PHY_HOST_CTRL0); - udelay(10); - phyhost &= ~(HOST_CTRL0_LINKSWRST | - HOST_CTRL0_UTMISWRST); - writel(phyhost, regs + EXYNOS5_PHY_HOST_CTRL0); - - /* OTG configuration */ - phyotg = readl(regs + EXYNOS5_PHY_OTG_SYS); - - /* phy reference clock configuration */ - phyotg &= ~OTG_SYS_FSEL_MASK; - phyotg |= OTG_SYS_FSEL(phyclk); - - /* Enable normal mode of operation */ - phyotg &= ~(OTG_SYS_FORCESUSPEND | - OTG_SYS_SIDDQ_UOTG | - OTG_SYS_FORCESLEEP | - OTG_SYS_REFCLKSEL_MASK | - /* COMMON Block configuration during suspend */ - OTG_SYS_COMMON_ON); - - /* OTG phy & link reset */ - phyotg |= (OTG_SYS_PHY0_SWRST | - OTG_SYS_LINKSWRST_UOTG | - OTG_SYS_PHYLINK_SWRESET | - OTG_SYS_OTGDISABLE | - /* Set phy refclk */ - OTG_SYS_REFCLKSEL_CLKCORE); - - writel(phyotg, regs + EXYNOS5_PHY_OTG_SYS); - udelay(10); - phyotg &= ~(OTG_SYS_PHY0_SWRST | - OTG_SYS_LINKSWRST_UOTG | - OTG_SYS_PHYLINK_SWRESET); - writel(phyotg, regs + EXYNOS5_PHY_OTG_SYS); - - /* HSIC phy configuration */ - phyhsic = (HSIC_CTRL_REFCLKDIV_12 | - HSIC_CTRL_REFCLKSEL | - HSIC_CTRL_PHYSWRST); - writel(phyhsic, regs + EXYNOS5_PHY_HSIC_CTRL1); - writel(phyhsic, regs + EXYNOS5_PHY_HSIC_CTRL2); - udelay(10); - phyhsic &= ~HSIC_CTRL_PHYSWRST; - writel(phyhsic, regs + EXYNOS5_PHY_HSIC_CTRL1); - writel(phyhsic, regs + EXYNOS5_PHY_HSIC_CTRL2); - - udelay(80); - - /* enable EHCI DMA burst */ - ehcictrl = readl(regs + EXYNOS5_PHY_HOST_EHCICTRL); - ehcictrl |= (HOST_EHCICTRL_ENAINCRXALIGN | - HOST_EHCICTRL_ENAINCR4 | - HOST_EHCICTRL_ENAINCR8 | - HOST_EHCICTRL_ENAINCR16); - writel(ehcictrl, regs + EXYNOS5_PHY_HOST_EHCICTRL); - - /* set ohci_suspend_on_n */ - ohcictrl = readl(regs + EXYNOS5_PHY_HOST_OHCICTRL); - ohcictrl |= HOST_OHCICTRL_SUSPLGCY; - writel(ohcictrl, regs + EXYNOS5_PHY_HOST_OHCICTRL); -} - -static void samsung_usb2phy_enable(struct samsung_usbphy *sphy) -{ - void __iomem *regs = sphy->regs; - u32 phypwr; - u32 phyclk; - u32 rstcon; - - /* set clock frequency for PLL */ - phyclk = sphy->ref_clk_freq; - phypwr = readl(regs + SAMSUNG_PHYPWR); - rstcon = readl(regs + SAMSUNG_RSTCON); - - switch (sphy->drv_data->cpu_type) { - case TYPE_S3C64XX: - phyclk &= ~PHYCLK_COMMON_ON_N; - phypwr &= ~PHYPWR_NORMAL_MASK; - rstcon |= RSTCON_SWRST; - break; - case TYPE_EXYNOS4X12: - phypwr &= ~(PHYPWR_NORMAL_MASK_HSIC0 | - PHYPWR_NORMAL_MASK_HSIC1 | - PHYPWR_NORMAL_MASK_PHY1); - rstcon |= RSTCON_HOSTPHY_SWRST; - case TYPE_EXYNOS4210: - phypwr &= ~PHYPWR_NORMAL_MASK_PHY0; - rstcon |= RSTCON_SWRST; - default: - break; - } - - writel(phyclk, regs + SAMSUNG_PHYCLK); - /* Configure PHY0 for normal operation*/ - writel(phypwr, regs + SAMSUNG_PHYPWR); - /* reset all ports of PHY and Link */ - writel(rstcon, regs + SAMSUNG_RSTCON); - udelay(10); - if (sphy->drv_data->cpu_type == TYPE_EXYNOS4X12) - rstcon &= ~RSTCON_HOSTPHY_SWRST; - rstcon &= ~RSTCON_SWRST; - writel(rstcon, regs + SAMSUNG_RSTCON); -} - -static void samsung_exynos5_usb2phy_disable(struct samsung_usbphy *sphy) -{ - void __iomem *regs = sphy->regs; - u32 phyhost; - u32 phyotg; - u32 phyhsic; - - if (atomic_dec_return(&sphy->phy_usage) > 0) { - dev_info(sphy->dev, "still being used\n"); - return; - } - - phyhsic = (HSIC_CTRL_REFCLKDIV_12 | - HSIC_CTRL_REFCLKSEL | - HSIC_CTRL_SIDDQ | - HSIC_CTRL_FORCESLEEP | - HSIC_CTRL_FORCESUSPEND); - writel(phyhsic, regs + EXYNOS5_PHY_HSIC_CTRL1); - writel(phyhsic, regs + EXYNOS5_PHY_HSIC_CTRL2); - - phyhost = readl(regs + EXYNOS5_PHY_HOST_CTRL0); - phyhost |= (HOST_CTRL0_SIDDQ | - HOST_CTRL0_FORCESUSPEND | - HOST_CTRL0_FORCESLEEP | - HOST_CTRL0_PHYSWRST | - HOST_CTRL0_PHYSWRSTALL); - writel(phyhost, regs + EXYNOS5_PHY_HOST_CTRL0); - - phyotg = readl(regs + EXYNOS5_PHY_OTG_SYS); - phyotg |= (OTG_SYS_FORCESUSPEND | - OTG_SYS_SIDDQ_UOTG | - OTG_SYS_FORCESLEEP); - writel(phyotg, regs + EXYNOS5_PHY_OTG_SYS); -} - -static void samsung_usb2phy_disable(struct samsung_usbphy *sphy) -{ - void __iomem *regs = sphy->regs; - u32 phypwr; - - phypwr = readl(regs + SAMSUNG_PHYPWR); - - switch (sphy->drv_data->cpu_type) { - case TYPE_S3C64XX: - phypwr |= PHYPWR_NORMAL_MASK; - break; - case TYPE_EXYNOS4X12: - phypwr |= (PHYPWR_NORMAL_MASK_HSIC0 | - PHYPWR_NORMAL_MASK_HSIC1 | - PHYPWR_NORMAL_MASK_PHY1); - case TYPE_EXYNOS4210: - phypwr |= PHYPWR_NORMAL_MASK_PHY0; - default: - break; - } - - /* Disable analog and otg block power */ - writel(phypwr, regs + SAMSUNG_PHYPWR); -} - -/* - * The function passed to the usb driver for phy initialization - */ -static int samsung_usb2phy_init(struct usb_phy *phy) -{ - struct samsung_usbphy *sphy; - struct usb_bus *host = NULL; - unsigned long flags; - int ret = 0; - - sphy = phy_to_sphy(phy); - - host = phy->otg->host; - - /* Enable the phy clock */ - ret = clk_prepare_enable(sphy->clk); - if (ret) { - dev_err(sphy->dev, "%s: clk_prepare_enable failed\n", __func__); - return ret; - } - - spin_lock_irqsave(&sphy->lock, flags); - - if (host) { - /* setting default phy-type for USB 2.0 */ - if (!strstr(dev_name(host->controller), "ehci") || - !strstr(dev_name(host->controller), "ohci")) - samsung_usbphy_set_type(&sphy->phy, USB_PHY_TYPE_HOST); - } else { - samsung_usbphy_set_type(&sphy->phy, USB_PHY_TYPE_DEVICE); - } - - /* Disable phy isolation */ - if (sphy->plat && sphy->plat->pmu_isolation) - sphy->plat->pmu_isolation(false); - else if (sphy->drv_data->set_isolation) - sphy->drv_data->set_isolation(sphy, false); - - /* Selecting Host/OTG mode; After reset USB2.0PHY_CFG: HOST */ - samsung_usbphy_cfg_sel(sphy); - - /* Initialize usb phy registers */ - sphy->drv_data->phy_enable(sphy); - - spin_unlock_irqrestore(&sphy->lock, flags); - - /* Disable the phy clock */ - clk_disable_unprepare(sphy->clk); - - return ret; -} - -/* - * The function passed to the usb driver for phy shutdown - */ -static void samsung_usb2phy_shutdown(struct usb_phy *phy) -{ - struct samsung_usbphy *sphy; - struct usb_bus *host = NULL; - unsigned long flags; - - sphy = phy_to_sphy(phy); - - host = phy->otg->host; - - if (clk_prepare_enable(sphy->clk)) { - dev_err(sphy->dev, "%s: clk_prepare_enable failed\n", __func__); - return; - } - - spin_lock_irqsave(&sphy->lock, flags); - - if (host) { - /* setting default phy-type for USB 2.0 */ - if (!strstr(dev_name(host->controller), "ehci") || - !strstr(dev_name(host->controller), "ohci")) - samsung_usbphy_set_type(&sphy->phy, USB_PHY_TYPE_HOST); - } else { - samsung_usbphy_set_type(&sphy->phy, USB_PHY_TYPE_DEVICE); - } - - /* De-initialize usb phy registers */ - sphy->drv_data->phy_disable(sphy); - - /* Enable phy isolation */ - if (sphy->plat && sphy->plat->pmu_isolation) - sphy->plat->pmu_isolation(true); - else if (sphy->drv_data->set_isolation) - sphy->drv_data->set_isolation(sphy, true); - - spin_unlock_irqrestore(&sphy->lock, flags); - - clk_disable_unprepare(sphy->clk); -} - -static int samsung_usb2phy_probe(struct platform_device *pdev) -{ - struct samsung_usbphy *sphy; - struct usb_otg *otg; - struct samsung_usbphy_data *pdata = dev_get_platdata(&pdev->dev); - const struct samsung_usbphy_drvdata *drv_data; - struct device *dev = &pdev->dev; - struct resource *phy_mem; - void __iomem *phy_base; - struct clk *clk; - int ret; - - phy_mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); - phy_base = devm_ioremap_resource(dev, phy_mem); - if (IS_ERR(phy_base)) - return PTR_ERR(phy_base); - - sphy = devm_kzalloc(dev, sizeof(*sphy), GFP_KERNEL); - if (!sphy) - return -ENOMEM; - - otg = devm_kzalloc(dev, sizeof(*otg), GFP_KERNEL); - if (!otg) - return -ENOMEM; - - drv_data = samsung_usbphy_get_driver_data(pdev); - - if (drv_data->cpu_type == TYPE_EXYNOS5250) - clk = devm_clk_get(dev, "usbhost"); - else - clk = devm_clk_get(dev, "otg"); - - if (IS_ERR(clk)) { - dev_err(dev, "Failed to get usbhost/otg clock\n"); - return PTR_ERR(clk); - } - - sphy->dev = dev; - - if (dev->of_node) { - ret = samsung_usbphy_parse_dt(sphy); - if (ret < 0) - return ret; - } else { - if (!pdata) { - dev_err(dev, "no platform data specified\n"); - return -EINVAL; - } - } - - sphy->plat = pdata; - sphy->regs = phy_base; - sphy->clk = clk; - sphy->drv_data = drv_data; - sphy->phy.dev = sphy->dev; - sphy->phy.label = "samsung-usb2phy"; - sphy->phy.type = USB_PHY_TYPE_USB2; - sphy->phy.init = samsung_usb2phy_init; - sphy->phy.shutdown = samsung_usb2phy_shutdown; - - sphy->ref_clk_freq = samsung_usbphy_get_refclk_freq(sphy); - if (sphy->ref_clk_freq < 0) - return -EINVAL; - - sphy->phy.otg = otg; - sphy->phy.otg->phy = &sphy->phy; - sphy->phy.otg->set_host = samsung_usbphy_set_host; - - spin_lock_init(&sphy->lock); - - platform_set_drvdata(pdev, sphy); - - return usb_add_phy_dev(&sphy->phy); -} - -static int samsung_usb2phy_remove(struct platform_device *pdev) -{ - struct samsung_usbphy *sphy = platform_get_drvdata(pdev); - - usb_remove_phy(&sphy->phy); - - if (sphy->pmuregs) - iounmap(sphy->pmuregs); - if (sphy->sysreg) - iounmap(sphy->sysreg); - - return 0; -} - -static const struct samsung_usbphy_drvdata usb2phy_s3c64xx = { - .cpu_type = TYPE_S3C64XX, - .devphy_en_mask = S3C64XX_USBPHY_ENABLE, - .rate_to_clksel = samsung_usbphy_rate_to_clksel_64xx, - .set_isolation = NULL, /* TODO */ - .phy_enable = samsung_usb2phy_enable, - .phy_disable = samsung_usb2phy_disable, -}; - -static const struct samsung_usbphy_drvdata usb2phy_exynos4 = { - .cpu_type = TYPE_EXYNOS4210, - .devphy_en_mask = EXYNOS_USBPHY_ENABLE, - .hostphy_en_mask = EXYNOS_USBPHY_ENABLE, - .rate_to_clksel = samsung_usbphy_rate_to_clksel_64xx, - .set_isolation = samsung_usbphy_set_isolation_4210, - .phy_enable = samsung_usb2phy_enable, - .phy_disable = samsung_usb2phy_disable, -}; - -static const struct samsung_usbphy_drvdata usb2phy_exynos4x12 = { - .cpu_type = TYPE_EXYNOS4X12, - .devphy_en_mask = EXYNOS_USBPHY_ENABLE, - .hostphy_en_mask = EXYNOS_USBPHY_ENABLE, - .rate_to_clksel = samsung_usbphy_rate_to_clksel_4x12, - .set_isolation = samsung_usbphy_set_isolation_4210, - .phy_enable = samsung_usb2phy_enable, - .phy_disable = samsung_usb2phy_disable, -}; - -static struct samsung_usbphy_drvdata usb2phy_exynos5 = { - .cpu_type = TYPE_EXYNOS5250, - .hostphy_en_mask = EXYNOS_USBPHY_ENABLE, - .hostphy_reg_offset = EXYNOS_USBHOST_PHY_CTRL_OFFSET, - .rate_to_clksel = samsung_usbphy_rate_to_clksel_4x12, - .set_isolation = samsung_usbphy_set_isolation_4210, - .phy_enable = samsung_exynos5_usb2phy_enable, - .phy_disable = samsung_exynos5_usb2phy_disable, -}; - -#ifdef CONFIG_OF -static const struct of_device_id samsung_usbphy_dt_match[] = { - { - .compatible = "samsung,s3c64xx-usb2phy", - .data = &usb2phy_s3c64xx, - }, { - .compatible = "samsung,exynos4210-usb2phy", - .data = &usb2phy_exynos4, - }, { - .compatible = "samsung,exynos4x12-usb2phy", - .data = &usb2phy_exynos4x12, - }, { - .compatible = "samsung,exynos5250-usb2phy", - .data = &usb2phy_exynos5 - }, - {}, -}; -MODULE_DEVICE_TABLE(of, samsung_usbphy_dt_match); -#endif - -static struct platform_device_id samsung_usbphy_driver_ids[] = { - { - .name = "s3c64xx-usb2phy", - .driver_data = (unsigned long)&usb2phy_s3c64xx, - }, { - .name = "exynos4210-usb2phy", - .driver_data = (unsigned long)&usb2phy_exynos4, - }, { - .name = "exynos4x12-usb2phy", - .driver_data = (unsigned long)&usb2phy_exynos4x12, - }, { - .name = "exynos5250-usb2phy", - .driver_data = (unsigned long)&usb2phy_exynos5, - }, - {}, -}; - -MODULE_DEVICE_TABLE(platform, samsung_usbphy_driver_ids); - -static struct platform_driver samsung_usb2phy_driver = { - .probe = samsung_usb2phy_probe, - .remove = samsung_usb2phy_remove, - .id_table = samsung_usbphy_driver_ids, - .driver = { - .name = "samsung-usb2phy", - .owner = THIS_MODULE, - .of_match_table = of_match_ptr(samsung_usbphy_dt_match), - }, -}; - -module_platform_driver(samsung_usb2phy_driver); - -MODULE_DESCRIPTION("Samsung USB 2.0 phy controller"); -MODULE_AUTHOR("Praveen Paneri "); -MODULE_LICENSE("GPL"); -MODULE_ALIAS("platform:samsung-usb2phy"); -- cgit v1.1 From 1c3c0528876e2dd86cfb86e96e03c38ef19858fe Mon Sep 17 00:00:00 2001 From: Bartlomiej Zolnierkiewicz Date: Fri, 22 Aug 2014 18:59:04 +0200 Subject: usb: phy: samsung: remove old USB 3.0 PHY driver drivers/usb/phy/phy-samsung-usb3 driver got replaced by drivers/phy/phy-samsung-usb3 one and is no longer used. Signed-off-by: Bartlomiej Zolnierkiewicz Acked-by: Kyungmin Park Reviewed-by: Vivek Gautam Reviewed-by: Jingoo Han Acked-by: Kishon Vijay Abraham I Cc: Kamil Debski Signed-off-by: Felipe Balbi --- drivers/usb/phy/Kconfig | 8 - drivers/usb/phy/Makefile | 1 - drivers/usb/phy/phy-samsung-usb3.c | 350 ------------------------------------- 3 files changed, 359 deletions(-) delete mode 100644 drivers/usb/phy/phy-samsung-usb3.c (limited to 'drivers/usb') diff --git a/drivers/usb/phy/Kconfig b/drivers/usb/phy/Kconfig index 452e252..0cd1f44 100644 --- a/drivers/usb/phy/Kconfig +++ b/drivers/usb/phy/Kconfig @@ -78,14 +78,6 @@ config SAMSUNG_USBPHY This driver provides common interface to interact, for Samsung USB 2.0 PHY driver and later for Samsung USB 3.0 PHY driver. -config SAMSUNG_USB3PHY - tristate "Samsung USB 3.0 PHY controller Driver" - select SAMSUNG_USBPHY - select USB_PHY - help - Enable this to support Samsung USB 3.0 (Super Speed) phy controller - for samsung SoCs. - config TWL6030_USB tristate "TWL6030 USB Transceiver Driver" depends on TWL4030_CORE && OMAP_USB2 && USB_MUSB_OMAP2PLUS diff --git a/drivers/usb/phy/Makefile b/drivers/usb/phy/Makefile index c651005..75f2bba 100644 --- a/drivers/usb/phy/Makefile +++ b/drivers/usb/phy/Makefile @@ -15,7 +15,6 @@ obj-$(CONFIG_AM335X_CONTROL_USB) += phy-am335x-control.o obj-$(CONFIG_AM335X_PHY_USB) += phy-am335x.o obj-$(CONFIG_OMAP_OTG) += phy-omap-otg.o obj-$(CONFIG_SAMSUNG_USBPHY) += phy-samsung-usb.o -obj-$(CONFIG_SAMSUNG_USB3PHY) += phy-samsung-usb3.o obj-$(CONFIG_TWL6030_USB) += phy-twl6030-usb.o obj-$(CONFIG_USB_EHCI_TEGRA) += phy-tegra-usb.o obj-$(CONFIG_USB_GPIO_VBUS) += phy-gpio-vbus-usb.o diff --git a/drivers/usb/phy/phy-samsung-usb3.c b/drivers/usb/phy/phy-samsung-usb3.c deleted file mode 100644 index cc08192..0000000 --- a/drivers/usb/phy/phy-samsung-usb3.c +++ /dev/null @@ -1,350 +0,0 @@ -/* linux/drivers/usb/phy/phy-samsung-usb3.c - * - * Copyright (c) 2013 Samsung Electronics Co., Ltd. - * http://www.samsung.com - * - * Author: Vivek Gautam - * - * Samsung USB 3.0 PHY transceiver; talks to DWC3 controller. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "phy-samsung-usb.h" - -/* - * Sets the phy clk as EXTREFCLK (XXTI) which is internal clock from clock core. - */ -static u32 samsung_usb3phy_set_refclk(struct samsung_usbphy *sphy) -{ - u32 reg; - u32 refclk; - - refclk = sphy->ref_clk_freq; - - reg = PHYCLKRST_REFCLKSEL_EXT_REFCLK | - PHYCLKRST_FSEL(refclk); - - switch (refclk) { - case FSEL_CLKSEL_50M: - reg |= (PHYCLKRST_MPLL_MULTIPLIER_50M_REF | - PHYCLKRST_SSC_REFCLKSEL(0x00)); - break; - case FSEL_CLKSEL_20M: - reg |= (PHYCLKRST_MPLL_MULTIPLIER_20MHZ_REF | - PHYCLKRST_SSC_REFCLKSEL(0x00)); - break; - case FSEL_CLKSEL_19200K: - reg |= (PHYCLKRST_MPLL_MULTIPLIER_19200KHZ_REF | - PHYCLKRST_SSC_REFCLKSEL(0x88)); - break; - case FSEL_CLKSEL_24M: - default: - reg |= (PHYCLKRST_MPLL_MULTIPLIER_24MHZ_REF | - PHYCLKRST_SSC_REFCLKSEL(0x88)); - break; - } - - return reg; -} - -static void samsung_exynos5_usb3phy_enable(struct samsung_usbphy *sphy) -{ - void __iomem *regs = sphy->regs; - u32 phyparam0; - u32 phyparam1; - u32 linksystem; - u32 phybatchg; - u32 phytest; - u32 phyclkrst; - - /* Reset USB 3.0 PHY */ - writel(0x0, regs + EXYNOS5_DRD_PHYREG0); - - phyparam0 = readl(regs + EXYNOS5_DRD_PHYPARAM0); - /* Select PHY CLK source */ - phyparam0 &= ~PHYPARAM0_REF_USE_PAD; - /* Set Loss-of-Signal Detector sensitivity */ - phyparam0 &= ~PHYPARAM0_REF_LOSLEVEL_MASK; - phyparam0 |= PHYPARAM0_REF_LOSLEVEL; - writel(phyparam0, regs + EXYNOS5_DRD_PHYPARAM0); - - writel(0x0, regs + EXYNOS5_DRD_PHYRESUME); - - /* - * Setting the Frame length Adj value[6:1] to default 0x20 - * See xHCI 1.0 spec, 5.2.4 - */ - linksystem = LINKSYSTEM_XHCI_VERSION_CONTROL | - LINKSYSTEM_FLADJ(0x20); - writel(linksystem, regs + EXYNOS5_DRD_LINKSYSTEM); - - phyparam1 = readl(regs + EXYNOS5_DRD_PHYPARAM1); - /* Set Tx De-Emphasis level */ - phyparam1 &= ~PHYPARAM1_PCS_TXDEEMPH_MASK; - phyparam1 |= PHYPARAM1_PCS_TXDEEMPH; - writel(phyparam1, regs + EXYNOS5_DRD_PHYPARAM1); - - phybatchg = readl(regs + EXYNOS5_DRD_PHYBATCHG); - phybatchg |= PHYBATCHG_UTMI_CLKSEL; - writel(phybatchg, regs + EXYNOS5_DRD_PHYBATCHG); - - /* PHYTEST POWERDOWN Control */ - phytest = readl(regs + EXYNOS5_DRD_PHYTEST); - phytest &= ~(PHYTEST_POWERDOWN_SSP | - PHYTEST_POWERDOWN_HSP); - writel(phytest, regs + EXYNOS5_DRD_PHYTEST); - - /* UTMI Power Control */ - writel(PHYUTMI_OTGDISABLE, regs + EXYNOS5_DRD_PHYUTMI); - - phyclkrst = samsung_usb3phy_set_refclk(sphy); - - phyclkrst |= PHYCLKRST_PORTRESET | - /* Digital power supply in normal operating mode */ - PHYCLKRST_RETENABLEN | - /* Enable ref clock for SS function */ - PHYCLKRST_REF_SSP_EN | - /* Enable spread spectrum */ - PHYCLKRST_SSC_EN | - /* Power down HS Bias and PLL blocks in suspend mode */ - PHYCLKRST_COMMONONN; - - writel(phyclkrst, regs + EXYNOS5_DRD_PHYCLKRST); - - udelay(10); - - phyclkrst &= ~(PHYCLKRST_PORTRESET); - writel(phyclkrst, regs + EXYNOS5_DRD_PHYCLKRST); -} - -static void samsung_exynos5_usb3phy_disable(struct samsung_usbphy *sphy) -{ - u32 phyutmi; - u32 phyclkrst; - u32 phytest; - void __iomem *regs = sphy->regs; - - phyutmi = PHYUTMI_OTGDISABLE | - PHYUTMI_FORCESUSPEND | - PHYUTMI_FORCESLEEP; - writel(phyutmi, regs + EXYNOS5_DRD_PHYUTMI); - - /* Resetting the PHYCLKRST enable bits to reduce leakage current */ - phyclkrst = readl(regs + EXYNOS5_DRD_PHYCLKRST); - phyclkrst &= ~(PHYCLKRST_REF_SSP_EN | - PHYCLKRST_SSC_EN | - PHYCLKRST_COMMONONN); - writel(phyclkrst, regs + EXYNOS5_DRD_PHYCLKRST); - - /* Control PHYTEST to remove leakage current */ - phytest = readl(regs + EXYNOS5_DRD_PHYTEST); - phytest |= (PHYTEST_POWERDOWN_SSP | - PHYTEST_POWERDOWN_HSP); - writel(phytest, regs + EXYNOS5_DRD_PHYTEST); -} - -static int samsung_usb3phy_init(struct usb_phy *phy) -{ - struct samsung_usbphy *sphy; - unsigned long flags; - int ret = 0; - - sphy = phy_to_sphy(phy); - - /* Enable the phy clock */ - ret = clk_prepare_enable(sphy->clk); - if (ret) { - dev_err(sphy->dev, "%s: clk_prepare_enable failed\n", __func__); - return ret; - } - - spin_lock_irqsave(&sphy->lock, flags); - - /* setting default phy-type for USB 3.0 */ - samsung_usbphy_set_type(&sphy->phy, USB_PHY_TYPE_DEVICE); - - /* Disable phy isolation */ - if (sphy->drv_data->set_isolation) - sphy->drv_data->set_isolation(sphy, false); - - /* Initialize usb phy registers */ - sphy->drv_data->phy_enable(sphy); - - spin_unlock_irqrestore(&sphy->lock, flags); - - /* Disable the phy clock */ - clk_disable_unprepare(sphy->clk); - - return ret; -} - -/* - * The function passed to the usb driver for phy shutdown - */ -static void samsung_usb3phy_shutdown(struct usb_phy *phy) -{ - struct samsung_usbphy *sphy; - unsigned long flags; - - sphy = phy_to_sphy(phy); - - if (clk_prepare_enable(sphy->clk)) { - dev_err(sphy->dev, "%s: clk_prepare_enable failed\n", __func__); - return; - } - - spin_lock_irqsave(&sphy->lock, flags); - - /* setting default phy-type for USB 3.0 */ - samsung_usbphy_set_type(&sphy->phy, USB_PHY_TYPE_DEVICE); - - /* De-initialize usb phy registers */ - sphy->drv_data->phy_disable(sphy); - - /* Enable phy isolation */ - if (sphy->drv_data->set_isolation) - sphy->drv_data->set_isolation(sphy, true); - - spin_unlock_irqrestore(&sphy->lock, flags); - - clk_disable_unprepare(sphy->clk); -} - -static int samsung_usb3phy_probe(struct platform_device *pdev) -{ - struct samsung_usbphy *sphy; - struct samsung_usbphy_data *pdata = dev_get_platdata(&pdev->dev); - struct device *dev = &pdev->dev; - struct resource *phy_mem; - void __iomem *phy_base; - struct clk *clk; - int ret; - - phy_mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); - phy_base = devm_ioremap_resource(dev, phy_mem); - if (IS_ERR(phy_base)) - return PTR_ERR(phy_base); - - sphy = devm_kzalloc(dev, sizeof(*sphy), GFP_KERNEL); - if (!sphy) - return -ENOMEM; - - clk = devm_clk_get(dev, "usbdrd30"); - if (IS_ERR(clk)) { - dev_err(dev, "Failed to get device clock\n"); - return PTR_ERR(clk); - } - - sphy->dev = dev; - - if (dev->of_node) { - ret = samsung_usbphy_parse_dt(sphy); - if (ret < 0) - return ret; - } else { - if (!pdata) { - dev_err(dev, "no platform data specified\n"); - return -EINVAL; - } - } - - sphy->plat = pdata; - sphy->regs = phy_base; - sphy->clk = clk; - sphy->phy.dev = sphy->dev; - sphy->phy.label = "samsung-usb3phy"; - sphy->phy.type = USB_PHY_TYPE_USB3; - sphy->phy.init = samsung_usb3phy_init; - sphy->phy.shutdown = samsung_usb3phy_shutdown; - sphy->drv_data = samsung_usbphy_get_driver_data(pdev); - - sphy->ref_clk_freq = samsung_usbphy_get_refclk_freq(sphy); - if (sphy->ref_clk_freq < 0) - return -EINVAL; - - spin_lock_init(&sphy->lock); - - platform_set_drvdata(pdev, sphy); - - return usb_add_phy_dev(&sphy->phy); -} - -static int samsung_usb3phy_remove(struct platform_device *pdev) -{ - struct samsung_usbphy *sphy = platform_get_drvdata(pdev); - - usb_remove_phy(&sphy->phy); - - if (sphy->pmuregs) - iounmap(sphy->pmuregs); - if (sphy->sysreg) - iounmap(sphy->sysreg); - - return 0; -} - -static struct samsung_usbphy_drvdata usb3phy_exynos5 = { - .cpu_type = TYPE_EXYNOS5250, - .devphy_en_mask = EXYNOS_USBPHY_ENABLE, - .rate_to_clksel = samsung_usbphy_rate_to_clksel_4x12, - .set_isolation = samsung_usbphy_set_isolation_4210, - .phy_enable = samsung_exynos5_usb3phy_enable, - .phy_disable = samsung_exynos5_usb3phy_disable, -}; - -#ifdef CONFIG_OF -static const struct of_device_id samsung_usbphy_dt_match[] = { - { - .compatible = "samsung,exynos5250-usb3phy", - .data = &usb3phy_exynos5 - }, - {}, -}; -MODULE_DEVICE_TABLE(of, samsung_usbphy_dt_match); -#endif - -static struct platform_device_id samsung_usbphy_driver_ids[] = { - { - .name = "exynos5250-usb3phy", - .driver_data = (unsigned long)&usb3phy_exynos5, - }, - {}, -}; - -MODULE_DEVICE_TABLE(platform, samsung_usbphy_driver_ids); - -static struct platform_driver samsung_usb3phy_driver = { - .probe = samsung_usb3phy_probe, - .remove = samsung_usb3phy_remove, - .id_table = samsung_usbphy_driver_ids, - .driver = { - .name = "samsung-usb3phy", - .owner = THIS_MODULE, - .of_match_table = of_match_ptr(samsung_usbphy_dt_match), - }, -}; - -module_platform_driver(samsung_usb3phy_driver); - -MODULE_DESCRIPTION("Samsung USB 3.0 phy controller"); -MODULE_AUTHOR("Vivek Gautam "); -MODULE_LICENSE("GPL"); -MODULE_ALIAS("platform:samsung-usb3phy"); -- cgit v1.1 From ea2fdf842365066c82ab941086c6a1741ced4f2a Mon Sep 17 00:00:00 2001 From: Bartlomiej Zolnierkiewicz Date: Thu, 28 Aug 2014 13:58:53 +0200 Subject: usb: phy: samsung: remove old common USB PHY code drivers/usb/phy/phy-samsung-usb[2,3] drivers got replaced by drivers/phy/phy-samsung-usb[2,3] ones and the old common Samsung USB PHY code is no longer used. Signed-off-by: Bartlomiej Zolnierkiewicz Acked-by: Kyungmin Park Reviewed-by: Vivek Gautam Reviewed-by: Jingoo Han Acked-by: Kishon Vijay Abraham I Cc: Kamil Debski Signed-off-by: Felipe Balbi --- drivers/usb/phy/phy-samsung-usb.c | 241 ----------------------------- drivers/usb/phy/phy-samsung-usb.h | 317 -------------------------------------- 2 files changed, 558 deletions(-) delete mode 100644 drivers/usb/phy/phy-samsung-usb.c delete mode 100644 drivers/usb/phy/phy-samsung-usb.h (limited to 'drivers/usb') diff --git a/drivers/usb/phy/phy-samsung-usb.c b/drivers/usb/phy/phy-samsung-usb.c deleted file mode 100644 index ac025ca..0000000 --- a/drivers/usb/phy/phy-samsung-usb.c +++ /dev/null @@ -1,241 +0,0 @@ -/* linux/drivers/usb/phy/phy-samsung-usb.c - * - * Copyright (c) 2012 Samsung Electronics Co., Ltd. - * http://www.samsung.com - * - * Author: Praveen Paneri - * - * Samsung USB-PHY helper driver with common function calls; - * interacts with Samsung USB 2.0 PHY controller driver and later - * with Samsung USB 3.0 PHY driver. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "phy-samsung-usb.h" - -int samsung_usbphy_parse_dt(struct samsung_usbphy *sphy) -{ - struct device_node *usbphy_sys; - - /* Getting node for system controller interface for usb-phy */ - usbphy_sys = of_get_child_by_name(sphy->dev->of_node, "usbphy-sys"); - if (!usbphy_sys) { - dev_err(sphy->dev, "No sys-controller interface for usb-phy\n"); - return -ENODEV; - } - - sphy->pmuregs = of_iomap(usbphy_sys, 0); - - if (sphy->pmuregs == NULL) { - dev_err(sphy->dev, "Can't get usb-phy pmu control register\n"); - goto err0; - } - - sphy->sysreg = of_iomap(usbphy_sys, 1); - - /* - * Not returning error code here, since this situation is not fatal. - * Few SoCs may not have this switch available - */ - if (sphy->sysreg == NULL) - dev_warn(sphy->dev, "Can't get usb-phy sysreg cfg register\n"); - - of_node_put(usbphy_sys); - - return 0; - -err0: - of_node_put(usbphy_sys); - return -ENXIO; -} -EXPORT_SYMBOL_GPL(samsung_usbphy_parse_dt); - -/* - * Set isolation here for phy. - * Here 'on = true' would mean USB PHY block is isolated, hence - * de-activated and vice-versa. - */ -void samsung_usbphy_set_isolation_4210(struct samsung_usbphy *sphy, bool on) -{ - void __iomem *reg = NULL; - u32 reg_val; - u32 en_mask = 0; - - if (!sphy->pmuregs) { - dev_warn(sphy->dev, "Can't set pmu isolation\n"); - return; - } - - if (sphy->phy_type == USB_PHY_TYPE_DEVICE) { - reg = sphy->pmuregs + sphy->drv_data->devphy_reg_offset; - en_mask = sphy->drv_data->devphy_en_mask; - } else if (sphy->phy_type == USB_PHY_TYPE_HOST) { - reg = sphy->pmuregs + sphy->drv_data->hostphy_reg_offset; - en_mask = sphy->drv_data->hostphy_en_mask; - } - - reg_val = readl(reg); - - if (on) - reg_val &= ~en_mask; - else - reg_val |= en_mask; - - writel(reg_val, reg); - - if (sphy->drv_data->cpu_type == TYPE_EXYNOS4X12) { - writel(reg_val, sphy->pmuregs + EXYNOS4X12_PHY_HSIC_CTRL0); - writel(reg_val, sphy->pmuregs + EXYNOS4X12_PHY_HSIC_CTRL1); - } -} -EXPORT_SYMBOL_GPL(samsung_usbphy_set_isolation_4210); - -/* - * Configure the mode of working of usb-phy here: HOST/DEVICE. - */ -void samsung_usbphy_cfg_sel(struct samsung_usbphy *sphy) -{ - u32 reg; - - if (!sphy->sysreg) { - dev_warn(sphy->dev, "Can't configure specified phy mode\n"); - return; - } - - reg = readl(sphy->sysreg); - - if (sphy->phy_type == USB_PHY_TYPE_DEVICE) - reg &= ~EXYNOS_USB20PHY_CFG_HOST_LINK; - else if (sphy->phy_type == USB_PHY_TYPE_HOST) - reg |= EXYNOS_USB20PHY_CFG_HOST_LINK; - - writel(reg, sphy->sysreg); -} -EXPORT_SYMBOL_GPL(samsung_usbphy_cfg_sel); - -/* - * PHYs are different for USB Device and USB Host. - * This make sure that correct PHY type is selected before - * any operation on PHY. - */ -int samsung_usbphy_set_type(struct usb_phy *phy, - enum samsung_usb_phy_type phy_type) -{ - struct samsung_usbphy *sphy = phy_to_sphy(phy); - - sphy->phy_type = phy_type; - - return 0; -} -EXPORT_SYMBOL_GPL(samsung_usbphy_set_type); - -int samsung_usbphy_rate_to_clksel_64xx(struct samsung_usbphy *sphy, - unsigned long rate) -{ - unsigned int clksel; - - switch (rate) { - case 12 * MHZ: - clksel = PHYCLK_CLKSEL_12M; - break; - case 24 * MHZ: - clksel = PHYCLK_CLKSEL_24M; - break; - case 48 * MHZ: - clksel = PHYCLK_CLKSEL_48M; - break; - default: - dev_err(sphy->dev, - "Invalid reference clock frequency: %lu\n", rate); - return -EINVAL; - } - - return clksel; -} -EXPORT_SYMBOL_GPL(samsung_usbphy_rate_to_clksel_64xx); - -int samsung_usbphy_rate_to_clksel_4x12(struct samsung_usbphy *sphy, - unsigned long rate) -{ - unsigned int clksel; - - switch (rate) { - case 9600 * KHZ: - clksel = FSEL_CLKSEL_9600K; - break; - case 10 * MHZ: - clksel = FSEL_CLKSEL_10M; - break; - case 12 * MHZ: - clksel = FSEL_CLKSEL_12M; - break; - case 19200 * KHZ: - clksel = FSEL_CLKSEL_19200K; - break; - case 20 * MHZ: - clksel = FSEL_CLKSEL_20M; - break; - case 24 * MHZ: - clksel = FSEL_CLKSEL_24M; - break; - case 50 * MHZ: - clksel = FSEL_CLKSEL_50M; - break; - default: - dev_err(sphy->dev, - "Invalid reference clock frequency: %lu\n", rate); - return -EINVAL; - } - - return clksel; -} -EXPORT_SYMBOL_GPL(samsung_usbphy_rate_to_clksel_4x12); - -/* - * Returns reference clock frequency selection value - */ -int samsung_usbphy_get_refclk_freq(struct samsung_usbphy *sphy) -{ - struct clk *ref_clk; - unsigned long rate; - int refclk_freq; - - /* - * In exynos5250 USB host and device PHY use - * external crystal clock XXTI - */ - if (sphy->drv_data->cpu_type == TYPE_EXYNOS5250) - ref_clk = clk_get(sphy->dev, "ext_xtal"); - else - ref_clk = clk_get(sphy->dev, "xusbxti"); - if (IS_ERR(ref_clk)) { - dev_err(sphy->dev, "Failed to get reference clock\n"); - return PTR_ERR(ref_clk); - } - - rate = clk_get_rate(ref_clk); - refclk_freq = sphy->drv_data->rate_to_clksel(sphy, rate); - - clk_put(ref_clk); - - return refclk_freq; -} -EXPORT_SYMBOL_GPL(samsung_usbphy_get_refclk_freq); diff --git a/drivers/usb/phy/phy-samsung-usb.h b/drivers/usb/phy/phy-samsung-usb.h deleted file mode 100644 index 4eef455..0000000 --- a/drivers/usb/phy/phy-samsung-usb.h +++ /dev/null @@ -1,317 +0,0 @@ -/* linux/drivers/usb/phy/phy-samsung-usb.h - * - * Copyright (c) 2012 Samsung Electronics Co., Ltd. - * http://www.samsung.com - * - * Samsung USB-PHY transceiver; talks to S3C HS OTG controller, EHCI-S5P and - * OHCI-EXYNOS controllers. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - */ - -#include - -/* Register definitions */ - -#define SAMSUNG_PHYPWR (0x00) -#define PHYPWR_NORMAL_MASK (0x19 << 0) -#define PHYPWR_OTG_DISABLE (0x1 << 4) -#define PHYPWR_ANALOG_POWERDOWN (0x1 << 3) -#define PHYPWR_FORCE_SUSPEND (0x1 << 1) -/* For Exynos4 */ -#define PHYPWR_NORMAL_MASK_PHY0 (0x39 << 0) -#define PHYPWR_SLEEP_PHY0 (0x1 << 5) - -#define SAMSUNG_PHYCLK (0x04) -#define PHYCLK_MODE_USB11 (0x1 << 6) -#define PHYCLK_EXT_OSC (0x1 << 5) -#define PHYCLK_COMMON_ON_N (0x1 << 4) -#define PHYCLK_ID_PULL (0x1 << 2) -#define PHYCLK_CLKSEL_MASK (0x3 << 0) -#define PHYCLK_CLKSEL_48M (0x0 << 0) -#define PHYCLK_CLKSEL_12M (0x2 << 0) -#define PHYCLK_CLKSEL_24M (0x3 << 0) - -#define SAMSUNG_RSTCON (0x08) -#define RSTCON_PHYLINK_SWRST (0x1 << 2) -#define RSTCON_HLINK_SWRST (0x1 << 1) -#define RSTCON_SWRST (0x1 << 0) - -/* EXYNOS4X12 */ -#define EXYNOS4X12_PHY_HSIC_CTRL0 (0x04) -#define EXYNOS4X12_PHY_HSIC_CTRL1 (0x08) -#define PHYPWR_NORMAL_MASK_HSIC1 (0x7 << 12) -#define PHYPWR_NORMAL_MASK_HSIC0 (0x7 << 9) -#define PHYPWR_NORMAL_MASK_PHY1 (0x7 << 6) -#define RSTCON_HOSTPHY_SWRST (0xf << 3) - -/* EXYNOS5 */ -#define EXYNOS5_PHY_HOST_CTRL0 (0x00) -#define HOST_CTRL0_PHYSWRSTALL (0x1 << 31) -#define HOST_CTRL0_REFCLKSEL_MASK (0x3 << 19) -#define HOST_CTRL0_REFCLKSEL_XTAL (0x0 << 19) -#define HOST_CTRL0_REFCLKSEL_EXTL (0x1 << 19) -#define HOST_CTRL0_REFCLKSEL_CLKCORE (0x2 << 19) -#define HOST_CTRL0_FSEL_MASK (0x7 << 16) -#define HOST_CTRL0_FSEL(_x) ((_x) << 16) -#define FSEL_CLKSEL_50M (0x7) -#define FSEL_CLKSEL_24M (0x5) -#define FSEL_CLKSEL_20M (0x4) -#define FSEL_CLKSEL_19200K (0x3) -#define FSEL_CLKSEL_12M (0x2) -#define FSEL_CLKSEL_10M (0x1) -#define FSEL_CLKSEL_9600K (0x0) -#define HOST_CTRL0_TESTBURNIN (0x1 << 11) -#define HOST_CTRL0_RETENABLE (0x1 << 10) -#define HOST_CTRL0_COMMONON_N (0x1 << 9) -#define HOST_CTRL0_SIDDQ (0x1 << 6) -#define HOST_CTRL0_FORCESLEEP (0x1 << 5) -#define HOST_CTRL0_FORCESUSPEND (0x1 << 4) -#define HOST_CTRL0_WORDINTERFACE (0x1 << 3) -#define HOST_CTRL0_UTMISWRST (0x1 << 2) -#define HOST_CTRL0_LINKSWRST (0x1 << 1) -#define HOST_CTRL0_PHYSWRST (0x1 << 0) - -#define EXYNOS5_PHY_HOST_TUNE0 (0x04) - -#define EXYNOS5_PHY_HSIC_CTRL1 (0x10) - -#define EXYNOS5_PHY_HSIC_TUNE1 (0x14) - -#define EXYNOS5_PHY_HSIC_CTRL2 (0x20) - -#define EXYNOS5_PHY_HSIC_TUNE2 (0x24) -#define HSIC_CTRL_REFCLKSEL_MASK (0x3 << 23) -#define HSIC_CTRL_REFCLKSEL (0x2 << 23) -#define HSIC_CTRL_REFCLKDIV_MASK (0x7f << 16) -#define HSIC_CTRL_REFCLKDIV(_x) ((_x) << 16) -#define HSIC_CTRL_REFCLKDIV_12 (0x24 << 16) -#define HSIC_CTRL_REFCLKDIV_15 (0x1c << 16) -#define HSIC_CTRL_REFCLKDIV_16 (0x1a << 16) -#define HSIC_CTRL_REFCLKDIV_19_2 (0x15 << 16) -#define HSIC_CTRL_REFCLKDIV_20 (0x14 << 16) -#define HSIC_CTRL_SIDDQ (0x1 << 6) -#define HSIC_CTRL_FORCESLEEP (0x1 << 5) -#define HSIC_CTRL_FORCESUSPEND (0x1 << 4) -#define HSIC_CTRL_WORDINTERFACE (0x1 << 3) -#define HSIC_CTRL_UTMISWRST (0x1 << 2) -#define HSIC_CTRL_PHYSWRST (0x1 << 0) - -#define EXYNOS5_PHY_HOST_EHCICTRL (0x30) -#define HOST_EHCICTRL_ENAINCRXALIGN (0x1 << 29) -#define HOST_EHCICTRL_ENAINCR4 (0x1 << 28) -#define HOST_EHCICTRL_ENAINCR8 (0x1 << 27) -#define HOST_EHCICTRL_ENAINCR16 (0x1 << 26) - -#define EXYNOS5_PHY_HOST_OHCICTRL (0x34) -#define HOST_OHCICTRL_SUSPLGCY (0x1 << 3) -#define HOST_OHCICTRL_APPSTARTCLK (0x1 << 2) -#define HOST_OHCICTRL_CNTSEL (0x1 << 1) -#define HOST_OHCICTRL_CLKCKTRST (0x1 << 0) - -#define EXYNOS5_PHY_OTG_SYS (0x38) -#define OTG_SYS_PHYLINK_SWRESET (0x1 << 14) -#define OTG_SYS_LINKSWRST_UOTG (0x1 << 13) -#define OTG_SYS_PHY0_SWRST (0x1 << 12) -#define OTG_SYS_REFCLKSEL_MASK (0x3 << 9) -#define OTG_SYS_REFCLKSEL_XTAL (0x0 << 9) -#define OTG_SYS_REFCLKSEL_EXTL (0x1 << 9) -#define OTG_SYS_REFCLKSEL_CLKCORE (0x2 << 9) -#define OTG_SYS_IDPULLUP_UOTG (0x1 << 8) -#define OTG_SYS_COMMON_ON (0x1 << 7) -#define OTG_SYS_FSEL_MASK (0x7 << 4) -#define OTG_SYS_FSEL(_x) ((_x) << 4) -#define OTG_SYS_FORCESLEEP (0x1 << 3) -#define OTG_SYS_OTGDISABLE (0x1 << 2) -#define OTG_SYS_SIDDQ_UOTG (0x1 << 1) -#define OTG_SYS_FORCESUSPEND (0x1 << 0) - -#define EXYNOS5_PHY_OTG_TUNE (0x40) - -/* EXYNOS5: USB 3.0 DRD */ -#define EXYNOS5_DRD_LINKSYSTEM (0x04) -#define LINKSYSTEM_FLADJ_MASK (0x3f << 1) -#define LINKSYSTEM_FLADJ(_x) ((_x) << 1) -#define LINKSYSTEM_XHCI_VERSION_CONTROL (0x1 << 27) - -#define EXYNOS5_DRD_PHYUTMI (0x08) -#define PHYUTMI_OTGDISABLE (0x1 << 6) -#define PHYUTMI_FORCESUSPEND (0x1 << 1) -#define PHYUTMI_FORCESLEEP (0x1 << 0) - -#define EXYNOS5_DRD_PHYPIPE (0x0c) - -#define EXYNOS5_DRD_PHYCLKRST (0x10) -#define PHYCLKRST_SSC_REFCLKSEL_MASK (0xff << 23) -#define PHYCLKRST_SSC_REFCLKSEL(_x) ((_x) << 23) -#define PHYCLKRST_SSC_RANGE_MASK (0x03 << 21) -#define PHYCLKRST_SSC_RANGE(_x) ((_x) << 21) -#define PHYCLKRST_SSC_EN (0x1 << 20) -#define PHYCLKRST_REF_SSP_EN (0x1 << 19) -#define PHYCLKRST_REF_CLKDIV2 (0x1 << 18) -#define PHYCLKRST_MPLL_MULTIPLIER_MASK (0x7f << 11) -#define PHYCLKRST_MPLL_MULTIPLIER_100MHZ_REF (0x19 << 11) -#define PHYCLKRST_MPLL_MULTIPLIER_50M_REF (0x02 << 11) -#define PHYCLKRST_MPLL_MULTIPLIER_24MHZ_REF (0x68 << 11) -#define PHYCLKRST_MPLL_MULTIPLIER_20MHZ_REF (0x7d << 11) -#define PHYCLKRST_MPLL_MULTIPLIER_19200KHZ_REF (0x02 << 11) -#define PHYCLKRST_FSEL_MASK (0x3f << 5) -#define PHYCLKRST_FSEL(_x) ((_x) << 5) -#define PHYCLKRST_FSEL_PAD_100MHZ (0x27 << 5) -#define PHYCLKRST_FSEL_PAD_24MHZ (0x2a << 5) -#define PHYCLKRST_FSEL_PAD_20MHZ (0x31 << 5) -#define PHYCLKRST_FSEL_PAD_19_2MHZ (0x38 << 5) -#define PHYCLKRST_RETENABLEN (0x1 << 4) -#define PHYCLKRST_REFCLKSEL_MASK (0x03 << 2) -#define PHYCLKRST_REFCLKSEL_PAD_REFCLK (0x2 << 2) -#define PHYCLKRST_REFCLKSEL_EXT_REFCLK (0x3 << 2) -#define PHYCLKRST_PORTRESET (0x1 << 1) -#define PHYCLKRST_COMMONONN (0x1 << 0) - -#define EXYNOS5_DRD_PHYREG0 (0x14) - -#define EXYNOS5_DRD_PHYREG1 (0x18) - -#define EXYNOS5_DRD_PHYPARAM0 (0x1c) -#define PHYPARAM0_REF_USE_PAD (0x1 << 31) -#define PHYPARAM0_REF_LOSLEVEL_MASK (0x1f << 26) -#define PHYPARAM0_REF_LOSLEVEL (0x9 << 26) - -#define EXYNOS5_DRD_PHYPARAM1 (0x20) -#define PHYPARAM1_PCS_TXDEEMPH_MASK (0x1f << 0) -#define PHYPARAM1_PCS_TXDEEMPH (0x1c) - -#define EXYNOS5_DRD_PHYTERM (0x24) - -#define EXYNOS5_DRD_PHYTEST (0x28) -#define PHYTEST_POWERDOWN_SSP (0x1 << 3) -#define PHYTEST_POWERDOWN_HSP (0x1 << 2) - -#define EXYNOS5_DRD_PHYADP (0x2c) - -#define EXYNOS5_DRD_PHYBATCHG (0x30) -#define PHYBATCHG_UTMI_CLKSEL (0x1 << 2) - -#define EXYNOS5_DRD_PHYRESUME (0x34) - -#define EXYNOS5_DRD_LINKPORT (0x44) - -#ifndef MHZ -#define MHZ (1000*1000) -#endif - -#ifndef KHZ -#define KHZ (1000) -#endif - -#define EXYNOS_USBHOST_PHY_CTRL_OFFSET (0x4) -#define S3C64XX_USBPHY_ENABLE (0x1 << 16) -#define EXYNOS_USBPHY_ENABLE (0x1 << 0) -#define EXYNOS_USB20PHY_CFG_HOST_LINK (0x1 << 0) - -enum samsung_cpu_type { - TYPE_S3C64XX, - TYPE_EXYNOS4210, - TYPE_EXYNOS4X12, - TYPE_EXYNOS5250, -}; - -struct samsung_usbphy; - -/* - * struct samsung_usbphy_drvdata - driver data for various SoC variants - * @cpu_type: machine identifier - * @devphy_en_mask: device phy enable mask for PHY CONTROL register - * @hostphy_en_mask: host phy enable mask for PHY CONTROL register - * @devphy_reg_offset: offset to DEVICE PHY CONTROL register from - * mapped address of system controller. - * @hostphy_reg_offset: offset to HOST PHY CONTROL register from - * mapped address of system controller. - * - * Here we have a separate mask for device type phy. - * Having different masks for host and device type phy helps - * in setting independent masks in case of SoCs like S5PV210, - * in which PHY0 and PHY1 enable bits belong to same register - * placed at position 0 and 1 respectively. - * Although for newer SoCs like exynos these bits belong to - * different registers altogether placed at position 0. - */ -struct samsung_usbphy_drvdata { - int cpu_type; - int devphy_en_mask; - int hostphy_en_mask; - u32 devphy_reg_offset; - u32 hostphy_reg_offset; - int (*rate_to_clksel)(struct samsung_usbphy *, unsigned long); - void (*set_isolation)(struct samsung_usbphy *, bool); - void (*phy_enable)(struct samsung_usbphy *); - void (*phy_disable)(struct samsung_usbphy *); -}; - -/* - * struct samsung_usbphy - transceiver driver state - * @phy: transceiver structure - * @plat: platform data - * @dev: The parent device supplied to the probe function - * @clk: usb phy clock - * @regs: usb phy controller registers memory base - * @pmuregs: USB device PHY_CONTROL register memory base - * @sysreg: USB2.0 PHY_CFG register memory base - * @ref_clk_freq: reference clock frequency selection - * @drv_data: driver data available for different SoCs - * @phy_type: Samsung SoCs specific phy types: #HOST - * #DEVICE - * @phy_usage: usage count for phy - * @lock: lock for phy operations - */ -struct samsung_usbphy { - struct usb_phy phy; - struct samsung_usbphy_data *plat; - struct device *dev; - struct clk *clk; - void __iomem *regs; - void __iomem *pmuregs; - void __iomem *sysreg; - int ref_clk_freq; - const struct samsung_usbphy_drvdata *drv_data; - enum samsung_usb_phy_type phy_type; - atomic_t phy_usage; - spinlock_t lock; -}; - -#define phy_to_sphy(x) container_of((x), struct samsung_usbphy, phy) - -static const struct of_device_id samsung_usbphy_dt_match[]; - -static inline const struct samsung_usbphy_drvdata -*samsung_usbphy_get_driver_data(struct platform_device *pdev) -{ - if (pdev->dev.of_node) { - const struct of_device_id *match; - match = of_match_node(samsung_usbphy_dt_match, - pdev->dev.of_node); - return match->data; - } - - return (struct samsung_usbphy_drvdata *) - platform_get_device_id(pdev)->driver_data; -} - -extern int samsung_usbphy_parse_dt(struct samsung_usbphy *sphy); -extern void samsung_usbphy_set_isolation_4210(struct samsung_usbphy *sphy, - bool on); -extern void samsung_usbphy_cfg_sel(struct samsung_usbphy *sphy); -extern int samsung_usbphy_set_type(struct usb_phy *phy, - enum samsung_usb_phy_type phy_type); -extern int samsung_usbphy_get_refclk_freq(struct samsung_usbphy *sphy); -extern int samsung_usbphy_rate_to_clksel_64xx(struct samsung_usbphy *sphy, - unsigned long rate); -extern int samsung_usbphy_rate_to_clksel_4x12(struct samsung_usbphy *sphy, - unsigned long rate); -- cgit v1.1 From fa9a582da9e78c8498654d5c9c221ec3690944c1 Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Fri, 29 Aug 2014 21:31:40 +0200 Subject: usb: gadget: USB_RENESAS_USBHS_UDC should depend on HAS_DMA If NO_DMA=y: drivers/built-in.o: In function `usbhsg_dma_map_ctrl': mod_gadget.c:(.text+0x53b226): undefined reference to `usb_gadget_map_request' mod_gadget.c:(.text+0x53b242): undefined reference to `usb_gadget_unmap_request' Signed-off-by: Geert Uytterhoeven Signed-off-by: Felipe Balbi --- drivers/usb/gadget/udc/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/usb') diff --git a/drivers/usb/gadget/udc/Kconfig b/drivers/usb/gadget/udc/Kconfig index 5151f94..00171d4 100644 --- a/drivers/usb/gadget/udc/Kconfig +++ b/drivers/usb/gadget/udc/Kconfig @@ -163,7 +163,7 @@ config USB_R8A66597 config USB_RENESAS_USBHS_UDC tristate 'Renesas USBHS controller' - depends on USB_RENESAS_USBHS + depends on USB_RENESAS_USBHS && HAS_DMA help Renesas USBHS is a discrete USB host and peripheral controller chip that supports both full and high speed USB 2.0 data transfers. -- cgit v1.1 From d3102a5eaac708ab83fede0986f90e9971be55d3 Mon Sep 17 00:00:00 2001 From: Tony Lindgren Date: Thu, 28 Aug 2014 11:19:02 -0700 Subject: usb: phy: twl6030-usb: Remove unused irq_enabled It's not being used any longer. Signed-off-by: Tony Lindgren Signed-off-by: Felipe Balbi --- drivers/usb/phy/phy-twl6030-usb.c | 2 -- 1 file changed, 2 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/phy/phy-twl6030-usb.c b/drivers/usb/phy/phy-twl6030-usb.c index 04778cf..44ea082 100644 --- a/drivers/usb/phy/phy-twl6030-usb.c +++ b/drivers/usb/phy/phy-twl6030-usb.c @@ -104,7 +104,6 @@ struct twl6030_usb { int irq2; enum omap_musb_vbus_id_status linkstat; u8 asleep; - bool irq_enabled; bool vbus_enable; const char *regulator; }; @@ -373,7 +372,6 @@ static int twl6030_usb_probe(struct platform_device *pdev) INIT_WORK(&twl->set_vbus_work, otg_set_vbus_work); - twl->irq_enabled = true; status = request_threaded_irq(twl->irq1, NULL, twl6030_usbotg_irq, IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING | IRQF_ONESHOT, "twl6030_usb", twl); -- cgit v1.1 From be0a8887bb931af0e21531da20c41533effbb0d6 Mon Sep 17 00:00:00 2001 From: Li Jun Date: Thu, 28 Aug 2014 21:44:11 +0800 Subject: usb: gadget: composite: dequeue cdev->req before free its buffer commit f226708(usb: gadget: composite: dequeue cdev->req before free it in composite_dev_cleanup) fixed a bug: free the usb request(i.e. cdev->req) but does not dequeue it beforehand. This fix is not proper enough because it dequeues the request after free its data buffer, considering the hardware can access the buffer's memory anytime before the request's complettion rountine runs, and usb_ep_dequeue always call the complettion rountine before it returns, so the best way is to dequeue the request before free its buffer. Suggested-by: Felipe Balbi Signed-off-by: Li Jun Signed-off-by: Felipe Balbi --- drivers/usb/gadget/composite.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/usb') diff --git a/drivers/usb/gadget/composite.c b/drivers/usb/gadget/composite.c index 6935a82..4514e73 100644 --- a/drivers/usb/gadget/composite.c +++ b/drivers/usb/gadget/composite.c @@ -1955,8 +1955,8 @@ void composite_dev_cleanup(struct usb_composite_dev *cdev) usb_ep_free_request(cdev->gadget->ep0, cdev->os_desc_req); } if (cdev->req) { - kfree(cdev->req->buf); usb_ep_dequeue(cdev->gadget->ep0, cdev->req); + kfree(cdev->req->buf); usb_ep_free_request(cdev->gadget->ep0, cdev->req); } cdev->next_string_id = 0; -- cgit v1.1 From 4953ef658910416655cdb4b61618458aa11302ab Mon Sep 17 00:00:00 2001 From: Michal Nazarewicz Date: Wed, 27 Aug 2014 22:58:48 +0200 Subject: usb: gadget: f_mass_storage: simplify start_transfer slightly Flatten the start_transfer function by reversing the if condition and returning early out of the function if everything went fine. It makes the function look less complicated, at least to me, and easier to understand. Signed-off-by: Michal Nazarewicz Signed-off-by: Felipe Balbi --- drivers/usb/gadget/function/f_mass_storage.c | 27 +++++++++++++-------------- 1 file changed, 13 insertions(+), 14 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/gadget/function/f_mass_storage.c b/drivers/usb/gadget/function/f_mass_storage.c index b963939..811929c 100644 --- a/drivers/usb/gadget/function/f_mass_storage.c +++ b/drivers/usb/gadget/function/f_mass_storage.c @@ -566,22 +566,22 @@ static void start_transfer(struct fsg_dev *fsg, struct usb_ep *ep, *pbusy = 1; *state = BUF_STATE_BUSY; spin_unlock_irq(&fsg->common->lock); + rc = usb_ep_queue(ep, req, GFP_KERNEL); - if (rc != 0) { - *pbusy = 0; - *state = BUF_STATE_EMPTY; + if (rc == 0) + return; /* All good, we're done */ - /* We can't do much more than wait for a reset */ + *pbusy = 0; + *state = BUF_STATE_EMPTY; - /* - * Note: currently the net2280 driver fails zero-length - * submissions if DMA is enabled. - */ - if (rc != -ESHUTDOWN && - !(rc == -EOPNOTSUPP && req->length == 0)) - WARNING(fsg, "error in submission: %s --> %d\n", - ep->name, rc); - } + /* We can't do much more than wait for a reset */ + + /* + * Note: currently the net2280 driver fails zero-length + * submissions if DMA is enabled. + */ + if (rc != -ESHUTDOWN && !(rc == -EOPNOTSUPP && req->length == 0)) + WARNING(fsg, "error in submission: %s --> %d\n", ep->name, rc); } static bool start_in_transfer(struct fsg_common *common, struct fsg_buffhd *bh) @@ -3665,4 +3665,3 @@ void fsg_config_from_params(struct fsg_config *cfg, cfg->fsg_num_buffers = fsg_num_buffers; } EXPORT_SYMBOL_GPL(fsg_config_from_params); - -- cgit v1.1 From 430fdbd3b7ea4a820d4c1aa936ede77433cfa818 Mon Sep 17 00:00:00 2001 From: Daniel Mack Date: Wed, 27 Aug 2014 19:09:03 +0200 Subject: usb: gadget: f_uac2: restructure some code in afunc_set_alt() Restructure some code to make it easier to read. While at it, return -ENOMEM instead of -EINVAL if usb_ep_alloc_request() fails, and omit the logging in such cases (the mm core will complain loud enough). Signed-off-by: Daniel Mack Signed-off-by: Felipe Balbi --- drivers/usb/gadget/function/f_uac2.c | 39 +++++++++++++++--------------------- 1 file changed, 16 insertions(+), 23 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/gadget/function/f_uac2.c b/drivers/usb/gadget/function/f_uac2.c index 0d65e7c..ab4652e 100644 --- a/drivers/usb/gadget/function/f_uac2.c +++ b/drivers/usb/gadget/function/f_uac2.c @@ -1104,31 +1104,24 @@ afunc_set_alt(struct usb_function *fn, unsigned intf, unsigned alt) usb_ep_enable(ep); for (i = 0; i < USB_XFERS; i++) { - if (prm->ureq[i].req) { - if (usb_ep_queue(ep, prm->ureq[i].req, GFP_ATOMIC)) - dev_err(&uac2->pdev.dev, "%d Error!\n", - __LINE__); - continue; - } - - req = usb_ep_alloc_request(ep, GFP_ATOMIC); - if (req == NULL) { - dev_err(&uac2->pdev.dev, - "%s:%d Error!\n", __func__, __LINE__); - return -EINVAL; + if (!prm->ureq[i].req) { + req = usb_ep_alloc_request(ep, GFP_ATOMIC); + if (req == NULL) + return -ENOMEM; + + prm->ureq[i].req = req; + prm->ureq[i].pp = prm; + + req->zero = 0; + req->context = &prm->ureq[i]; + req->length = prm->max_psize; + req->complete = agdev_iso_complete; + req->buf = prm->rbuf + i * req->length; } - prm->ureq[i].req = req; - prm->ureq[i].pp = prm; - - req->zero = 0; - req->context = &prm->ureq[i]; - req->length = prm->max_psize; - req->complete = agdev_iso_complete; - req->buf = prm->rbuf + i * req->length; - - if (usb_ep_queue(ep, req, GFP_ATOMIC)) - dev_err(&uac2->pdev.dev, "%d Error!\n", __LINE__); + if (usb_ep_queue(ep, prm->ureq[i].req, GFP_ATOMIC)) + dev_err(&uac2->pdev.dev, "%s:%d Error!\n", + __func__, __LINE__); } return 0; -- cgit v1.1 From a8147dabe56f7e7a4975e60abb613f7e62277577 Mon Sep 17 00:00:00 2001 From: Daniel Mack Date: Wed, 27 Aug 2014 19:09:04 +0200 Subject: usb: gadget: f_uac2: add short-hand for 'dev' In afunc_bind() and afunc_set_alt(), &uac2->pdev.dev are used multiple times. Adding a short-hand for them makes lines shorter so we can remove some line wraps. No functional change. Signed-off-by: Daniel Mack Signed-off-by: Felipe Balbi --- drivers/usb/gadget/function/f_uac2.c | 29 +++++++++++------------------ 1 file changed, 11 insertions(+), 18 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/gadget/function/f_uac2.c b/drivers/usb/gadget/function/f_uac2.c index ab4652e..efe8add 100644 --- a/drivers/usb/gadget/function/f_uac2.c +++ b/drivers/usb/gadget/function/f_uac2.c @@ -918,6 +918,7 @@ afunc_bind(struct usb_configuration *cfg, struct usb_function *fn) struct snd_uac2_chip *uac2 = &agdev->uac2; struct usb_composite_dev *cdev = cfg->cdev; struct usb_gadget *gadget = cdev->gadget; + struct device *dev = &uac2->pdev.dev; struct uac2_rtd_params *prm; struct f_uac2_opts *uac2_opts; struct usb_string *us; @@ -961,8 +962,7 @@ afunc_bind(struct usb_configuration *cfg, struct usb_function *fn) ret = usb_interface_id(cfg, fn); if (ret < 0) { - dev_err(&uac2->pdev.dev, - "%s:%d Error!\n", __func__, __LINE__); + dev_err(dev, "%s:%d Error!\n", __func__, __LINE__); return ret; } std_ac_if_desc.bInterfaceNumber = ret; @@ -971,8 +971,7 @@ afunc_bind(struct usb_configuration *cfg, struct usb_function *fn) ret = usb_interface_id(cfg, fn); if (ret < 0) { - dev_err(&uac2->pdev.dev, - "%s:%d Error!\n", __func__, __LINE__); + dev_err(dev, "%s:%d Error!\n", __func__, __LINE__); return ret; } std_as_out_if0_desc.bInterfaceNumber = ret; @@ -982,8 +981,7 @@ afunc_bind(struct usb_configuration *cfg, struct usb_function *fn) ret = usb_interface_id(cfg, fn); if (ret < 0) { - dev_err(&uac2->pdev.dev, - "%s:%d Error!\n", __func__, __LINE__); + dev_err(dev, "%s:%d Error!\n", __func__, __LINE__); return ret; } std_as_in_if0_desc.bInterfaceNumber = ret; @@ -993,16 +991,14 @@ afunc_bind(struct usb_configuration *cfg, struct usb_function *fn) agdev->out_ep = usb_ep_autoconfig(gadget, &fs_epout_desc); if (!agdev->out_ep) { - dev_err(&uac2->pdev.dev, - "%s:%d Error!\n", __func__, __LINE__); + dev_err(dev, "%s:%d Error!\n", __func__, __LINE__); goto err; } agdev->out_ep->driver_data = agdev; agdev->in_ep = usb_ep_autoconfig(gadget, &fs_epin_desc); if (!agdev->in_ep) { - dev_err(&uac2->pdev.dev, - "%s:%d Error!\n", __func__, __LINE__); + dev_err(dev, "%s:%d Error!\n", __func__, __LINE__); goto err; } agdev->in_ep->driver_data = agdev; @@ -1057,6 +1053,7 @@ afunc_set_alt(struct usb_function *fn, unsigned intf, unsigned alt) struct audio_dev *agdev = func_to_agdev(fn); struct snd_uac2_chip *uac2 = &agdev->uac2; struct usb_gadget *gadget = cdev->gadget; + struct device *dev = &uac2->pdev.dev; struct usb_request *req; struct usb_ep *ep; struct uac2_rtd_params *prm; @@ -1064,16 +1061,14 @@ afunc_set_alt(struct usb_function *fn, unsigned intf, unsigned alt) /* No i/f has more than 2 alt settings */ if (alt > 1) { - dev_err(&uac2->pdev.dev, - "%s:%d Error!\n", __func__, __LINE__); + dev_err(dev, "%s:%d Error!\n", __func__, __LINE__); return -EINVAL; } if (intf == agdev->ac_intf) { /* Control I/f has only 1 AltSetting - 0 */ if (alt) { - dev_err(&uac2->pdev.dev, - "%s:%d Error!\n", __func__, __LINE__); + dev_err(dev, "%s:%d Error!\n", __func__, __LINE__); return -EINVAL; } return 0; @@ -1090,8 +1085,7 @@ afunc_set_alt(struct usb_function *fn, unsigned intf, unsigned alt) config_ep_by_speed(gadget, fn, ep); agdev->as_in_alt = alt; } else { - dev_err(&uac2->pdev.dev, - "%s:%d Error!\n", __func__, __LINE__); + dev_err(dev, "%s:%d Error!\n", __func__, __LINE__); return -EINVAL; } @@ -1120,8 +1114,7 @@ afunc_set_alt(struct usb_function *fn, unsigned intf, unsigned alt) } if (usb_ep_queue(ep, prm->ureq[i].req, GFP_ATOMIC)) - dev_err(&uac2->pdev.dev, "%s:%d Error!\n", - __func__, __LINE__); + dev_err(dev, "%s:%d Error!\n", __func__, __LINE__); } return 0; -- cgit v1.1 From 254b3bf68b65ac8f82da1c7e0c1a2bb17012aa7d Mon Sep 17 00:00:00 2001 From: Daniel Mack Date: Wed, 27 Aug 2014 19:09:05 +0200 Subject: usb: gadget: f_uac2: introduce agdev_to_uac2_opts Add a simple container_of() wrapper to get a struct f_uac2_opts from a struct struct audio_dev. Use it in two places where it is currently open-coded. Signed-off-by: Daniel Mack Signed-off-by: Felipe Balbi --- drivers/usb/gadget/function/f_uac2.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/gadget/function/f_uac2.c b/drivers/usb/gadget/function/f_uac2.c index efe8add..9c8831d 100644 --- a/drivers/usb/gadget/function/f_uac2.c +++ b/drivers/usb/gadget/function/f_uac2.c @@ -140,6 +140,12 @@ struct snd_uac2_chip *pdev_to_uac2(struct platform_device *p) } static inline +struct f_uac2_opts *agdev_to_uac2_opts(struct audio_dev *agdev) +{ + return container_of(agdev->func.fi, struct f_uac2_opts, func_inst); +} + +static inline uint num_channels(uint chanmask) { uint num = 0; @@ -1168,7 +1174,7 @@ in_rq_cur(struct usb_function *fn, const struct usb_ctrlrequest *cr) int value = -EOPNOTSUPP; int p_srate, c_srate; - opts = container_of(agdev->func.fi, struct f_uac2_opts, func_inst); + opts = agdev_to_uac2_opts(agdev); p_srate = opts->p_srate; c_srate = opts->c_srate; @@ -1210,7 +1216,7 @@ in_rq_range(struct usb_function *fn, const struct usb_ctrlrequest *cr) int value = -EOPNOTSUPP; int p_srate, c_srate; - opts = container_of(agdev->func.fi, struct f_uac2_opts, func_inst); + opts = agdev_to_uac2_opts(agdev); p_srate = opts->p_srate; c_srate = opts->c_srate; -- cgit v1.1 From ec9e43138f1219966850477e056f6eb7fbcc4fa4 Mon Sep 17 00:00:00 2001 From: Daniel Mack Date: Wed, 27 Aug 2014 19:09:06 +0200 Subject: usb: gadget: f_uac2: handle partial dma area wrap With packet sizes other than 512, payloads in the packets may wrap around the ALSA dma buffer partially, which leads to memory corruption and audible clicks and pops in the audio stream at the moment, because there is no boundary check before the memcpy(). In preparation to an implementation for smaller and dynamically sized packets, we have to address such cases, and copy the payload in two steps conditionally. The 'src' and 'dst' approach doesn't work here anymore, as different behavior is necessary in playback and capture cases. Thus, this patch open-codes the routine now. Signed-off-by: Daniel Mack Signed-off-by: Felipe Balbi --- drivers/usb/gadget/function/f_uac2.c | 32 +++++++++++++++++++++++--------- 1 file changed, 23 insertions(+), 9 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/gadget/function/f_uac2.c b/drivers/usb/gadget/function/f_uac2.c index 9c8831d..246a778 100644 --- a/drivers/usb/gadget/function/f_uac2.c +++ b/drivers/usb/gadget/function/f_uac2.c @@ -163,8 +163,8 @@ agdev_iso_complete(struct usb_ep *ep, struct usb_request *req) { unsigned pending; unsigned long flags; + unsigned int hw_ptr; bool update_alsa = false; - unsigned char *src, *dst; int status = req->status; struct uac2_req *ur = req->context; struct snd_pcm_substream *substream; @@ -191,26 +191,40 @@ agdev_iso_complete(struct usb_ep *ep, struct usb_request *req) spin_lock_irqsave(&prm->lock, flags); - if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { - src = prm->dma_area + prm->hw_ptr; + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) req->actual = req->length; - dst = req->buf; - } else { - dst = prm->dma_area + prm->hw_ptr; - src = req->buf; - } pending = prm->hw_ptr % prm->period_size; pending += req->actual; if (pending >= prm->period_size) update_alsa = true; + hw_ptr = prm->hw_ptr; prm->hw_ptr = (prm->hw_ptr + req->actual) % prm->dma_bytes; spin_unlock_irqrestore(&prm->lock, flags); /* Pack USB load in ALSA ring buffer */ - memcpy(dst, src, req->actual); + pending = prm->dma_bytes - hw_ptr; + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + if (unlikely(pending < req->actual)) { + memcpy(req->buf, prm->dma_area + hw_ptr, pending); + memcpy(req->buf + pending, prm->dma_area, + req->actual - pending); + } else { + memcpy(req->buf, prm->dma_area + hw_ptr, req->actual); + } + } else { + if (unlikely(pending < req->actual)) { + memcpy(prm->dma_area + hw_ptr, req->buf, pending); + memcpy(prm->dma_area, req->buf + pending, + req->actual - pending); + } else { + memcpy(prm->dma_area + hw_ptr, req->buf, req->actual); + } + } + exit: if (usb_ep_queue(ep, req, GFP_ATOMIC)) dev_err(&uac2->pdev.dev, "%d Error!\n", __LINE__); -- cgit v1.1 From 9bb87f168931cf55738ed2fbda3639575cede886 Mon Sep 17 00:00:00 2001 From: Daniel Mack Date: Wed, 27 Aug 2014 19:09:07 +0200 Subject: usb: gadget: f_uac2: send reasonably sized packets The UAC2 function driver currently responds to all packets at all times with wMaxPacketSize packets. That results in way too fast audio playback as the function driver (which is in fact supposed to define the audio stream pace) delivers as fast as it can. Fix this by sizing each packet correctly with the following steps: a) Set the packet's size by dividing the nominal data rate by the playback endpoint's interval. b) If there is a residual value from the calculation in a), add it to a accumulator to keep track of it across packets. c) If the accumulator has gathered at least the number of bytes that are needed for one sample frame, increase the packet size. This way, the packet size calculation will get rid of any kind of imprecision that would otherwise occur with a simple division over time. Some of the variables that are needed while processing each packet are pre-computed for performance reasons. Signed-off-by: Daniel Mack Signed-off-by: Felipe Balbi --- drivers/usb/gadget/function/f_uac2.c | 69 +++++++++++++++++++++++++++++++++--- 1 file changed, 65 insertions(+), 4 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/gadget/function/f_uac2.c b/drivers/usb/gadget/function/f_uac2.c index 246a778..a5a27a5 100644 --- a/drivers/usb/gadget/function/f_uac2.c +++ b/drivers/usb/gadget/function/f_uac2.c @@ -92,6 +92,15 @@ struct snd_uac2_chip { struct snd_card *card; struct snd_pcm *pcm; + + /* timekeeping for the playback endpoint */ + unsigned int p_interval; + unsigned int p_residue; + + /* pre-calculated values for playback iso completion */ + unsigned int p_pktsize; + unsigned int p_pktsize_residue; + unsigned int p_framesize; }; #define BUFF_SIZE_MAX (PAGE_SIZE * 16) @@ -191,8 +200,29 @@ agdev_iso_complete(struct usb_ep *ep, struct usb_request *req) spin_lock_irqsave(&prm->lock, flags); - if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + /* + * For each IN packet, take the quotient of the current data + * rate and the endpoint's interval as the base packet size. + * If there is a residue from this division, add it to the + * residue accumulator. + */ + req->length = uac2->p_pktsize; + uac2->p_residue += uac2->p_pktsize_residue; + + /* + * Whenever there are more bytes in the accumulator than we + * need to add one more sample frame, increase this packet's + * size and decrease the accumulator. + */ + if (uac2->p_residue / uac2->p_interval >= uac2->p_framesize) { + req->length += uac2->p_framesize; + uac2->p_residue -= uac2->p_framesize * + uac2->p_interval; + } + req->actual = req->length; + } pending = prm->hw_ptr % prm->period_size; pending += req->actual; @@ -346,6 +376,7 @@ static int uac2_pcm_open(struct snd_pcm_substream *substream) c_srate = opts->c_srate; p_chmask = opts->p_chmask; c_chmask = opts->c_chmask; + uac2->p_residue = 0; runtime->hw = uac2_pcm_hardware; @@ -1077,7 +1108,7 @@ afunc_set_alt(struct usb_function *fn, unsigned intf, unsigned alt) struct usb_request *req; struct usb_ep *ep; struct uac2_rtd_params *prm; - int i; + int req_len, i; /* No i/f has more than 2 alt settings */ if (alt > 1) { @@ -1099,11 +1130,41 @@ afunc_set_alt(struct usb_function *fn, unsigned intf, unsigned alt) prm = &uac2->c_prm; config_ep_by_speed(gadget, fn, ep); agdev->as_out_alt = alt; + req_len = prm->max_psize; } else if (intf == agdev->as_in_intf) { + struct f_uac2_opts *opts = agdev_to_uac2_opts(agdev); + unsigned int factor, rate; + struct usb_endpoint_descriptor *ep_desc; + ep = agdev->in_ep; prm = &uac2->p_prm; config_ep_by_speed(gadget, fn, ep); agdev->as_in_alt = alt; + + /* pre-calculate the playback endpoint's interval */ + if (gadget->speed == USB_SPEED_FULL) { + ep_desc = &fs_epin_desc; + factor = 1000; + } else { + ep_desc = &hs_epin_desc; + factor = 125; + } + + /* pre-compute some values for iso_complete() */ + uac2->p_framesize = opts->p_ssize * + num_channels(opts->p_chmask); + rate = opts->p_srate * uac2->p_framesize; + uac2->p_interval = (1 << (ep_desc->bInterval - 1)) * factor; + uac2->p_pktsize = min_t(unsigned int, rate / uac2->p_interval, + prm->max_psize); + + if (uac2->p_pktsize < prm->max_psize) + uac2->p_pktsize_residue = rate % uac2->p_interval; + else + uac2->p_pktsize_residue = 0; + + req_len = uac2->p_pktsize; + uac2->p_residue = 0; } else { dev_err(dev, "%s:%d Error!\n", __func__, __LINE__); return -EINVAL; @@ -1128,9 +1189,9 @@ afunc_set_alt(struct usb_function *fn, unsigned intf, unsigned alt) req->zero = 0; req->context = &prm->ureq[i]; - req->length = prm->max_psize; + req->length = req_len; req->complete = agdev_iso_complete; - req->buf = prm->rbuf + i * req->length; + req->buf = prm->rbuf + i * prm->max_psize; } if (usb_ep_queue(ep, prm->ureq[i].req, GFP_ATOMIC)) -- cgit v1.1 From 80977dc99be5d874d10716594e716ef317c1723c Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Tue, 19 Aug 2014 16:37:22 -0500 Subject: usb: dwc3: move all string helper functions to debug.h Those functions are only using within debugging messages, grouping them into debug.h makes sense. While at that, also add missing multiple inclusion guard. Signed-off-by: Felipe Balbi --- drivers/usb/dwc3/debug.h | 164 +++++++++++++++++++++++++++++++++++++++++++++- drivers/usb/dwc3/ep0.c | 1 + drivers/usb/dwc3/gadget.c | 89 +------------------------ drivers/usb/dwc3/gadget.h | 56 ---------------- 4 files changed, 165 insertions(+), 145 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/dwc3/debug.h b/drivers/usb/dwc3/debug.h index fceb39d..e35a3d1 100644 --- a/drivers/usb/dwc3/debug.h +++ b/drivers/usb/dwc3/debug.h @@ -16,8 +16,170 @@ * GNU General Public License for more details. */ +#ifndef __DWC3_DEBUG_H +#define __DWC3_DEBUG_H + #include "core.h" +/** + * dwc3_gadget_ep_cmd_string - returns endpoint command string + * @cmd: command code + */ +static inline const char * +dwc3_gadget_ep_cmd_string(u8 cmd) +{ + switch (cmd) { + case DWC3_DEPCMD_DEPSTARTCFG: + return "Start New Configuration"; + case DWC3_DEPCMD_ENDTRANSFER: + return "End Transfer"; + case DWC3_DEPCMD_UPDATETRANSFER: + return "Update Transfer"; + case DWC3_DEPCMD_STARTTRANSFER: + return "Start Transfer"; + case DWC3_DEPCMD_CLEARSTALL: + return "Clear Stall"; + case DWC3_DEPCMD_SETSTALL: + return "Set Stall"; + case DWC3_DEPCMD_GETEPSTATE: + return "Get Endpoint State"; + case DWC3_DEPCMD_SETTRANSFRESOURCE: + return "Set Endpoint Transfer Resource"; + case DWC3_DEPCMD_SETEPCONFIG: + return "Set Endpoint Configuration"; + default: + return "UNKNOWN command"; + } +} + +/** + * dwc3_gadget_generic_cmd_string - returns generic command string + * @cmd: command code + */ +static inline const char * +dwc3_gadget_generic_cmd_string(u8 cmd) +{ + switch (cmd) { + case DWC3_DGCMD_SET_LMP: + return "Set LMP"; + case DWC3_DGCMD_SET_PERIODIC_PAR: + return "Set Periodic Parameters"; + case DWC3_DGCMD_XMIT_FUNCTION: + return "Transmit Function Wake Device Notification"; + case DWC3_DGCMD_SET_SCRATCHPAD_ADDR_LO: + return "Set Scratchpad Buffer Array Address Lo"; + case DWC3_DGCMD_SET_SCRATCHPAD_ADDR_HI: + return "Set Scratchpad Buffer Array Address Hi"; + case DWC3_DGCMD_SELECTED_FIFO_FLUSH: + return "Selected FIFO Flush"; + case DWC3_DGCMD_ALL_FIFO_FLUSH: + return "All FIFO Flush"; + case DWC3_DGCMD_SET_ENDPOINT_NRDY: + return "Set Endpoint NRDY"; + case DWC3_DGCMD_RUN_SOC_BUS_LOOPBACK: + return "Run SoC Bus Loopback Test"; + default: + return "UNKNOWN"; + } +} + +/** + * dwc3_gadget_link_string - returns link name + * @link_state: link state code + */ +static inline const char * +dwc3_gadget_link_string(enum dwc3_link_state link_state) +{ + switch (link_state) { + case DWC3_LINK_STATE_U0: + return "U0"; + case DWC3_LINK_STATE_U1: + return "U1"; + case DWC3_LINK_STATE_U2: + return "U2"; + case DWC3_LINK_STATE_U3: + return "U3"; + case DWC3_LINK_STATE_SS_DIS: + return "SS.Disabled"; + case DWC3_LINK_STATE_RX_DET: + return "RX.Detect"; + case DWC3_LINK_STATE_SS_INACT: + return "SS.Inactive"; + case DWC3_LINK_STATE_POLL: + return "Polling"; + case DWC3_LINK_STATE_RECOV: + return "Recovery"; + case DWC3_LINK_STATE_HRESET: + return "Hot Reset"; + case DWC3_LINK_STATE_CMPLY: + return "Compliance"; + case DWC3_LINK_STATE_LPBK: + return "Loopback"; + case DWC3_LINK_STATE_RESET: + return "Reset"; + case DWC3_LINK_STATE_RESUME: + return "Resume"; + default: + return "UNKNOWN link state\n"; + } +} + +/** + * dwc3_gadget_event_string - returns event name + * @event: the event code + */ +static inline const char *dwc3_gadget_event_string(u8 event) +{ + switch (event) { + case DWC3_DEVICE_EVENT_DISCONNECT: + return "Disconnect"; + case DWC3_DEVICE_EVENT_RESET: + return "Reset"; + case DWC3_DEVICE_EVENT_CONNECT_DONE: + return "Connection Done"; + case DWC3_DEVICE_EVENT_LINK_STATUS_CHANGE: + return "Link Status Change"; + case DWC3_DEVICE_EVENT_WAKEUP: + return "WakeUp"; + case DWC3_DEVICE_EVENT_EOPF: + return "End-Of-Frame"; + case DWC3_DEVICE_EVENT_SOF: + return "Start-Of-Frame"; + case DWC3_DEVICE_EVENT_ERRATIC_ERROR: + return "Erratic Error"; + case DWC3_DEVICE_EVENT_CMD_CMPL: + return "Command Complete"; + case DWC3_DEVICE_EVENT_OVERFLOW: + return "Overflow"; + } + + return "UNKNOWN"; +} + +/** + * dwc3_ep_event_string - returns event name + * @event: then event code + */ +static inline const char *dwc3_ep_event_string(u8 event) +{ + switch (event) { + case DWC3_DEPEVT_XFERCOMPLETE: + return "Transfer Complete"; + case DWC3_DEPEVT_XFERINPROGRESS: + return "Transfer In-Progress"; + case DWC3_DEPEVT_XFERNOTREADY: + return "Transfer Not Ready"; + case DWC3_DEPEVT_RXTXFIFOEVT: + return "FIFO"; + case DWC3_DEPEVT_STREAMEVT: + return "Stream"; + case DWC3_DEPEVT_EPCMDCMPLT: + return "Endpoint Command Complete"; + } + + return "UNKNOWN"; +} + #ifdef CONFIG_DEBUG_FS extern int dwc3_debugfs_init(struct dwc3 *); extern void dwc3_debugfs_exit(struct dwc3 *); @@ -27,4 +189,4 @@ static inline int dwc3_debugfs_init(struct dwc3 *d) static inline void dwc3_debugfs_exit(struct dwc3 *d) { } #endif - +#endif /* __DWC3_DEBUG_H */ diff --git a/drivers/usb/dwc3/ep0.c b/drivers/usb/dwc3/ep0.c index 21a3520..994e1d8 100644 --- a/drivers/usb/dwc3/ep0.c +++ b/drivers/usb/dwc3/ep0.c @@ -31,6 +31,7 @@ #include #include "core.h" +#include "debug.h" #include "gadget.h" #include "io.h" diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c index 307b513..77c49a6 100644 --- a/drivers/usb/dwc3/gadget.c +++ b/drivers/usb/dwc3/gadget.c @@ -30,6 +30,7 @@ #include #include +#include "debug.h" #include "core.h" #include "gadget.h" #include "io.h" @@ -272,94 +273,6 @@ void dwc3_gadget_giveback(struct dwc3_ep *dep, struct dwc3_request *req, spin_lock(&dwc->lock); } -static const char *dwc3_gadget_ep_cmd_string(u8 cmd) -{ - switch (cmd) { - case DWC3_DEPCMD_DEPSTARTCFG: - return "Start New Configuration"; - case DWC3_DEPCMD_ENDTRANSFER: - return "End Transfer"; - case DWC3_DEPCMD_UPDATETRANSFER: - return "Update Transfer"; - case DWC3_DEPCMD_STARTTRANSFER: - return "Start Transfer"; - case DWC3_DEPCMD_CLEARSTALL: - return "Clear Stall"; - case DWC3_DEPCMD_SETSTALL: - return "Set Stall"; - case DWC3_DEPCMD_GETEPSTATE: - return "Get Endpoint State"; - case DWC3_DEPCMD_SETTRANSFRESOURCE: - return "Set Endpoint Transfer Resource"; - case DWC3_DEPCMD_SETEPCONFIG: - return "Set Endpoint Configuration"; - default: - return "UNKNOWN command"; - } -} - -static const char *dwc3_gadget_generic_cmd_string(u8 cmd) -{ - switch (cmd) { - case DWC3_DGCMD_SET_LMP: - return "Set LMP"; - case DWC3_DGCMD_SET_PERIODIC_PAR: - return "Set Periodic Parameters"; - case DWC3_DGCMD_XMIT_FUNCTION: - return "Transmit Function Wake Device Notification"; - case DWC3_DGCMD_SET_SCRATCHPAD_ADDR_LO: - return "Set Scratchpad Buffer Array Address Lo"; - case DWC3_DGCMD_SET_SCRATCHPAD_ADDR_HI: - return "Set Scratchpad Buffer Array Address Hi"; - case DWC3_DGCMD_SELECTED_FIFO_FLUSH: - return "Selected FIFO Flush"; - case DWC3_DGCMD_ALL_FIFO_FLUSH: - return "All FIFO Flush"; - case DWC3_DGCMD_SET_ENDPOINT_NRDY: - return "Set Endpoint NRDY"; - case DWC3_DGCMD_RUN_SOC_BUS_LOOPBACK: - return "Run SoC Bus Loopback Test"; - default: - return "UNKNOWN"; - } -} - -static const char *dwc3_gadget_link_string(enum dwc3_link_state link_state) -{ - switch (link_state) { - case DWC3_LINK_STATE_U0: - return "U0"; - case DWC3_LINK_STATE_U1: - return "U1"; - case DWC3_LINK_STATE_U2: - return "U2"; - case DWC3_LINK_STATE_U3: - return "U3"; - case DWC3_LINK_STATE_SS_DIS: - return "SS.Disabled"; - case DWC3_LINK_STATE_RX_DET: - return "RX.Detect"; - case DWC3_LINK_STATE_SS_INACT: - return "SS.Inactive"; - case DWC3_LINK_STATE_POLL: - return "Polling"; - case DWC3_LINK_STATE_RECOV: - return "Recovery"; - case DWC3_LINK_STATE_HRESET: - return "Hot Reset"; - case DWC3_LINK_STATE_CMPLY: - return "Compliance"; - case DWC3_LINK_STATE_LPBK: - return "Loopback"; - case DWC3_LINK_STATE_RESET: - return "Reset"; - case DWC3_LINK_STATE_RESUME: - return "Resume"; - default: - return "UNKNOWN link state\n"; - } -} - int dwc3_send_gadget_generic_command(struct dwc3 *dwc, int cmd, u32 param) { u32 timeout = 500; diff --git a/drivers/usb/dwc3/gadget.h b/drivers/usb/dwc3/gadget.h index a0ee75b..178ad89 100644 --- a/drivers/usb/dwc3/gadget.h +++ b/drivers/usb/dwc3/gadget.h @@ -103,60 +103,4 @@ static inline u32 dwc3_gadget_ep_get_transfer_index(struct dwc3 *dwc, u8 number) return DWC3_DEPCMD_GET_RSC_IDX(res_id); } -/** - * dwc3_gadget_event_string - returns event name - * @event: the event code - */ -static inline const char *dwc3_gadget_event_string(u8 event) -{ - switch (event) { - case DWC3_DEVICE_EVENT_DISCONNECT: - return "Disconnect"; - case DWC3_DEVICE_EVENT_RESET: - return "Reset"; - case DWC3_DEVICE_EVENT_CONNECT_DONE: - return "Connection Done"; - case DWC3_DEVICE_EVENT_LINK_STATUS_CHANGE: - return "Link Status Change"; - case DWC3_DEVICE_EVENT_WAKEUP: - return "WakeUp"; - case DWC3_DEVICE_EVENT_EOPF: - return "End-Of-Frame"; - case DWC3_DEVICE_EVENT_SOF: - return "Start-Of-Frame"; - case DWC3_DEVICE_EVENT_ERRATIC_ERROR: - return "Erratic Error"; - case DWC3_DEVICE_EVENT_CMD_CMPL: - return "Command Complete"; - case DWC3_DEVICE_EVENT_OVERFLOW: - return "Overflow"; - } - - return "UNKNOWN"; -} - -/** - * dwc3_ep_event_string - returns event name - * @event: then event code - */ -static inline const char *dwc3_ep_event_string(u8 event) -{ - switch (event) { - case DWC3_DEPEVT_XFERCOMPLETE: - return "Transfer Complete"; - case DWC3_DEPEVT_XFERINPROGRESS: - return "Transfer In-Progress"; - case DWC3_DEPEVT_XFERNOTREADY: - return "Transfer Not Ready"; - case DWC3_DEPEVT_RXTXFIFOEVT: - return "FIFO"; - case DWC3_DEPEVT_STREAMEVT: - return "Stream"; - case DWC3_DEPEVT_EPCMDCMPLT: - return "Endpoint Command Complete"; - } - - return "UNKNOWN"; -} - #endif /* __DRIVERS_USB_DWC3_GADGET_H */ -- cgit v1.1 From e996061b9632d2ac7d00112208c644328c2411d5 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Tue, 19 Aug 2014 16:49:20 -0500 Subject: usb: dwc3: debug: add dwc3_gadget_event_type_string this new helper will return a pretty string for DWC3 Gadget Events. Signed-off-by: Felipe Balbi --- drivers/usb/dwc3/debug.h | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) (limited to 'drivers/usb') diff --git a/drivers/usb/dwc3/debug.h b/drivers/usb/dwc3/debug.h index e35a3d1..12ff4c9 100644 --- a/drivers/usb/dwc3/debug.h +++ b/drivers/usb/dwc3/debug.h @@ -180,6 +180,40 @@ static inline const char *dwc3_ep_event_string(u8 event) return "UNKNOWN"; } +/** + * dwc3_gadget_event_type_string - return event name + * @event: the event code + */ +static inline const char *dwc3_gadget_event_type_string(u8 event) +{ + switch (event) { + case DWC3_DEVICE_EVENT_DISCONNECT: + return "Disconnect"; + case DWC3_DEVICE_EVENT_RESET: + return "Reset"; + case DWC3_DEVICE_EVENT_CONNECT_DONE: + return "Connect Done"; + case DWC3_DEVICE_EVENT_LINK_STATUS_CHANGE: + return "Link Status Change"; + case DWC3_DEVICE_EVENT_WAKEUP: + return "Wake-Up"; + case DWC3_DEVICE_EVENT_HIBER_REQ: + return "Hibernation"; + case DWC3_DEVICE_EVENT_EOPF: + return "End of Periodic Frame"; + case DWC3_DEVICE_EVENT_SOF: + return "Start of Frame"; + case DWC3_DEVICE_EVENT_ERRATIC_ERROR: + return "Erratic Error"; + case DWC3_DEVICE_EVENT_CMD_CMPL: + return "Command Complete"; + case DWC3_DEVICE_EVENT_OVERFLOW: + return "Overflow"; + default: + return "UNKNOWN"; + } +} + #ifdef CONFIG_DEBUG_FS extern int dwc3_debugfs_init(struct dwc3 *); extern void dwc3_debugfs_exit(struct dwc3 *); -- cgit v1.1 From 3ece0ec474bf3cea9eefa7f92e3d4b6c3f9f71fd Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Fri, 5 Sep 2014 09:47:44 -0500 Subject: usb: dwc3: gadget: cmd argument should always be unsigned No functional changes, just making sure we're dealing with unsigned ints. Signed-off-by: Felipe Balbi --- drivers/usb/dwc3/core.h | 2 +- drivers/usb/dwc3/gadget.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/dwc3/core.h b/drivers/usb/dwc3/core.h index 48fb264..c123745 100644 --- a/drivers/usb/dwc3/core.h +++ b/drivers/usb/dwc3/core.h @@ -938,7 +938,7 @@ int dwc3_gadget_get_link_state(struct dwc3 *dwc); int dwc3_gadget_set_link_state(struct dwc3 *dwc, enum dwc3_link_state state); int dwc3_send_gadget_ep_cmd(struct dwc3 *dwc, unsigned ep, unsigned cmd, struct dwc3_gadget_ep_cmd_params *params); -int dwc3_send_gadget_generic_command(struct dwc3 *dwc, int cmd, u32 param); +int dwc3_send_gadget_generic_command(struct dwc3 *dwc, unsigned cmd, u32 param); #else static inline int dwc3_gadget_init(struct dwc3 *dwc) { return 0; } diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c index 77c49a6..096b638 100644 --- a/drivers/usb/dwc3/gadget.c +++ b/drivers/usb/dwc3/gadget.c @@ -273,7 +273,7 @@ void dwc3_gadget_giveback(struct dwc3_ep *dep, struct dwc3_request *req, spin_lock(&dwc->lock); } -int dwc3_send_gadget_generic_command(struct dwc3 *dwc, int cmd, u32 param) +int dwc3_send_gadget_generic_command(struct dwc3 *dwc, unsigned cmd, u32 param) { u32 timeout = 500; u32 reg; -- cgit v1.1 From 2c4cbe6e5a9c71408b496e00a78ea9284e98af16 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Wed, 30 Apr 2014 17:45:10 -0500 Subject: usb: dwc3: add tracepoints to aid debugging When we're debugging hard-to-reproduce and time-sensitive use cases, printk() poses too much overhead. That's when the kernel's tracing infrastructure comes into play. This patch implements a few initial tracepoints for the dwc3 driver. More traces can be added as necessary in order to ease the task of debugging dwc3. Reviewed-by: Paul Zimmerman Signed-off-by: Felipe Balbi --- drivers/usb/dwc3/Makefile | 5 +- drivers/usb/dwc3/core.h | 2 + drivers/usb/dwc3/debug.c | 32 +++++++ drivers/usb/dwc3/debug.h | 2 + drivers/usb/dwc3/ep0.c | 64 ++++++++------ drivers/usb/dwc3/gadget.c | 36 ++++---- drivers/usb/dwc3/io.h | 30 ++++++- drivers/usb/dwc3/trace.c | 19 ++++ drivers/usb/dwc3/trace.h | 220 ++++++++++++++++++++++++++++++++++++++++++++++ 9 files changed, 357 insertions(+), 53 deletions(-) create mode 100644 drivers/usb/dwc3/debug.c create mode 100644 drivers/usb/dwc3/trace.c create mode 100644 drivers/usb/dwc3/trace.h (limited to 'drivers/usb') diff --git a/drivers/usb/dwc3/Makefile b/drivers/usb/dwc3/Makefile index 10ac3e7..7793e6c 100644 --- a/drivers/usb/dwc3/Makefile +++ b/drivers/usb/dwc3/Makefile @@ -1,9 +1,12 @@ +# define_trace.h needs to know how to find our header +CFLAGS_trace.o := -I$(src) + ccflags-$(CONFIG_USB_DWC3_DEBUG) := -DDEBUG ccflags-$(CONFIG_USB_DWC3_VERBOSE) += -DVERBOSE_DEBUG obj-$(CONFIG_USB_DWC3) += dwc3.o -dwc3-y := core.o +dwc3-y := core.o debug.o trace.o ifneq ($(filter y,$(CONFIG_USB_DWC3_HOST) $(CONFIG_USB_DWC3_DUAL_ROLE)),) dwc3-y += host.o diff --git a/drivers/usb/dwc3/core.h b/drivers/usb/dwc3/core.h index c123745..66f6256 100644 --- a/drivers/usb/dwc3/core.h +++ b/drivers/usb/dwc3/core.h @@ -33,6 +33,8 @@ #include +#define DWC3_MSG_MAX 500 + /* Global constants */ #define DWC3_EP0_BOUNCE_SIZE 512 #define DWC3_ENDPOINTS_NUM 32 diff --git a/drivers/usb/dwc3/debug.c b/drivers/usb/dwc3/debug.c new file mode 100644 index 0000000..0be6885 --- /dev/null +++ b/drivers/usb/dwc3/debug.c @@ -0,0 +1,32 @@ +/** + * debug.c - DesignWare USB3 DRD Controller Debug/Trace Support + * + * Copyright (C) 2014 Texas Instruments Incorporated - http://www.ti.com + * + * Author: Felipe Balbi + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 of + * the License as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "debug.h" + +void dwc3_trace(void (*trace)(struct va_format *), const char *fmt, ...) +{ + struct va_format vaf; + va_list args; + + va_start(args, fmt); + vaf.fmt = fmt; + vaf.va = &args; + + trace(&vaf); + + va_end(args); +} diff --git a/drivers/usb/dwc3/debug.h b/drivers/usb/dwc3/debug.h index 12ff4c9..07fbc2d 100644 --- a/drivers/usb/dwc3/debug.h +++ b/drivers/usb/dwc3/debug.h @@ -214,6 +214,8 @@ static inline const char *dwc3_gadget_event_type_string(u8 event) } } +void dwc3_trace(void (*trace)(struct va_format *), const char *fmt, ...); + #ifdef CONFIG_DEBUG_FS extern int dwc3_debugfs_init(struct dwc3 *); extern void dwc3_debugfs_exit(struct dwc3 *); diff --git a/drivers/usb/dwc3/ep0.c b/drivers/usb/dwc3/ep0.c index 994e1d8..b359387 100644 --- a/drivers/usb/dwc3/ep0.c +++ b/drivers/usb/dwc3/ep0.c @@ -66,7 +66,7 @@ static int dwc3_ep0_start_trans(struct dwc3 *dwc, u8 epnum, dma_addr_t buf_dma, dep = dwc->eps[epnum]; if (dep->flags & DWC3_EP_BUSY) { - dev_vdbg(dwc->dev, "%s: still busy\n", dep->name); + dwc3_trace(trace_dwc3_ep0, "%s still busy", dep->name); return 0; } @@ -89,7 +89,8 @@ static int dwc3_ep0_start_trans(struct dwc3 *dwc, u8 epnum, dma_addr_t buf_dma, ret = dwc3_send_gadget_ep_cmd(dwc, dep->number, DWC3_DEPCMD_STARTTRANSFER, ¶ms); if (ret < 0) { - dev_dbg(dwc->dev, "failed to send STARTTRANSFER command\n"); + dwc3_trace(trace_dwc3_ep0, "%s STARTTRANSFER failed", + dep->name); return ret; } @@ -154,7 +155,8 @@ static int __dwc3_gadget_ep0_queue(struct dwc3_ep *dep, if (dwc->ep0state == EP0_STATUS_PHASE) __dwc3_ep0_do_control_status(dwc, dwc->eps[direction]); else - dev_dbg(dwc->dev, "too early for delayed status\n"); + dwc3_trace(trace_dwc3_ep0, + "too early for delayed status"); return 0; } @@ -218,7 +220,8 @@ int dwc3_gadget_ep0_queue(struct usb_ep *ep, struct usb_request *request, spin_lock_irqsave(&dwc->lock, flags); if (!dep->endpoint.desc) { - dev_dbg(dwc->dev, "trying to queue request %p to disabled %s\n", + dwc3_trace(trace_dwc3_ep0, + "trying to queue request %p to disabled %s", request, dep->name); ret = -ESHUTDOWN; goto out; @@ -230,7 +233,8 @@ int dwc3_gadget_ep0_queue(struct usb_ep *ep, struct usb_request *request, goto out; } - dev_vdbg(dwc->dev, "queueing request %p to %s length %d, state '%s'\n", + dwc3_trace(trace_dwc3_ep0, + "queueing request %p to %s length %d state '%s'", request, dep->name, request->length, dwc3_ep0_state_string(dwc->ep0state)); @@ -486,12 +490,13 @@ static int dwc3_ep0_set_address(struct dwc3 *dwc, struct usb_ctrlrequest *ctrl) addr = le16_to_cpu(ctrl->wValue); if (addr > 127) { - dev_dbg(dwc->dev, "invalid device address %d\n", addr); + dwc3_trace(trace_dwc3_ep0, "invalid device address %d", addr); return -EINVAL; } if (state == USB_STATE_CONFIGURED) { - dev_dbg(dwc->dev, "trying to set address when configured\n"); + dwc3_trace(trace_dwc3_ep0, + "trying to set address when configured"); return -EINVAL; } @@ -557,7 +562,7 @@ static int dwc3_ep0_set_config(struct dwc3 *dwc, struct usb_ctrlrequest *ctrl) dwc3_writel(dwc->regs, DWC3_DCTL, reg); dwc->resize_fifos = true; - dev_dbg(dwc->dev, "resize fifos flag SET\n"); + dwc3_trace(trace_dwc3_ep0, "resize FIFOs flag SET"); } break; @@ -681,35 +686,35 @@ static int dwc3_ep0_std_request(struct dwc3 *dwc, struct usb_ctrlrequest *ctrl) switch (ctrl->bRequest) { case USB_REQ_GET_STATUS: - dev_vdbg(dwc->dev, "USB_REQ_GET_STATUS\n"); + dwc3_trace(trace_dwc3_ep0, "USB_REQ_GET_STATUS\n"); ret = dwc3_ep0_handle_status(dwc, ctrl); break; case USB_REQ_CLEAR_FEATURE: - dev_vdbg(dwc->dev, "USB_REQ_CLEAR_FEATURE\n"); + dwc3_trace(trace_dwc3_ep0, "USB_REQ_CLEAR_FEATURE\n"); ret = dwc3_ep0_handle_feature(dwc, ctrl, 0); break; case USB_REQ_SET_FEATURE: - dev_vdbg(dwc->dev, "USB_REQ_SET_FEATURE\n"); + dwc3_trace(trace_dwc3_ep0, "USB_REQ_SET_FEATURE\n"); ret = dwc3_ep0_handle_feature(dwc, ctrl, 1); break; case USB_REQ_SET_ADDRESS: - dev_vdbg(dwc->dev, "USB_REQ_SET_ADDRESS\n"); + dwc3_trace(trace_dwc3_ep0, "USB_REQ_SET_ADDRESS\n"); ret = dwc3_ep0_set_address(dwc, ctrl); break; case USB_REQ_SET_CONFIGURATION: - dev_vdbg(dwc->dev, "USB_REQ_SET_CONFIGURATION\n"); + dwc3_trace(trace_dwc3_ep0, "USB_REQ_SET_CONFIGURATION\n"); ret = dwc3_ep0_set_config(dwc, ctrl); break; case USB_REQ_SET_SEL: - dev_vdbg(dwc->dev, "USB_REQ_SET_SEL\n"); + dwc3_trace(trace_dwc3_ep0, "USB_REQ_SET_SEL\n"); ret = dwc3_ep0_set_sel(dwc, ctrl); break; case USB_REQ_SET_ISOCH_DELAY: - dev_vdbg(dwc->dev, "USB_REQ_SET_ISOCH_DELAY\n"); + dwc3_trace(trace_dwc3_ep0, "USB_REQ_SET_ISOCH_DELAY\n"); ret = dwc3_ep0_set_isoch_delay(dwc, ctrl); break; default: - dev_vdbg(dwc->dev, "Forwarding to gadget driver\n"); + dwc3_trace(trace_dwc3_ep0, "Forwarding to gadget driver\n"); ret = dwc3_ep0_delegate_req(dwc, ctrl); break; } @@ -727,6 +732,8 @@ static void dwc3_ep0_inspect_setup(struct dwc3 *dwc, if (!dwc->gadget_driver) goto out; + trace_dwc3_ctrl_req(ctrl); + len = le16_to_cpu(ctrl->wLength); if (!len) { dwc->three_stage_setup = false; @@ -775,7 +782,7 @@ static void dwc3_ep0_complete_data(struct dwc3 *dwc, status = DWC3_TRB_SIZE_TRBSTS(trb->size); if (status == DWC3_TRBSTS_SETUP_PENDING) { - dev_dbg(dwc->dev, "Setup Pending received\n"); + dwc3_trace(trace_dwc3_ep0, "Setup Pending received"); if (r) dwc3_gadget_giveback(ep0, r, -ECONNRESET); @@ -835,7 +842,7 @@ static void dwc3_ep0_complete_status(struct dwc3 *dwc, ret = dwc3_gadget_set_test_mode(dwc, dwc->test_mode_nr); if (ret < 0) { - dev_dbg(dwc->dev, "Invalid Test #%d\n", + dwc3_trace(trace_dwc3_ep0, "Invalid Test #%d", dwc->test_mode_nr); dwc3_ep0_stall_and_restart(dwc); return; @@ -844,7 +851,7 @@ static void dwc3_ep0_complete_status(struct dwc3 *dwc, status = DWC3_TRB_SIZE_TRBSTS(trb->size); if (status == DWC3_TRBSTS_SETUP_PENDING) - dev_dbg(dwc->dev, "Setup Pending received\n"); + dwc3_trace(trace_dwc3_ep0, "Setup Pending received\n"); dwc->ep0state = EP0_SETUP_PHASE; dwc3_ep0_out_start(dwc); @@ -861,17 +868,17 @@ static void dwc3_ep0_xfer_complete(struct dwc3 *dwc, switch (dwc->ep0state) { case EP0_SETUP_PHASE: - dev_vdbg(dwc->dev, "Inspecting Setup Bytes\n"); + dwc3_trace(trace_dwc3_ep0, "Setup Phase"); dwc3_ep0_inspect_setup(dwc, event); break; case EP0_DATA_PHASE: - dev_vdbg(dwc->dev, "Data Phase\n"); + dwc3_trace(trace_dwc3_ep0, "Data Phase"); dwc3_ep0_complete_data(dwc, event); break; case EP0_STATUS_PHASE: - dev_vdbg(dwc->dev, "Status Phase\n"); + dwc3_trace(trace_dwc3_ep0, "Status Phase"); dwc3_ep0_complete_status(dwc, event); break; default: @@ -947,7 +954,7 @@ static int dwc3_ep0_start_control_status(struct dwc3_ep *dep) static void __dwc3_ep0_do_control_status(struct dwc3 *dwc, struct dwc3_ep *dep) { if (dwc->resize_fifos) { - dev_dbg(dwc->dev, "starting to resize fifos\n"); + dwc3_trace(trace_dwc3_ep0, "Resizing FIFOs"); dwc3_gadget_resize_tx_fifos(dwc); dwc->resize_fifos = 0; } @@ -988,7 +995,7 @@ static void dwc3_ep0_xfernotready(struct dwc3 *dwc, switch (event->status) { case DEPEVT_STATUS_CONTROL_DATA: - dev_vdbg(dwc->dev, "Control Data\n"); + dwc3_trace(trace_dwc3_ep0, "Control Data"); /* * We already have a DATA transfer in the controller's cache, @@ -1002,7 +1009,8 @@ static void dwc3_ep0_xfernotready(struct dwc3 *dwc, if (dwc->ep0_expect_in != event->endpoint_number) { struct dwc3_ep *dep = dwc->eps[dwc->ep0_expect_in]; - dev_vdbg(dwc->dev, "Wrong direction for Data phase\n"); + dwc3_trace(trace_dwc3_ep0, + "Wrong direction for Data phase"); dwc3_ep0_end_control_data(dwc, dep); dwc3_ep0_stall_and_restart(dwc); return; @@ -1014,13 +1022,13 @@ static void dwc3_ep0_xfernotready(struct dwc3 *dwc, if (dwc->ep0_next_event != DWC3_EP0_NRDY_STATUS) return; - dev_vdbg(dwc->dev, "Control Status\n"); + dwc3_trace(trace_dwc3_ep0, "Control Status"); dwc->ep0state = EP0_STATUS_PHASE; if (dwc->delayed_status) { WARN_ON_ONCE(event->endpoint_number != 1); - dev_vdbg(dwc->dev, "Mass Storage delayed status\n"); + dwc3_trace(trace_dwc3_ep0, "Delayed Status"); return; } @@ -1033,7 +1041,7 @@ void dwc3_ep0_interrupt(struct dwc3 *dwc, { u8 epnum = event->endpoint_number; - dev_dbg(dwc->dev, "%s while ep%d%s in state '%s'\n", + dwc3_trace(trace_dwc3_ep0, "%s while ep%d%s in state '%s'", dwc3_ep_event_string(event->endpoint_event), epnum >> 1, (epnum & 1) ? "in" : "out", dwc3_ep0_state_string(dwc->ep0state)); diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c index 096b638..f2dbaca 100644 --- a/drivers/usb/dwc3/gadget.c +++ b/drivers/usb/dwc3/gadget.c @@ -267,6 +267,7 @@ void dwc3_gadget_giveback(struct dwc3_ep *dep, struct dwc3_request *req, dev_dbg(dwc->dev, "request %p from %s completed %d/%d ===> %d\n", req, dep->name, req->request.actual, req->request.length, status); + trace_dwc3_gadget_giveback(req); spin_unlock(&dwc->lock); req->request.complete(&dep->endpoint, &req->request); @@ -278,8 +279,7 @@ int dwc3_send_gadget_generic_command(struct dwc3 *dwc, unsigned cmd, u32 param) u32 timeout = 500; u32 reg; - dev_vdbg(dwc->dev, "generic cmd '%s' [%d] param %08x\n", - dwc3_gadget_generic_cmd_string(cmd), cmd, param); + trace_dwc3_gadget_generic_cmd(cmd, param); dwc3_writel(dwc->regs, DWC3_DGCMDPAR, param); dwc3_writel(dwc->regs, DWC3_DGCMD, cmd | DWC3_DGCMD_CMDACT); @@ -310,10 +310,7 @@ int dwc3_send_gadget_ep_cmd(struct dwc3 *dwc, unsigned ep, u32 timeout = 500; u32 reg; - dev_vdbg(dwc->dev, "%s: cmd '%s' [%d] params %08x %08x %08x\n", - dep->name, - dwc3_gadget_ep_cmd_string(cmd), cmd, params->param0, - params->param1, params->param2); + trace_dwc3_gadget_ep_cmd(dep, cmd, params); dwc3_writel(dwc->regs, DWC3_DEPCMDPAR0(ep), params->param0); dwc3_writel(dwc->regs, DWC3_DEPCMDPAR1(ep), params->param1); @@ -710,6 +707,8 @@ static struct usb_request *dwc3_gadget_ep_alloc_request(struct usb_ep *ep, req->epnum = dep->number; req->dep = dep; + trace_dwc3_alloc_request(req); + return &req->request; } @@ -718,6 +717,7 @@ static void dwc3_gadget_ep_free_request(struct usb_ep *ep, { struct dwc3_request *req = to_dwc3_request(request); + trace_dwc3_free_request(req); kfree(req); } @@ -799,6 +799,8 @@ static void dwc3_prepare_one_trb(struct dwc3_ep *dep, trb->ctrl |= DWC3_TRB_CTRL_SID_SOFN(req->request.stream_id); trb->ctrl |= DWC3_TRB_CTRL_HWO; + + trace_dwc3_prepare_trb(dep, trb); } /* @@ -1143,6 +1145,7 @@ static int dwc3_gadget_ep_queue(struct usb_ep *ep, struct usb_request *request, dev_vdbg(dwc->dev, "queing request %p to %s length %d\n", request, ep->name, request->length); + trace_dwc3_ep_queue(req); spin_lock_irqsave(&dwc->lock, flags); ret = __dwc3_gadget_ep_queue(dep, req); @@ -1163,6 +1166,8 @@ static int dwc3_gadget_ep_dequeue(struct usb_ep *ep, unsigned long flags; int ret = 0; + trace_dwc3_ep_dequeue(req); + spin_lock_irqsave(&dwc->lock, flags); list_for_each_entry(r, &dep->request_list, list) { @@ -1753,6 +1758,8 @@ static int __dwc3_cleanup_done_trbs(struct dwc3 *dwc, struct dwc3_ep *dep, unsigned int s_pkt = 0; unsigned int trb_status; + trace_dwc3_complete_trb(dep, trb); + if ((trb->ctrl & DWC3_TRB_CTRL_HWO) && status != -ESHUTDOWN) /* * We continue despite the error. There is not much we @@ -1927,9 +1934,6 @@ static void dwc3_endpoint_interrupt(struct dwc3 *dwc, if (!(dep->flags & DWC3_EP_ENABLED)) return; - dev_vdbg(dwc->dev, "%s: %s\n", dep->name, - dwc3_ep_event_string(event->endpoint_event)); - if (epnum == 0 || epnum == 1) { dwc3_ep0_interrupt(dwc, event); return; @@ -2122,8 +2126,6 @@ static void dwc3_gadget_disconnect_interrupt(struct dwc3 *dwc) { int reg; - dev_vdbg(dwc->dev, "%s\n", __func__); - reg = dwc3_readl(dwc->regs, DWC3_DCTL); reg &= ~DWC3_DCTL_INITU1ENA; dwc3_writel(dwc->regs, DWC3_DCTL, reg); @@ -2142,8 +2144,6 @@ static void dwc3_gadget_reset_interrupt(struct dwc3 *dwc) { u32 reg; - dev_vdbg(dwc->dev, "%s\n", __func__); - /* * WORKAROUND: DWC3 revisions <1.88a have an issue which * would cause a missing Disconnect Event if there's a @@ -2228,8 +2228,6 @@ static void dwc3_gadget_conndone_interrupt(struct dwc3 *dwc) u32 reg; u8 speed; - dev_vdbg(dwc->dev, "%s\n", __func__); - reg = dwc3_readl(dwc->regs, DWC3_DSTS); speed = reg & DWC3_DSTS_CONNECTSPD; dwc->speed = speed; @@ -2327,8 +2325,6 @@ static void dwc3_gadget_conndone_interrupt(struct dwc3 *dwc) static void dwc3_gadget_wakeup_interrupt(struct dwc3 *dwc) { - dev_vdbg(dwc->dev, "%s\n", __func__); - /* * TODO take core out of low power mode when that's * implemented. @@ -2433,10 +2429,6 @@ static void dwc3_gadget_linksts_change_interrupt(struct dwc3 *dwc, break; } - dev_vdbg(dwc->dev, "link change: %s [%d] -> %s [%d]\n", - dwc3_gadget_link_string(dwc->link_state), - dwc->link_state, dwc3_gadget_link_string(next), next); - dwc->link_state = next; } @@ -2513,6 +2505,8 @@ static void dwc3_gadget_interrupt(struct dwc3 *dwc, static void dwc3_process_event_entry(struct dwc3 *dwc, const union dwc3_event *event) { + trace_dwc3_event(event->raw); + /* Endpoint IRQ, handle it and return early */ if (event->type.is_devspec == 0) { /* depevt */ diff --git a/drivers/usb/dwc3/io.h b/drivers/usb/dwc3/io.h index d94441c..6a79c8e6 100644 --- a/drivers/usb/dwc3/io.h +++ b/drivers/usb/dwc3/io.h @@ -20,27 +20,51 @@ #define __DRIVERS_USB_DWC3_IO_H #include - +#include "trace.h" +#include "debug.h" #include "core.h" static inline u32 dwc3_readl(void __iomem *base, u32 offset) { + u32 offs = offset - DWC3_GLOBALS_REGS_START; + u32 value; + /* * We requested the mem region starting from the Globals address * space, see dwc3_probe in core.c. * However, the offsets are given starting from xHCI address space. */ - return readl(base + (offset - DWC3_GLOBALS_REGS_START)); + value = readl(base + offs); + + /* + * When tracing we want to make it easy to find the correct address on + * documentation, so we revert it back to the proper addresses, the + * same way they are described on SNPS documentation + */ + dwc3_trace(trace_dwc3_readl, "addr %p value %08x", + base - DWC3_GLOBALS_REGS_START + offset, value); + + return value; } static inline void dwc3_writel(void __iomem *base, u32 offset, u32 value) { + u32 offs = offset - DWC3_GLOBALS_REGS_START; + /* * We requested the mem region starting from the Globals address * space, see dwc3_probe in core.c. * However, the offsets are given starting from xHCI address space. */ - writel(value, base + (offset - DWC3_GLOBALS_REGS_START)); + writel(value, base + offs); + + /* + * When tracing we want to make it easy to find the correct address on + * documentation, so we revert it back to the proper addresses, the + * same way they are described on SNPS documentation + */ + dwc3_trace(trace_dwc3_writel, "addr %p value %08x", + base - DWC3_GLOBALS_REGS_START + offset, value); } #endif /* __DRIVERS_USB_DWC3_IO_H */ diff --git a/drivers/usb/dwc3/trace.c b/drivers/usb/dwc3/trace.c new file mode 100644 index 0000000..6cd1664 --- /dev/null +++ b/drivers/usb/dwc3/trace.c @@ -0,0 +1,19 @@ +/** + * trace.c - DesignWare USB3 DRD Controller Trace Support + * + * Copyright (C) 2014 Texas Instruments Incorporated - http://www.ti.com + * + * Author: Felipe Balbi + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 of + * the License as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#define CREATE_TRACE_POINTS +#include "trace.h" diff --git a/drivers/usb/dwc3/trace.h b/drivers/usb/dwc3/trace.h new file mode 100644 index 0000000..78aff1d --- /dev/null +++ b/drivers/usb/dwc3/trace.h @@ -0,0 +1,220 @@ +/** + * trace.h - DesignWare USB3 DRD Controller Trace Support + * + * Copyright (C) 2014 Texas Instruments Incorporated - http://www.ti.com + * + * Author: Felipe Balbi + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 of + * the License as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#undef TRACE_SYSTEM +#define TRACE_SYSTEM dwc3 + +#if !defined(__DWC3_TRACE_H) || defined(TRACE_HEADER_MULTI_READ) +#define __DWC3_TRACE_H + +#include +#include +#include +#include "core.h" +#include "debug.h" + +DECLARE_EVENT_CLASS(dwc3_log_msg, + TP_PROTO(struct va_format *vaf), + TP_ARGS(vaf), + TP_STRUCT__entry(__dynamic_array(char, msg, DWC3_MSG_MAX)), + TP_fast_assign( + vsnprintf(__get_str(msg), DWC3_MSG_MAX, vaf->fmt, *vaf->va); + ), + TP_printk("%s", __get_str(msg)) +); + +DEFINE_EVENT(dwc3_log_msg, dwc3_readl, + TP_PROTO(struct va_format *vaf), + TP_ARGS(vaf) +); + +DEFINE_EVENT(dwc3_log_msg, dwc3_writel, + TP_PROTO(struct va_format *vaf), + TP_ARGS(vaf) +); + +DEFINE_EVENT(dwc3_log_msg, dwc3_ep0, + TP_PROTO(struct va_format *vaf), + TP_ARGS(vaf) +); + +DECLARE_EVENT_CLASS(dwc3_log_event, + TP_PROTO(u32 event), + TP_ARGS(event), + TP_STRUCT__entry( + __field(u32, event) + ), + TP_fast_assign( + __entry->event = event; + ), + TP_printk("event %08x\n", __entry->event) +); + +DEFINE_EVENT(dwc3_log_event, dwc3_event, + TP_PROTO(u32 event), + TP_ARGS(event) +); + +DECLARE_EVENT_CLASS(dwc3_log_ctrl, + TP_PROTO(struct usb_ctrlrequest *ctrl), + TP_ARGS(ctrl), + TP_STRUCT__entry( + __field(struct usb_ctrlrequest *, ctrl) + ), + TP_fast_assign( + __entry->ctrl = ctrl; + ), + TP_printk("bRequestType %02x bRequest %02x wValue %04x wIndex %04x wLength %d", + __entry->ctrl->bRequestType, __entry->ctrl->bRequest, + le16_to_cpu(__entry->ctrl->wValue), le16_to_cpu(__entry->ctrl->wIndex), + le16_to_cpu(__entry->ctrl->wLength) + ) +); + +DEFINE_EVENT(dwc3_log_ctrl, dwc3_ctrl_req, + TP_PROTO(struct usb_ctrlrequest *ctrl), + TP_ARGS(ctrl) +); + +DECLARE_EVENT_CLASS(dwc3_log_request, + TP_PROTO(struct dwc3_request *req), + TP_ARGS(req), + TP_STRUCT__entry( + __field(struct dwc3_request *, req) + ), + TP_fast_assign( + __entry->req = req; + ), + TP_printk("%s: req %p length %u/%u ==> %d", + __entry->req->dep->name, __entry->req, + __entry->req->request.actual, __entry->req->request.length, + __entry->req->request.status + ) +); + +DEFINE_EVENT(dwc3_log_request, dwc3_alloc_request, + TP_PROTO(struct dwc3_request *req), + TP_ARGS(req) +); + +DEFINE_EVENT(dwc3_log_request, dwc3_free_request, + TP_PROTO(struct dwc3_request *req), + TP_ARGS(req) +); + +DEFINE_EVENT(dwc3_log_request, dwc3_ep_queue, + TP_PROTO(struct dwc3_request *req), + TP_ARGS(req) +); + +DEFINE_EVENT(dwc3_log_request, dwc3_ep_dequeue, + TP_PROTO(struct dwc3_request *req), + TP_ARGS(req) +); + +DEFINE_EVENT(dwc3_log_request, dwc3_gadget_giveback, + TP_PROTO(struct dwc3_request *req), + TP_ARGS(req) +); + +DECLARE_EVENT_CLASS(dwc3_log_generic_cmd, + TP_PROTO(unsigned int cmd, u32 param), + TP_ARGS(cmd, param), + TP_STRUCT__entry( + __field(unsigned int, cmd) + __field(u32, param) + ), + TP_fast_assign( + __entry->cmd = cmd; + __entry->param = param; + ), + TP_printk("cmd '%s' [%d] param %08x\n", + dwc3_gadget_generic_cmd_string(__entry->cmd), + __entry->cmd, __entry->param + ) +); + +DEFINE_EVENT(dwc3_log_generic_cmd, dwc3_gadget_generic_cmd, + TP_PROTO(unsigned int cmd, u32 param), + TP_ARGS(cmd, param) +); + +DECLARE_EVENT_CLASS(dwc3_log_gadget_ep_cmd, + TP_PROTO(struct dwc3_ep *dep, unsigned int cmd, + struct dwc3_gadget_ep_cmd_params *params), + TP_ARGS(dep, cmd, params), + TP_STRUCT__entry( + __field(struct dwc3_ep *, dep) + __field(unsigned int, cmd) + __field(struct dwc3_gadget_ep_cmd_params *, params) + ), + TP_fast_assign( + __entry->dep = dep; + __entry->cmd = cmd; + __entry->params = params; + ), + TP_printk("%s: cmd '%s' [%d] params %08x %08x %08x\n", + __entry->dep->name, dwc3_gadget_ep_cmd_string(__entry->cmd), + __entry->cmd, __entry->params->param0, + __entry->params->param1, __entry->params->param2 + ) +); + +DEFINE_EVENT(dwc3_log_gadget_ep_cmd, dwc3_gadget_ep_cmd, + TP_PROTO(struct dwc3_ep *dep, unsigned int cmd, + struct dwc3_gadget_ep_cmd_params *params), + TP_ARGS(dep, cmd, params) +); + +DECLARE_EVENT_CLASS(dwc3_log_trb, + TP_PROTO(struct dwc3_ep *dep, struct dwc3_trb *trb), + TP_ARGS(dep, trb), + TP_STRUCT__entry( + __field(struct dwc3_ep *, dep) + __field(struct dwc3_trb *, trb) + ), + TP_fast_assign( + __entry->dep = dep; + __entry->trb = trb; + ), + TP_printk("%s: trb %p bph %08x bpl %08x size %08x ctrl %08x\n", + __entry->dep->name, __entry->trb, __entry->trb->bph, + __entry->trb->bpl, __entry->trb->size, __entry->trb->ctrl + ) +); + +DEFINE_EVENT(dwc3_log_trb, dwc3_prepare_trb, + TP_PROTO(struct dwc3_ep *dep, struct dwc3_trb *trb), + TP_ARGS(dep, trb) +); + +DEFINE_EVENT(dwc3_log_trb, dwc3_complete_trb, + TP_PROTO(struct dwc3_ep *dep, struct dwc3_trb *trb), + TP_ARGS(dep, trb) +); + +#endif /* __DWC3_TRACE_H */ + +/* this part has to be here */ + +#undef TRACE_INCLUDE_PATH +#define TRACE_INCLUDE_PATH . + +#undef TRACE_INCLUDE_FILE +#define TRACE_INCLUDE_FILE trace + +#include -- cgit v1.1 From f83fca0707c66e36f14efef7f68702cb12de70b7 Mon Sep 17 00:00:00 2001 From: Peter Griffin Date: Fri, 5 Sep 2014 16:36:30 +0100 Subject: usb: dwc3: add ST dwc3 glue layer to manage dwc3 HC This patch adds the ST glue logic to manage the DWC3 HC on STiH407 SoC family. It manages the powerdown signal, and configures the internal glue logic and syscfg registers. [ balbi@ti.com : actually switch over to of_platform_depopulate() ] Signed-off-by: Giuseppe Cavallaro Signed-off-by: Peter Griffin Acked-by: Lee Jones Signed-off-by: Felipe Balbi --- drivers/usb/dwc3/Kconfig | 9 ++ drivers/usb/dwc3/Makefile | 1 + drivers/usb/dwc3/dwc3-st.c | 367 +++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 377 insertions(+) create mode 100644 drivers/usb/dwc3/dwc3-st.c (limited to 'drivers/usb') diff --git a/drivers/usb/dwc3/Kconfig b/drivers/usb/dwc3/Kconfig index 785510a..5238251 100644 --- a/drivers/usb/dwc3/Kconfig +++ b/drivers/usb/dwc3/Kconfig @@ -80,6 +80,15 @@ config USB_DWC3_KEYSTONE Support of USB2/3 functionality in TI Keystone2 platforms. Say 'Y' or 'M' here if you have one such device +config USB_DWC3_ST + tristate "STMicroelectronics Platforms" + depends on ARCH_STI && OF + default USB_DWC3 + help + STMicroelectronics SoCs with one DesignWare Core USB3 IP + inside (i.e. STiH407). + Say 'Y' or 'M' if you have one such device. + comment "Debugging features" config USB_DWC3_DEBUG diff --git a/drivers/usb/dwc3/Makefile b/drivers/usb/dwc3/Makefile index 7793e6c..3e88c77 100644 --- a/drivers/usb/dwc3/Makefile +++ b/drivers/usb/dwc3/Makefile @@ -36,3 +36,4 @@ obj-$(CONFIG_USB_DWC3_OMAP) += dwc3-omap.o obj-$(CONFIG_USB_DWC3_EXYNOS) += dwc3-exynos.o obj-$(CONFIG_USB_DWC3_PCI) += dwc3-pci.o obj-$(CONFIG_USB_DWC3_KEYSTONE) += dwc3-keystone.o +obj-$(CONFIG_USB_DWC3_ST) += dwc3-st.o diff --git a/drivers/usb/dwc3/dwc3-st.c b/drivers/usb/dwc3/dwc3-st.c new file mode 100644 index 0000000..c7602b5 --- /dev/null +++ b/drivers/usb/dwc3/dwc3-st.c @@ -0,0 +1,367 @@ +/** + * dwc3-st.c Support for dwc3 platform devices on ST Microelectronics platforms + * + * This is a small driver for the dwc3 to provide the glue logic + * to configure the controller. Tested on STi platforms. + * + * Copyright (C) 2014 Stmicroelectronics + * + * Author: Giuseppe Cavallaro + * Contributors: Aymen Bouattay + * Peter Griffin + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * Inspired by dwc3-omap.c and dwc3-exynos.c. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "core.h" +#include "io.h" + +/* glue registers */ +#define CLKRST_CTRL 0x00 +#define AUX_CLK_EN BIT(0) +#define SW_PIPEW_RESET_N BIT(4) +#define EXT_CFG_RESET_N BIT(8) +/* + * 1'b0 : The host controller complies with the xHCI revision 0.96 + * 1'b1 : The host controller complies with the xHCI revision 1.0 + */ +#define XHCI_REVISION BIT(12) + +#define USB2_VBUS_MNGMNT_SEL1 0x2C +/* + * For all fields in USB2_VBUS_MNGMNT_SEL1 + * 2’b00 : Override value from Reg 0x30 is selected + * 2’b01 : utmiotg_ from usb3_top is selected + * 2’b10 : pipew_ from PIPEW instance is selected + * 2’b11 : value is 1'b0 + */ +#define USB2_VBUS_REG30 0x0 +#define USB2_VBUS_UTMIOTG 0x1 +#define USB2_VBUS_PIPEW 0x2 +#define USB2_VBUS_ZERO 0x3 + +#define SEL_OVERRIDE_VBUSVALID(n) (n << 0) +#define SEL_OVERRIDE_POWERPRESENT(n) (n << 4) +#define SEL_OVERRIDE_BVALID(n) (n << 8) + +/* Static DRD configuration */ +#define USB3_CONTROL_MASK 0xf77 + +#define USB3_DEVICE_NOT_HOST BIT(0) +#define USB3_FORCE_VBUSVALID BIT(1) +#define USB3_DELAY_VBUSVALID BIT(2) +#define USB3_SEL_FORCE_OPMODE BIT(4) +#define USB3_FORCE_OPMODE(n) (n << 5) +#define USB3_SEL_FORCE_DPPULLDOWN2 BIT(8) +#define USB3_FORCE_DPPULLDOWN2 BIT(9) +#define USB3_SEL_FORCE_DMPULLDOWN2 BIT(10) +#define USB3_FORCE_DMPULLDOWN2 BIT(11) + +/** + * struct st_dwc3 - dwc3-st driver private structure + * @dev: device pointer + * @glue_base: ioaddr for the glue registers + * @regmap: regmap pointer for getting syscfg + * @syscfg_reg_off: usb syscfg control offset + * @dr_mode: drd static host/device config + * @rstc_pwrdn: rest controller for powerdown signal + * @rstc_rst: reset controller for softreset signal + */ + +struct st_dwc3 { + struct device *dev; + void __iomem *glue_base; + struct regmap *regmap; + int syscfg_reg_off; + enum usb_dr_mode dr_mode; + struct reset_control *rstc_pwrdn; + struct reset_control *rstc_rst; +}; + +static inline u32 st_dwc3_readl(void __iomem *base, u32 offset) +{ + return readl_relaxed(base + offset); +} + +static inline void st_dwc3_writel(void __iomem *base, u32 offset, u32 value) +{ + writel_relaxed(value, base + offset); +} + +/** + * st_dwc3_drd_init: program the port + * @dwc3_data: driver private structure + * Description: this function is to program the port as either host or device + * according to the static configuration passed from devicetree. + * OTG and dual role are not yet supported! + */ +static int st_dwc3_drd_init(struct st_dwc3 *dwc3_data) +{ + u32 val; + int err; + + err = regmap_read(dwc3_data->regmap, dwc3_data->syscfg_reg_off, &val); + if (err) + return err; + + val &= USB3_CONTROL_MASK; + + switch (dwc3_data->dr_mode) { + case USB_DR_MODE_PERIPHERAL: + + val &= ~(USB3_FORCE_VBUSVALID | USB3_DELAY_VBUSVALID + | USB3_SEL_FORCE_OPMODE | USB3_FORCE_OPMODE(0x3) + | USB3_SEL_FORCE_DPPULLDOWN2 | USB3_FORCE_DPPULLDOWN2 + | USB3_SEL_FORCE_DMPULLDOWN2 | USB3_FORCE_DMPULLDOWN2); + + val |= USB3_DEVICE_NOT_HOST; + + dev_dbg(dwc3_data->dev, "Configuring as Device\n"); + break; + + case USB_DR_MODE_HOST: + + val &= ~(USB3_DEVICE_NOT_HOST | USB3_FORCE_VBUSVALID + | USB3_SEL_FORCE_OPMODE | USB3_FORCE_OPMODE(0x3) + | USB3_SEL_FORCE_DPPULLDOWN2 | USB3_FORCE_DPPULLDOWN2 + | USB3_SEL_FORCE_DMPULLDOWN2 | USB3_FORCE_DMPULLDOWN2); + + /* + * USB3_DELAY_VBUSVALID is ANDed with USB_C_VBUSVALID. Thus, + * when set to ‘0‘, it can delay the arrival of VBUSVALID + * information to VBUSVLDEXT2 input of the pico PHY. + * We don't want to do that so we set the bit to '1'. + */ + + val |= USB3_DELAY_VBUSVALID; + + dev_dbg(dwc3_data->dev, "Configuring as Host\n"); + break; + + default: + dev_err(dwc3_data->dev, "Unsupported mode of operation %d\n", + dwc3_data->dr_mode); + return -EINVAL; + } + + return regmap_write(dwc3_data->regmap, dwc3_data->syscfg_reg_off, val); +} + +/** + * st_dwc3_init: init the controller via glue logic + * @dwc3_data: driver private structure + */ +static void st_dwc3_init(struct st_dwc3 *dwc3_data) +{ + u32 reg = st_dwc3_readl(dwc3_data->glue_base, CLKRST_CTRL); + + reg |= AUX_CLK_EN | EXT_CFG_RESET_N | XHCI_REVISION; + reg &= ~SW_PIPEW_RESET_N; + st_dwc3_writel(dwc3_data->glue_base, CLKRST_CTRL, reg); + + /* configure mux for vbus, powerpresent and bvalid signals */ + reg = st_dwc3_readl(dwc3_data->glue_base, USB2_VBUS_MNGMNT_SEL1); + + reg |= SEL_OVERRIDE_VBUSVALID(USB2_VBUS_UTMIOTG) | + SEL_OVERRIDE_POWERPRESENT(USB2_VBUS_UTMIOTG) | + SEL_OVERRIDE_BVALID(USB2_VBUS_UTMIOTG); + + st_dwc3_writel(dwc3_data->glue_base, USB2_VBUS_MNGMNT_SEL1, reg); + + reg = st_dwc3_readl(dwc3_data->glue_base, CLKRST_CTRL); + reg |= SW_PIPEW_RESET_N; + st_dwc3_writel(dwc3_data->glue_base, CLKRST_CTRL, reg); +} + +static int st_dwc3_probe(struct platform_device *pdev) +{ + struct st_dwc3 *dwc3_data; + struct resource *res; + struct device *dev = &pdev->dev; + struct device_node *node = dev->of_node, *child; + struct regmap *regmap; + int ret; + + dwc3_data = devm_kzalloc(dev, sizeof(*dwc3_data), GFP_KERNEL); + if (!dwc3_data) + return -ENOMEM; + + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "reg-glue"); + dwc3_data->glue_base = devm_ioremap_resource(dev, res); + if (IS_ERR(dwc3_data->glue_base)) + return PTR_ERR(dwc3_data->glue_base); + + regmap = syscon_regmap_lookup_by_phandle(node, "st,syscfg"); + if (IS_ERR(regmap)) + return PTR_ERR(regmap); + + dma_set_coherent_mask(dev, dev->coherent_dma_mask); + dwc3_data->dev = dev; + dwc3_data->regmap = regmap; + + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "syscfg-reg"); + if (!res) { + ret = -ENXIO; + goto undo_platform_dev_alloc; + } + + dwc3_data->syscfg_reg_off = res->start; + + dev_vdbg(&pdev->dev, "glue-logic addr 0x%p, syscfg-reg offset 0x%x\n", + dwc3_data->glue_base, dwc3_data->syscfg_reg_off); + + dwc3_data->rstc_pwrdn = devm_reset_control_get(dev, "powerdown"); + if (IS_ERR(dwc3_data->rstc_pwrdn)) { + dev_err(&pdev->dev, "could not get power controller\n"); + ret = PTR_ERR(dwc3_data->rstc_pwrdn); + goto undo_platform_dev_alloc; + } + + /* Manage PowerDown */ + reset_control_deassert(dwc3_data->rstc_pwrdn); + + dwc3_data->rstc_rst = devm_reset_control_get(dev, "softreset"); + if (IS_ERR(dwc3_data->rstc_rst)) { + dev_err(&pdev->dev, "could not get reset controller\n"); + ret = PTR_ERR(dwc3_data->rstc_pwrdn); + goto undo_powerdown; + } + + /* Manage SoftReset */ + reset_control_deassert(dwc3_data->rstc_rst); + + child = of_get_child_by_name(node, "dwc3"); + if (!child) { + dev_err(&pdev->dev, "failed to find dwc3 core node\n"); + ret = -ENODEV; + goto undo_softreset; + } + + dwc3_data->dr_mode = of_usb_get_dr_mode(child); + + /* Allocate and initialize the core */ + ret = of_platform_populate(node, NULL, NULL, dev); + if (ret) { + dev_err(dev, "failed to add dwc3 core\n"); + goto undo_softreset; + } + + /* + * Configure the USB port as device or host according to the static + * configuration passed from DT. + * DRD is the only mode currently supported so this will be enhanced + * as soon as OTG is available. + */ + ret = st_dwc3_drd_init(dwc3_data); + if (ret) { + dev_err(dev, "drd initialisation failed\n"); + goto undo_softreset; + } + + /* ST glue logic init */ + st_dwc3_init(dwc3_data); + + platform_set_drvdata(pdev, dwc3_data); + return 0; + +undo_softreset: + reset_control_assert(dwc3_data->rstc_rst); +undo_powerdown: + reset_control_assert(dwc3_data->rstc_pwrdn); +undo_platform_dev_alloc: + platform_device_put(pdev); + return ret; +} + +static int st_dwc3_remove(struct platform_device *pdev) +{ + struct st_dwc3 *dwc3_data = platform_get_drvdata(pdev); + + of_platform_depopulate(&pdev->dev); + + reset_control_assert(dwc3_data->rstc_pwrdn); + reset_control_assert(dwc3_data->rstc_rst); + + return 0; +} + +#ifdef CONFIG_PM_SLEEP +static int st_dwc3_suspend(struct device *dev) +{ + struct st_dwc3 *dwc3_data = dev_get_drvdata(dev); + + reset_control_assert(dwc3_data->rstc_pwrdn); + reset_control_assert(dwc3_data->rstc_rst); + + pinctrl_pm_select_sleep_state(dev); + + return 0; +} + +static int st_dwc3_resume(struct device *dev) +{ + struct st_dwc3 *dwc3_data = dev_get_drvdata(dev); + int ret; + + pinctrl_pm_select_default_state(dev); + + reset_control_deassert(dwc3_data->rstc_pwrdn); + reset_control_deassert(dwc3_data->rstc_rst); + + ret = st_dwc3_drd_init(dwc3_data); + if (ret) { + dev_err(dev, "drd initialisation failed\n"); + return ret; + } + + /* ST glue logic init */ + st_dwc3_init(dwc3_data); + + return 0; +} +#endif /* CONFIG_PM_SLEEP */ + +static SIMPLE_DEV_PM_OPS(st_dwc3_dev_pm_ops, st_dwc3_suspend, st_dwc3_resume); + +static const struct of_device_id st_dwc3_match[] = { + { .compatible = "st,stih407-dwc3" }, + { /* sentinel */ }, +}; + +MODULE_DEVICE_TABLE(of, st_dwc3_match); + +static struct platform_driver st_dwc3_driver = { + .probe = st_dwc3_probe, + .remove = st_dwc3_remove, + .driver = { + .name = "usb-st-dwc3", + .of_match_table = st_dwc3_match, + .pm = &st_dwc3_dev_pm_ops, + }, +}; + +module_platform_driver(st_dwc3_driver); + +MODULE_AUTHOR("Giuseppe Cavallaro "); +MODULE_DESCRIPTION("DesignWare USB3 STi Glue Layer"); +MODULE_LICENSE("GPL v2"); -- cgit v1.1 From a5e4aa4d770ae96da52c8fa035751d2046e2434f Mon Sep 17 00:00:00 2001 From: Daniel Mack Date: Wed, 3 Sep 2014 17:21:24 +0200 Subject: usb: musb: cppi41: tweak hrtimer values Intensive tests with USB audio devices connected to a musb host port have shown reproducible pops and clicks in both the playback and the capture stream. These are related to how the early_tx hrtimer is set up, and it turns out they can be fixed by reducing the timer's slack value from 40 to 25 us. Also, when the callback is ran without taking action, it should be rescheduled 20 us later instead of 50 us. Reported-and-tested-by: Sven Neumann Signed-off-by: Daniel Mack Signed-off-by: Felipe Balbi --- drivers/usb/musb/musb_cppi41.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/musb/musb_cppi41.c b/drivers/usb/musb/musb_cppi41.c index 47ae645..ecdd632 100644 --- a/drivers/usb/musb/musb_cppi41.c +++ b/drivers/usb/musb/musb_cppi41.c @@ -200,7 +200,7 @@ static enum hrtimer_restart cppi41_recheck_tx_req(struct hrtimer *timer) if (!list_empty(&controller->early_tx_list)) { ret = HRTIMER_RESTART; hrtimer_forward_now(&controller->early_tx, - ktime_set(0, 50 * NSEC_PER_USEC)); + ktime_set(0, 20 * NSEC_PER_USEC)); } spin_unlock_irqrestore(&musb->lock, flags); @@ -278,7 +278,7 @@ static void cppi41_dma_callback(void *private_data) hrtimer_start_range_ns(&controller->early_tx, ktime_set(0, usecs * NSEC_PER_USEC), - 40 * NSEC_PER_USEC, + 20 * NSEC_PER_USEC, HRTIMER_MODE_REL); } } -- cgit v1.1 From b854100eda59a1df9eaf7454cc7c297910055f42 Mon Sep 17 00:00:00 2001 From: Yoshihiro Shimoda Date: Wed, 3 Sep 2014 14:25:56 +0900 Subject: usb: renesas_usbhs: Add device tree support for R-Car H2 and M2 This driver supports other SoCs, but they need boards/Soc depend code. So, this patch adds device tree support for R-Car H2 and M2 initially. Signed-off-by: Yoshihiro Shimoda Acked-by: Kuninori Morimoto Signed-off-by: Felipe Balbi --- drivers/usb/renesas_usbhs/common.c | 44 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 44 insertions(+) (limited to 'drivers/usb') diff --git a/drivers/usb/renesas_usbhs/common.c b/drivers/usb/renesas_usbhs/common.c index 1b9bf8d..b3b6813 100644 --- a/drivers/usb/renesas_usbhs/common.c +++ b/drivers/usb/renesas_usbhs/common.c @@ -18,6 +18,8 @@ #include #include #include +#include +#include #include #include #include @@ -438,6 +440,43 @@ static int usbhsc_drvcllbck_notify_hotplug(struct platform_device *pdev) /* * platform functions */ +static const struct of_device_id usbhs_of_match[] = { + { + .compatible = "renesas,usbhs-r8a7790", + .data = (void *)USBHS_TYPE_R8A7790, + }, + { + .compatible = "renesas,usbhs-r8a7791", + .data = (void *)USBHS_TYPE_R8A7791, + }, + { }, +}; +MODULE_DEVICE_TABLE(of, usbhs_of_match); + +static struct renesas_usbhs_platform_info *usbhs_parse_dt(struct device *dev) +{ + struct renesas_usbhs_platform_info *info; + struct renesas_usbhs_driver_param *dparam; + const struct of_device_id *of_id = of_match_device(usbhs_of_match, dev); + u32 tmp; + int gpio; + + info = devm_kzalloc(dev, sizeof(*info), GFP_KERNEL); + if (!info) + return NULL; + + dparam = &info->driver_param; + dparam->type = of_id ? (u32)of_id->data : 0; + if (!of_property_read_u32(dev->of_node, "renesas,buswait", &tmp)) + dparam->buswait_bwait = tmp; + gpio = of_get_named_gpio_flags(dev->of_node, "renesas,enable-gpio", 0, + NULL); + if (gpio > 0) + dparam->enable_gpio = gpio; + + return info; +} + static int usbhs_probe(struct platform_device *pdev) { struct renesas_usbhs_platform_info *info = dev_get_platdata(&pdev->dev); @@ -446,6 +485,10 @@ static int usbhs_probe(struct platform_device *pdev) struct resource *res, *irq_res; int ret; + /* check device node */ + if (pdev->dev.of_node) + info = pdev->dev.platform_data = usbhs_parse_dt(&pdev->dev); + /* check platform information */ if (!info) { dev_err(&pdev->dev, "no platform information\n"); @@ -689,6 +732,7 @@ static struct platform_driver renesas_usbhs_driver = { .driver = { .name = "renesas_usbhs", .pm = &usbhsc_pm_ops, + .of_match_table = of_match_ptr(usbhs_of_match), }, .probe = usbhs_probe, .remove = usbhs_remove, -- cgit v1.1 From 26a029f2277bf58c72ada0a92ae44ff9dd702a2e Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Mon, 8 Sep 2014 11:18:14 +0300 Subject: usb: gadget: f_uvc: Store EP0 control request state during setup stage To handle class requests received on ep0, the driver needs to access the length and direction of the request after the setup stage. It currently stores them in a v4l2 event during the setup stage, and then copies them from the event structure to the driver internal state structure when the event is dequeued. This two-steps approach isn't necessary. Simplify the driver by storing the needed information in the driver internal state structure directly during the setup stage. Signed-off-by: Laurent Pinchart Acked-by: Andrzej Pietrasiewicz Signed-off-by: Felipe Balbi --- drivers/usb/gadget/function/f_uvc.c | 6 ++++++ drivers/usb/gadget/function/uvc_v4l2.c | 19 +------------------ 2 files changed, 7 insertions(+), 18 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/gadget/function/f_uvc.c b/drivers/usb/gadget/function/f_uvc.c index ff4340a..e9d625b 100644 --- a/drivers/usb/gadget/function/f_uvc.c +++ b/drivers/usb/gadget/function/f_uvc.c @@ -251,6 +251,12 @@ uvc_function_setup(struct usb_function *f, const struct usb_ctrlrequest *ctrl) if (le16_to_cpu(ctrl->wLength) > UVC_MAX_REQUEST_SIZE) return -EINVAL; + /* Tell the complete callback to generate an event for the next request + * that will be enqueued by UVCIOC_SEND_RESPONSE. + */ + uvc->event_setup_out = !(ctrl->bRequestType & USB_DIR_IN); + uvc->event_length = le16_to_cpu(ctrl->wLength); + memset(&v4l2_event, 0, sizeof(v4l2_event)); v4l2_event.type = UVC_EVENT_SETUP; memcpy(&uvc_event->req, ctrl, sizeof(uvc_event->req)); diff --git a/drivers/usb/gadget/function/uvc_v4l2.c b/drivers/usb/gadget/function/uvc_v4l2.c index bcd71ce..f22b878 100644 --- a/drivers/usb/gadget/function/uvc_v4l2.c +++ b/drivers/usb/gadget/function/uvc_v4l2.c @@ -271,25 +271,8 @@ uvc_v4l2_do_ioctl(struct file *file, unsigned int cmd, void *arg) /* Events */ case VIDIOC_DQEVENT: - { - struct v4l2_event *event = arg; - - ret = v4l2_event_dequeue(&handle->vfh, event, + return v4l2_event_dequeue(&handle->vfh, arg, file->f_flags & O_NONBLOCK); - if (ret == 0 && event->type == UVC_EVENT_SETUP) { - struct uvc_event *uvc_event = (void *)&event->u.data; - - /* Tell the complete callback to generate an event for - * the next request that will be enqueued by - * uvc_event_write. - */ - uvc->event_setup_out = - !(uvc_event->req.bRequestType & USB_DIR_IN); - uvc->event_length = uvc_event->req.wLength; - } - - return ret; - } case VIDIOC_SUBSCRIBE_EVENT: { -- cgit v1.1 From a1d27a4bf5bb4144c593358cbd7261c6c6f0a023 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Mon, 8 Sep 2014 11:18:15 +0300 Subject: usb: gadget: f_uvc: Move to video_ioctl2 Simplify ioctl handling by using video_ioctl2. Signed-off-by: Laurent Pinchart Acked-by: Andrzej Pietrasiewicz Signed-off-by: Felipe Balbi --- drivers/usb/gadget/function/f_uvc.c | 2 + drivers/usb/gadget/function/uvc_v4l2.c | 298 +++++++++++++++++---------------- 2 files changed, 159 insertions(+), 141 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/gadget/function/f_uvc.c b/drivers/usb/gadget/function/f_uvc.c index e9d625b..b347530 100644 --- a/drivers/usb/gadget/function/f_uvc.c +++ b/drivers/usb/gadget/function/f_uvc.c @@ -414,7 +414,9 @@ uvc_register_video(struct uvc_device *uvc) video->v4l2_dev = &uvc->v4l2_dev; video->fops = &uvc_v4l2_fops; + video->ioctl_ops = &uvc_v4l2_ioctl_ops; video->release = video_device_release; + video->vfl_dir = VFL_DIR_TX; strlcpy(video->name, cdev->gadget->name, sizeof(video->name)); uvc->vdev = video; diff --git a/drivers/usb/gadget/function/uvc_v4l2.c b/drivers/usb/gadget/function/uvc_v4l2.c index f22b878..14c3a37 100644 --- a/drivers/usb/gadget/function/uvc_v4l2.c +++ b/drivers/usb/gadget/function/uvc_v4l2.c @@ -48,7 +48,7 @@ uvc_send_response(struct uvc_device *uvc, struct uvc_request_data *data) } /* -------------------------------------------------------------------------- - * V4L2 + * V4L2 ioctls */ struct uvc_format @@ -63,8 +63,29 @@ static struct uvc_format uvc_formats[] = { }; static int -uvc_v4l2_get_format(struct uvc_video *video, struct v4l2_format *fmt) +uvc_v4l2_querycap(struct file *file, void *fh, struct v4l2_capability *cap) +{ + struct video_device *vdev = video_devdata(file); + struct uvc_device *uvc = video_get_drvdata(vdev); + struct usb_composite_dev *cdev = uvc->func.config->cdev; + + strlcpy(cap->driver, "g_uvc", sizeof(cap->driver)); + strlcpy(cap->card, cdev->gadget->name, sizeof(cap->card)); + strlcpy(cap->bus_info, dev_name(&cdev->gadget->dev), + sizeof(cap->bus_info)); + + cap->capabilities = V4L2_CAP_VIDEO_OUTPUT | V4L2_CAP_STREAMING; + + return 0; +} + +static int +uvc_v4l2_get_format(struct file *file, void *fh, struct v4l2_format *fmt) { + struct video_device *vdev = video_devdata(file); + struct uvc_device *uvc = video_get_drvdata(vdev); + struct uvc_video *video = &uvc->video; + fmt->fmt.pix.pixelformat = video->fcc; fmt->fmt.pix.width = video->width; fmt->fmt.pix.height = video->height; @@ -78,8 +99,11 @@ uvc_v4l2_get_format(struct uvc_video *video, struct v4l2_format *fmt) } static int -uvc_v4l2_set_format(struct uvc_video *video, struct v4l2_format *fmt) +uvc_v4l2_set_format(struct file *file, void *fh, struct v4l2_format *fmt) { + struct video_device *vdev = video_devdata(file); + struct uvc_device *uvc = video_get_drvdata(vdev); + struct uvc_video *video = &uvc->video; struct uvc_format *format; unsigned int imagesize; unsigned int bpl; @@ -116,192 +140,184 @@ uvc_v4l2_set_format(struct uvc_video *video, struct v4l2_format *fmt) } static int -uvc_v4l2_open(struct file *file) +uvc_v4l2_reqbufs(struct file *file, void *fh, struct v4l2_requestbuffers *b) { struct video_device *vdev = video_devdata(file); struct uvc_device *uvc = video_get_drvdata(vdev); - struct uvc_file_handle *handle; - - handle = kzalloc(sizeof(*handle), GFP_KERNEL); - if (handle == NULL) - return -ENOMEM; - - v4l2_fh_init(&handle->vfh, vdev); - v4l2_fh_add(&handle->vfh); + struct uvc_video *video = &uvc->video; - handle->device = &uvc->video; - file->private_data = &handle->vfh; + if (b->type != video->queue.queue.type) + return -EINVAL; - uvc_function_connect(uvc); - return 0; + return uvc_alloc_buffers(&video->queue, b); } static int -uvc_v4l2_release(struct file *file) +uvc_v4l2_querybuf(struct file *file, void *fh, struct v4l2_buffer *b) { struct video_device *vdev = video_devdata(file); struct uvc_device *uvc = video_get_drvdata(vdev); - struct uvc_file_handle *handle = to_uvc_file_handle(file->private_data); - struct uvc_video *video = handle->device; - - uvc_function_disconnect(uvc); - - uvc_video_enable(video, 0); - uvc_free_buffers(&video->queue); - - file->private_data = NULL; - v4l2_fh_del(&handle->vfh); - v4l2_fh_exit(&handle->vfh); - kfree(handle); + struct uvc_video *video = &uvc->video; - return 0; + return uvc_query_buffer(&video->queue, b); } -static long -uvc_v4l2_do_ioctl(struct file *file, unsigned int cmd, void *arg) +static int +uvc_v4l2_qbuf(struct file *file, void *fh, struct v4l2_buffer *b) { struct video_device *vdev = video_devdata(file); struct uvc_device *uvc = video_get_drvdata(vdev); - struct uvc_file_handle *handle = to_uvc_file_handle(file->private_data); - struct usb_composite_dev *cdev = uvc->func.config->cdev; struct uvc_video *video = &uvc->video; - int ret = 0; - - switch (cmd) { - /* Query capabilities */ - case VIDIOC_QUERYCAP: - { - struct v4l2_capability *cap = arg; - - memset(cap, 0, sizeof *cap); - strlcpy(cap->driver, "g_uvc", sizeof(cap->driver)); - strlcpy(cap->card, cdev->gadget->name, sizeof(cap->card)); - strlcpy(cap->bus_info, dev_name(&cdev->gadget->dev), - sizeof cap->bus_info); - cap->version = LINUX_VERSION_CODE; - cap->capabilities = V4L2_CAP_VIDEO_OUTPUT | V4L2_CAP_STREAMING; - break; - } - - /* Get & Set format */ - case VIDIOC_G_FMT: - { - struct v4l2_format *fmt = arg; + int ret; - if (fmt->type != video->queue.queue.type) - return -EINVAL; - - return uvc_v4l2_get_format(video, fmt); - } + ret = uvc_queue_buffer(&video->queue, b); + if (ret < 0) + return ret; - case VIDIOC_S_FMT: - { - struct v4l2_format *fmt = arg; + return uvc_video_pump(video); +} - if (fmt->type != video->queue.queue.type) - return -EINVAL; +static int +uvc_v4l2_dqbuf(struct file *file, void *fh, struct v4l2_buffer *b) +{ + struct video_device *vdev = video_devdata(file); + struct uvc_device *uvc = video_get_drvdata(vdev); + struct uvc_video *video = &uvc->video; - return uvc_v4l2_set_format(video, fmt); - } + return uvc_dequeue_buffer(&video->queue, b, file->f_flags & O_NONBLOCK); +} - /* Buffers & streaming */ - case VIDIOC_REQBUFS: - { - struct v4l2_requestbuffers *rb = arg; +static int +uvc_v4l2_streamon(struct file *file, void *fh, enum v4l2_buf_type type) +{ + struct video_device *vdev = video_devdata(file); + struct uvc_device *uvc = video_get_drvdata(vdev); + struct uvc_video *video = &uvc->video; + int ret; - if (rb->type != video->queue.queue.type) - return -EINVAL; + if (type != video->queue.queue.type) + return -EINVAL; - ret = uvc_alloc_buffers(&video->queue, rb); - if (ret < 0) - return ret; + /* Enable UVC video. */ + ret = uvc_video_enable(video, 1); + if (ret < 0) + return ret; - ret = 0; - break; - } + /* + * Complete the alternate setting selection setup phase now that + * userspace is ready to provide video frames. + */ + uvc_function_setup_continue(uvc); + uvc->state = UVC_STATE_STREAMING; - case VIDIOC_QUERYBUF: - { - struct v4l2_buffer *buf = arg; + return 0; +} - return uvc_query_buffer(&video->queue, buf); - } +static int +uvc_v4l2_streamoff(struct file *file, void *fh, enum v4l2_buf_type type) +{ + struct video_device *vdev = video_devdata(file); + struct uvc_device *uvc = video_get_drvdata(vdev); + struct uvc_video *video = &uvc->video; - case VIDIOC_QBUF: - if ((ret = uvc_queue_buffer(&video->queue, arg)) < 0) - return ret; + if (type != video->queue.queue.type) + return -EINVAL; - return uvc_video_pump(video); + return uvc_video_enable(video, 0); +} - case VIDIOC_DQBUF: - return uvc_dequeue_buffer(&video->queue, arg, - file->f_flags & O_NONBLOCK); +static int +uvc_v4l2_subscribe_event(struct v4l2_fh *fh, + const struct v4l2_event_subscription *sub) +{ + if (sub->type < UVC_EVENT_FIRST || sub->type > UVC_EVENT_LAST) + return -EINVAL; - case VIDIOC_STREAMON: - { - int *type = arg; + return v4l2_event_subscribe(fh, sub, 2, NULL); +} - if (*type != video->queue.queue.type) - return -EINVAL; +static int +uvc_v4l2_unsubscribe_event(struct v4l2_fh *fh, + const struct v4l2_event_subscription *sub) +{ + return v4l2_event_unsubscribe(fh, sub); +} - /* Enable UVC video. */ - ret = uvc_video_enable(video, 1); - if (ret < 0) - return ret; +static long +uvc_v4l2_ioctl_default(struct file *file, void *fh, bool valid_prio, + unsigned int cmd, void *arg) +{ + struct video_device *vdev = video_devdata(file); + struct uvc_device *uvc = video_get_drvdata(vdev); - /* - * Complete the alternate setting selection setup phase now that - * userspace is ready to provide video frames. - */ - uvc_function_setup_continue(uvc); - uvc->state = UVC_STATE_STREAMING; + switch (cmd) { + case UVCIOC_SEND_RESPONSE: + return uvc_send_response(uvc, arg); - return 0; + default: + return -ENOIOCTLCMD; } +} - case VIDIOC_STREAMOFF: - { - int *type = arg; +static const struct v4l2_ioctl_ops uvc_v4l2_ioctl_ops = { + .vidioc_querycap = uvc_v4l2_querycap, + .vidioc_g_fmt_vid_out = uvc_v4l2_get_format, + .vidioc_s_fmt_vid_out = uvc_v4l2_set_format, + .vidioc_reqbufs = uvc_v4l2_reqbufs, + .vidioc_querybuf = uvc_v4l2_querybuf, + .vidioc_qbuf = uvc_v4l2_qbuf, + .vidioc_dqbuf = uvc_v4l2_dqbuf, + .vidioc_streamon = uvc_v4l2_streamon, + .vidioc_streamoff = uvc_v4l2_streamoff, + .vidioc_subscribe_event = uvc_v4l2_subscribe_event, + .vidioc_unsubscribe_event = uvc_v4l2_unsubscribe_event, + .vidioc_default = uvc_v4l2_ioctl_default, +}; - if (*type != video->queue.queue.type) - return -EINVAL; +/* -------------------------------------------------------------------------- + * V4L2 + */ - return uvc_video_enable(video, 0); - } +static int +uvc_v4l2_open(struct file *file) +{ + struct video_device *vdev = video_devdata(file); + struct uvc_device *uvc = video_get_drvdata(vdev); + struct uvc_file_handle *handle; - /* Events */ - case VIDIOC_DQEVENT: - return v4l2_event_dequeue(&handle->vfh, arg, - file->f_flags & O_NONBLOCK); + handle = kzalloc(sizeof(*handle), GFP_KERNEL); + if (handle == NULL) + return -ENOMEM; - case VIDIOC_SUBSCRIBE_EVENT: - { - struct v4l2_event_subscription *sub = arg; + v4l2_fh_init(&handle->vfh, vdev); + v4l2_fh_add(&handle->vfh); - if (sub->type < UVC_EVENT_FIRST || sub->type > UVC_EVENT_LAST) - return -EINVAL; + handle->device = &uvc->video; + file->private_data = &handle->vfh; - return v4l2_event_subscribe(&handle->vfh, arg, 2, NULL); - } + uvc_function_connect(uvc); + return 0; +} - case VIDIOC_UNSUBSCRIBE_EVENT: - return v4l2_event_unsubscribe(&handle->vfh, arg); +static int +uvc_v4l2_release(struct file *file) +{ + struct video_device *vdev = video_devdata(file); + struct uvc_device *uvc = video_get_drvdata(vdev); + struct uvc_file_handle *handle = to_uvc_file_handle(file->private_data); + struct uvc_video *video = handle->device; - case UVCIOC_SEND_RESPONSE: - ret = uvc_send_response(uvc, arg); - break; + uvc_function_disconnect(uvc); - default: - return -ENOIOCTLCMD; - } + uvc_video_enable(video, 0); + uvc_free_buffers(&video->queue); - return ret; -} + file->private_data = NULL; + v4l2_fh_del(&handle->vfh); + v4l2_fh_exit(&handle->vfh); + kfree(handle); -static long -uvc_v4l2_ioctl(struct file *file, unsigned int cmd, unsigned long arg) -{ - return video_usercopy(file, cmd, arg, uvc_v4l2_do_ioctl); + return 0; } static int @@ -338,7 +354,7 @@ static struct v4l2_file_operations uvc_v4l2_fops = { .owner = THIS_MODULE, .open = uvc_v4l2_open, .release = uvc_v4l2_release, - .ioctl = uvc_v4l2_ioctl, + .ioctl = video_ioctl2, .mmap = uvc_v4l2_mmap, .poll = uvc_v4l2_poll, #ifndef CONFIG_MMU -- cgit v1.1 From efb540c895d2cb77b1472edda6ca45d40719a041 Mon Sep 17 00:00:00 2001 From: Andrzej Pietrasiewicz Date: Mon, 8 Sep 2014 11:18:16 +0300 Subject: usb: gadget: uvc: move module parameters from f_uvc When configfs support is integrated the future uvc function module must not take any parameters. Move parameters to webcam. Signed-off-by: Andrzej Pietrasiewicz Tested-by: Michael Grzeschik Signed-off-by: Laurent Pinchart Signed-off-by: Felipe Balbi --- drivers/usb/gadget/function/f_uvc.c | 26 +++++++++----------------- drivers/usb/gadget/function/f_uvc.h | 6 +++++- drivers/usb/gadget/legacy/webcam.c | 27 ++++++++++++++++++++++++--- 3 files changed, 38 insertions(+), 21 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/gadget/function/f_uvc.c b/drivers/usb/gadget/function/f_uvc.c index b347530..187c3a0 100644 --- a/drivers/usb/gadget/function/f_uvc.c +++ b/drivers/usb/gadget/function/f_uvc.c @@ -29,21 +29,9 @@ #include "uvc.h" unsigned int uvc_gadget_trace_param; - -/*-------------------------------------------------------------------------*/ - -/* module parameters specific to the Video streaming endpoint */ -static unsigned int streaming_interval = 1; -module_param(streaming_interval, uint, S_IRUGO|S_IWUSR); -MODULE_PARM_DESC(streaming_interval, "1 - 16"); - -static unsigned int streaming_maxpacket = 1024; -module_param(streaming_maxpacket, uint, S_IRUGO|S_IWUSR); -MODULE_PARM_DESC(streaming_maxpacket, "1 - 1023 (FS), 1 - 3072 (hs/ss)"); - +static unsigned int streaming_interval; +static unsigned int streaming_maxpacket; static unsigned int streaming_maxburst; -module_param(streaming_maxburst, uint, S_IRUGO|S_IWUSR); -MODULE_PARM_DESC(streaming_maxburst, "0 - 15 (ss only)"); /* -------------------------------------------------------------------------- * Function descriptors @@ -756,7 +744,9 @@ uvc_bind_config(struct usb_configuration *c, const struct uvc_descriptor_header * const *ss_control, const struct uvc_descriptor_header * const *fs_streaming, const struct uvc_descriptor_header * const *hs_streaming, - const struct uvc_descriptor_header * const *ss_streaming) + const struct uvc_descriptor_header * const *ss_streaming, + unsigned int stream_interv, unsigned int stream_maxpkt, + unsigned int stream_maxburst, unsigned int trace) { struct uvc_device *uvc; int ret = 0; @@ -794,6 +784,10 @@ uvc_bind_config(struct usb_configuration *c, ss_streaming[0]->bDescriptorSubType != UVC_VS_INPUT_HEADER) goto error; + streaming_interval = stream_interv; + streaming_maxpacket = stream_maxpkt; + streaming_maxburst = stream_maxburst; + uvc_gadget_trace_param = trace; uvc->desc.fs_control = fs_control; uvc->desc.ss_control = ss_control; uvc->desc.fs_streaming = fs_streaming; @@ -838,6 +832,4 @@ error: return ret; } -module_param_named(trace, uvc_gadget_trace_param, uint, S_IRUGO|S_IWUSR); -MODULE_PARM_DESC(trace, "Trace level bitmask"); diff --git a/drivers/usb/gadget/function/f_uvc.h b/drivers/usb/gadget/function/f_uvc.h index ec52752..74b9602 100644 --- a/drivers/usb/gadget/function/f_uvc.h +++ b/drivers/usb/gadget/function/f_uvc.h @@ -21,7 +21,11 @@ int uvc_bind_config(struct usb_configuration *c, const struct uvc_descriptor_header * const *hs_control, const struct uvc_descriptor_header * const *fs_streaming, const struct uvc_descriptor_header * const *hs_streaming, - const struct uvc_descriptor_header * const *ss_streaming); + const struct uvc_descriptor_header * const *ss_streaming, + unsigned int streaming_interval_webcam, + unsigned int streaming_maxpacket_webcam, + unsigned int streaming_maxburst_webcam, + unsigned int uvc_gadget_trace_webcam); #endif /* _F_UVC_H_ */ diff --git a/drivers/usb/gadget/legacy/webcam.c b/drivers/usb/gadget/legacy/webcam.c index a11d8e4..f826622 100644 --- a/drivers/usb/gadget/legacy/webcam.c +++ b/drivers/usb/gadget/legacy/webcam.c @@ -29,6 +29,25 @@ #include "f_uvc.c" USB_GADGET_COMPOSITE_OPTIONS(); + +/*-------------------------------------------------------------------------*/ + +/* module parameters specific to the Video streaming endpoint */ +static unsigned int streaming_interval = 1; +module_param(streaming_interval, uint, S_IRUGO|S_IWUSR); +MODULE_PARM_DESC(streaming_interval, "1 - 16"); + +static unsigned int streaming_maxpacket = 1024; +module_param(streaming_maxpacket, uint, S_IRUGO|S_IWUSR); +MODULE_PARM_DESC(streaming_maxpacket, "1 - 1023 (FS), 1 - 3072 (hs/ss)"); + +static unsigned int streaming_maxburst; +module_param(streaming_maxburst, uint, S_IRUGO|S_IWUSR); +MODULE_PARM_DESC(streaming_maxburst, "0 - 15 (ss only)"); + +static unsigned int trace; +module_param(trace, uint, S_IRUGO|S_IWUSR); +MODULE_PARM_DESC(trace, "Trace level bitmask"); /* -------------------------------------------------------------------------- * Device descriptor */ @@ -326,9 +345,11 @@ static const struct uvc_descriptor_header * const uvc_ss_streaming_cls[] = { static int __init webcam_config_bind(struct usb_configuration *c) { - return uvc_bind_config(c, uvc_fs_control_cls, uvc_ss_control_cls, - uvc_fs_streaming_cls, uvc_hs_streaming_cls, - uvc_ss_streaming_cls); + return uvc_bind_config(c, uvc_fs_control_cls, + uvc_ss_control_cls, uvc_fs_streaming_cls, + uvc_hs_streaming_cls, uvc_ss_streaming_cls, + streaming_interval, streaming_maxpacket, + streaming_maxburst, trace); } static struct usb_configuration webcam_config_driver = { -- cgit v1.1 From 70aacc5777d1f1ca0a88067c9121ce86441bc4e0 Mon Sep 17 00:00:00 2001 From: Amit Virdi Date: Tue, 9 Sep 2014 11:57:37 +0530 Subject: usb: gadget: zero: Fix warning generated by kbuild The kbuild test bot generated the warning: drivers/usb/gadget/function/f_sourcesink.c:1498: warning: comparison is always false due to limited range of data type This patch fixes it. Reported-by: kbuild test robot Signed-off-by: Amit Virdi CC: Felipe Balbi Signed-off-by: Felipe Balbi --- drivers/usb/gadget/function/f_sourcesink.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/gadget/function/f_sourcesink.c b/drivers/usb/gadget/function/f_sourcesink.c index 7c091a3..80be25b 100644 --- a/drivers/usb/gadget/function/f_sourcesink.c +++ b/drivers/usb/gadget/function/f_sourcesink.c @@ -1483,7 +1483,7 @@ static ssize_t f_ss_opts_int_interval_store(struct f_ss_opts *opts, const char *page, size_t len) { int ret; - u8 num; + u32 num; mutex_lock(&opts->lock); if (opts->refcnt) { @@ -1491,7 +1491,7 @@ static ssize_t f_ss_opts_int_interval_store(struct f_ss_opts *opts, goto end; } - ret = kstrtou8(page, 0, &num); + ret = kstrtou32(page, 0, &num); if (ret) goto end; -- cgit v1.1 From 7ea95b110811fa8e41f5960c278bcfc80b8b21c1 Mon Sep 17 00:00:00 2001 From: Andrzej Pietrasiewicz Date: Tue, 9 Sep 2014 02:02:08 +0300 Subject: usb: gadget: uvc: rename functions to avoid conflicts with host uvc Prepare for separate compilation of uvc function's components. Some symbols will have to be exported, so rename to avoid conflicts with functions of the same name in host uvc. Signed-off-by: Andrzej Pietrasiewicz Tested-by: Michael Grzeschik [Rename uvc_video_pump and uvc_queue_head as well] [Rename forgotten uvc_queue_cancel instance in a comment] Signed-off-by: Laurent Pinchart Acked-by: Andrzej Pietrasiewicz Signed-off-by: Felipe Balbi --- drivers/usb/gadget/function/f_uvc.c | 2 +- drivers/usb/gadget/function/uvc_queue.c | 49 +++++++++++++++++---------------- drivers/usb/gadget/function/uvc_v4l2.c | 26 ++++++++--------- drivers/usb/gadget/function/uvc_video.c | 34 +++++++++++------------ 4 files changed, 56 insertions(+), 55 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/gadget/function/f_uvc.c b/drivers/usb/gadget/function/f_uvc.c index 187c3a0..ae5bcb4 100644 --- a/drivers/usb/gadget/function/f_uvc.c +++ b/drivers/usb/gadget/function/f_uvc.c @@ -693,7 +693,7 @@ uvc_function_bind(struct usb_configuration *c, struct usb_function *f) } /* Initialise video. */ - ret = uvc_video_init(&uvc->video); + ret = uvcg_video_init(&uvc->video); if (ret < 0) goto error; diff --git a/drivers/usb/gadget/function/uvc_queue.c b/drivers/usb/gadget/function/uvc_queue.c index 8590f9f..1e1bc73 100644 --- a/drivers/usb/gadget/function/uvc_queue.c +++ b/drivers/usb/gadget/function/uvc_queue.c @@ -28,7 +28,7 @@ /* ------------------------------------------------------------------------ * Video buffers queue management. * - * Video queues is initialized by uvc_queue_init(). The function performs + * Video queues is initialized by uvcg_queue_init(). The function performs * basic initialization of the uvc_video_queue struct and never fails. * * Video buffers are managed by videobuf2. The driver uses a mutex to protect @@ -126,8 +126,8 @@ static struct vb2_ops uvc_queue_qops = { .wait_finish = uvc_wait_finish, }; -static int uvc_queue_init(struct uvc_video_queue *queue, - enum v4l2_buf_type type) +static int uvcg_queue_init(struct uvc_video_queue *queue, + enum v4l2_buf_type type) { int ret; @@ -154,7 +154,7 @@ static int uvc_queue_init(struct uvc_video_queue *queue, /* * Free the video buffers. */ -static void uvc_free_buffers(struct uvc_video_queue *queue) +static void uvcg_free_buffers(struct uvc_video_queue *queue) { mutex_lock(&queue->mutex); vb2_queue_release(&queue->queue); @@ -164,8 +164,8 @@ static void uvc_free_buffers(struct uvc_video_queue *queue) /* * Allocate the video buffers. */ -static int uvc_alloc_buffers(struct uvc_video_queue *queue, - struct v4l2_requestbuffers *rb) +static int uvcg_alloc_buffers(struct uvc_video_queue *queue, + struct v4l2_requestbuffers *rb) { int ret; @@ -176,8 +176,8 @@ static int uvc_alloc_buffers(struct uvc_video_queue *queue, return ret ? ret : rb->count; } -static int uvc_query_buffer(struct uvc_video_queue *queue, - struct v4l2_buffer *buf) +static int uvcg_query_buffer(struct uvc_video_queue *queue, + struct v4l2_buffer *buf) { int ret; @@ -188,8 +188,8 @@ static int uvc_query_buffer(struct uvc_video_queue *queue, return ret; } -static int uvc_queue_buffer(struct uvc_video_queue *queue, - struct v4l2_buffer *buf) +static int uvcg_queue_buffer(struct uvc_video_queue *queue, + struct v4l2_buffer *buf) { unsigned long flags; int ret; @@ -213,8 +213,8 @@ done: * Dequeue a video buffer. If nonblocking is false, block until a buffer is * available. */ -static int uvc_dequeue_buffer(struct uvc_video_queue *queue, - struct v4l2_buffer *buf, int nonblocking) +static int uvcg_dequeue_buffer(struct uvc_video_queue *queue, + struct v4l2_buffer *buf, int nonblocking) { int ret; @@ -231,8 +231,8 @@ static int uvc_dequeue_buffer(struct uvc_video_queue *queue, * This function implements video queue polling and is intended to be used by * the device poll handler. */ -static unsigned int uvc_queue_poll(struct uvc_video_queue *queue, - struct file *file, poll_table *wait) +static unsigned int uvcg_queue_poll(struct uvc_video_queue *queue, + struct file *file, poll_table *wait) { unsigned int ret; @@ -243,8 +243,8 @@ static unsigned int uvc_queue_poll(struct uvc_video_queue *queue, return ret; } -static int uvc_queue_mmap(struct uvc_video_queue *queue, - struct vm_area_struct *vma) +static int uvcg_queue_mmap(struct uvc_video_queue *queue, + struct vm_area_struct *vma) { int ret; @@ -261,8 +261,9 @@ static int uvc_queue_mmap(struct uvc_video_queue *queue, * * NO-MMU arch need this function to make mmap() work correctly. */ -static unsigned long uvc_queue_get_unmapped_area(struct uvc_video_queue *queue, - unsigned long pgoff) +static unsigned long uvcg_queue_get_unmapped_area( + struct uvc_video_queue *queue, + unsigned long pgoff) { unsigned long ret; @@ -285,7 +286,7 @@ static unsigned long uvc_queue_get_unmapped_area(struct uvc_video_queue *queue, * This function acquires the irq spinlock and can be called from interrupt * context. */ -static void uvc_queue_cancel(struct uvc_video_queue *queue, int disconnect) +static void uvcg_queue_cancel(struct uvc_video_queue *queue, int disconnect) { struct uvc_buffer *buf; unsigned long flags; @@ -324,9 +325,9 @@ static void uvc_queue_cancel(struct uvc_video_queue *queue, int disconnect) * the main queue. * * This function can't be called from interrupt context. Use - * uvc_queue_cancel() instead. + * uvcg_queue_cancel() instead. */ -static int uvc_queue_enable(struct uvc_video_queue *queue, int enable) +static int uvcg_queue_enable(struct uvc_video_queue *queue, int enable) { unsigned long flags; int ret = 0; @@ -363,8 +364,8 @@ done: } /* called with &queue_irqlock held.. */ -static struct uvc_buffer *uvc_queue_next_buffer(struct uvc_video_queue *queue, - struct uvc_buffer *buf) +static struct uvc_buffer *uvcg_queue_next_buffer(struct uvc_video_queue *queue, + struct uvc_buffer *buf) { struct uvc_buffer *nextbuf; @@ -392,7 +393,7 @@ static struct uvc_buffer *uvc_queue_next_buffer(struct uvc_video_queue *queue, return nextbuf; } -static struct uvc_buffer *uvc_queue_head(struct uvc_video_queue *queue) +static struct uvc_buffer *uvcg_queue_head(struct uvc_video_queue *queue) { struct uvc_buffer *buf = NULL; diff --git a/drivers/usb/gadget/function/uvc_v4l2.c b/drivers/usb/gadget/function/uvc_v4l2.c index 14c3a37..c85730e 100644 --- a/drivers/usb/gadget/function/uvc_v4l2.c +++ b/drivers/usb/gadget/function/uvc_v4l2.c @@ -149,7 +149,7 @@ uvc_v4l2_reqbufs(struct file *file, void *fh, struct v4l2_requestbuffers *b) if (b->type != video->queue.queue.type) return -EINVAL; - return uvc_alloc_buffers(&video->queue, b); + return uvcg_alloc_buffers(&video->queue, b); } static int @@ -159,7 +159,7 @@ uvc_v4l2_querybuf(struct file *file, void *fh, struct v4l2_buffer *b) struct uvc_device *uvc = video_get_drvdata(vdev); struct uvc_video *video = &uvc->video; - return uvc_query_buffer(&video->queue, b); + return uvcg_query_buffer(&video->queue, b); } static int @@ -170,11 +170,11 @@ uvc_v4l2_qbuf(struct file *file, void *fh, struct v4l2_buffer *b) struct uvc_video *video = &uvc->video; int ret; - ret = uvc_queue_buffer(&video->queue, b); + ret = uvcg_queue_buffer(&video->queue, b); if (ret < 0) return ret; - return uvc_video_pump(video); + return uvcg_video_pump(video); } static int @@ -184,7 +184,7 @@ uvc_v4l2_dqbuf(struct file *file, void *fh, struct v4l2_buffer *b) struct uvc_device *uvc = video_get_drvdata(vdev); struct uvc_video *video = &uvc->video; - return uvc_dequeue_buffer(&video->queue, b, file->f_flags & O_NONBLOCK); + return uvcg_dequeue_buffer(&video->queue, b, file->f_flags & O_NONBLOCK); } static int @@ -199,7 +199,7 @@ uvc_v4l2_streamon(struct file *file, void *fh, enum v4l2_buf_type type) return -EINVAL; /* Enable UVC video. */ - ret = uvc_video_enable(video, 1); + ret = uvcg_video_enable(video, 1); if (ret < 0) return ret; @@ -223,7 +223,7 @@ uvc_v4l2_streamoff(struct file *file, void *fh, enum v4l2_buf_type type) if (type != video->queue.queue.type) return -EINVAL; - return uvc_video_enable(video, 0); + return uvcg_video_enable(video, 0); } static int @@ -309,8 +309,8 @@ uvc_v4l2_release(struct file *file) uvc_function_disconnect(uvc); - uvc_video_enable(video, 0); - uvc_free_buffers(&video->queue); + uvcg_video_enable(video, 0); + uvcg_free_buffers(&video->queue); file->private_data = NULL; v4l2_fh_del(&handle->vfh); @@ -326,7 +326,7 @@ uvc_v4l2_mmap(struct file *file, struct vm_area_struct *vma) struct video_device *vdev = video_devdata(file); struct uvc_device *uvc = video_get_drvdata(vdev); - return uvc_queue_mmap(&uvc->video.queue, vma); + return uvcg_queue_mmap(&uvc->video.queue, vma); } static unsigned int @@ -335,7 +335,7 @@ uvc_v4l2_poll(struct file *file, poll_table *wait) struct video_device *vdev = video_devdata(file); struct uvc_device *uvc = video_get_drvdata(vdev); - return uvc_queue_poll(&uvc->video.queue, file, wait); + return uvcg_queue_poll(&uvc->video.queue, file, wait); } #ifndef CONFIG_MMU @@ -346,7 +346,7 @@ static unsigned long uvc_v4l2_get_unmapped_area(struct file *file, struct video_device *vdev = video_devdata(file); struct uvc_device *uvc = video_get_drvdata(vdev); - return uvc_queue_get_unmapped_area(&uvc->video.queue, pgoff); + return uvcg_queue_get_unmapped_area(&uvc->video.queue, pgoff); } #endif @@ -358,7 +358,7 @@ static struct v4l2_file_operations uvc_v4l2_fops = { .mmap = uvc_v4l2_mmap, .poll = uvc_v4l2_poll, #ifndef CONFIG_MMU - .get_unmapped_area = uvc_v4l2_get_unmapped_area, + .get_unmapped_area = uvcg_v4l2_get_unmapped_area, #endif }; diff --git a/drivers/usb/gadget/function/uvc_video.c b/drivers/usb/gadget/function/uvc_video.c index 1ff478a..be6749e 100644 --- a/drivers/usb/gadget/function/uvc_video.c +++ b/drivers/usb/gadget/function/uvc_video.c @@ -85,7 +85,7 @@ uvc_video_encode_bulk(struct usb_request *req, struct uvc_video *video, if (buf->bytesused == video->queue.buf_used) { video->queue.buf_used = 0; buf->state = UVC_BUF_STATE_DONE; - uvc_queue_next_buffer(&video->queue, buf); + uvcg_queue_next_buffer(&video->queue, buf); video->fid ^= UVC_STREAM_FID; video->payload_size = 0; @@ -118,7 +118,7 @@ uvc_video_encode_isoc(struct usb_request *req, struct uvc_video *video, if (buf->bytesused == video->queue.buf_used) { video->queue.buf_used = 0; buf->state = UVC_BUF_STATE_DONE; - uvc_queue_next_buffer(&video->queue, buf); + uvcg_queue_next_buffer(&video->queue, buf); video->fid ^= UVC_STREAM_FID; } } @@ -172,18 +172,18 @@ uvc_video_complete(struct usb_ep *ep, struct usb_request *req) case -ESHUTDOWN: /* disconnect from host. */ printk(KERN_DEBUG "VS request cancelled.\n"); - uvc_queue_cancel(queue, 1); + uvcg_queue_cancel(queue, 1); goto requeue; default: printk(KERN_INFO "VS request completed with status %d.\n", req->status); - uvc_queue_cancel(queue, 0); + uvcg_queue_cancel(queue, 0); goto requeue; } spin_lock_irqsave(&video->queue.irqlock, flags); - buf = uvc_queue_head(&video->queue); + buf = uvcg_queue_head(&video->queue); if (buf == NULL) { spin_unlock_irqrestore(&video->queue.irqlock, flags); goto requeue; @@ -195,7 +195,7 @@ uvc_video_complete(struct usb_ep *ep, struct usb_request *req) printk(KERN_INFO "Failed to queue request (%d).\n", ret); usb_ep_set_halt(ep); spin_unlock_irqrestore(&video->queue.irqlock, flags); - uvc_queue_cancel(queue, 0); + uvcg_queue_cancel(queue, 0); goto requeue; } spin_unlock_irqrestore(&video->queue.irqlock, flags); @@ -274,13 +274,13 @@ error: */ /* - * uvc_video_pump - Pump video data into the USB requests + * uvcg_video_pump - Pump video data into the USB requests * * This function fills the available USB requests (listed in req_free) with * video data from the queued buffers. */ static int -uvc_video_pump(struct uvc_video *video) +uvcg_video_pump(struct uvc_video *video) { struct uvc_video_queue *queue = &video->queue; struct usb_request *req; @@ -288,7 +288,7 @@ uvc_video_pump(struct uvc_video *video) unsigned long flags; int ret; - /* FIXME TODO Race between uvc_video_pump and requests completion + /* FIXME TODO Race between uvcg_video_pump and requests completion * handler ??? */ @@ -310,7 +310,7 @@ uvc_video_pump(struct uvc_video *video) * request, protected by the video queue irqlock. */ spin_lock_irqsave(&video->queue.irqlock, flags); - buf = uvc_queue_head(&video->queue); + buf = uvcg_queue_head(&video->queue); if (buf == NULL) { spin_unlock_irqrestore(&video->queue.irqlock, flags); break; @@ -324,7 +324,7 @@ uvc_video_pump(struct uvc_video *video) printk(KERN_INFO "Failed to queue request (%d)\n", ret); usb_ep_set_halt(video->ep); spin_unlock_irqrestore(&video->queue.irqlock, flags); - uvc_queue_cancel(queue, 0); + uvcg_queue_cancel(queue, 0); break; } spin_unlock_irqrestore(&video->queue.irqlock, flags); @@ -340,7 +340,7 @@ uvc_video_pump(struct uvc_video *video) * Enable or disable the video stream. */ static int -uvc_video_enable(struct uvc_video *video, int enable) +uvcg_video_enable(struct uvc_video *video, int enable) { unsigned int i; int ret; @@ -356,11 +356,11 @@ uvc_video_enable(struct uvc_video *video, int enable) usb_ep_dequeue(video->ep, video->req[i]); uvc_video_free_requests(video); - uvc_queue_enable(&video->queue, 0); + uvcg_queue_enable(&video->queue, 0); return 0; } - if ((ret = uvc_queue_enable(&video->queue, 1)) < 0) + if ((ret = uvcg_queue_enable(&video->queue, 1)) < 0) return ret; if ((ret = uvc_video_alloc_requests(video)) < 0) @@ -372,14 +372,14 @@ uvc_video_enable(struct uvc_video *video, int enable) } else video->encode = uvc_video_encode_isoc; - return uvc_video_pump(video); + return uvcg_video_pump(video); } /* * Initialize the UVC video stream. */ static int -uvc_video_init(struct uvc_video *video) +uvcg_video_init(struct uvc_video *video) { INIT_LIST_HEAD(&video->req_free); spin_lock_init(&video->req_lock); @@ -391,7 +391,7 @@ uvc_video_init(struct uvc_video *video) video->imagesize = 320 * 240 * 2; /* Initialize the video buffers queue. */ - uvc_queue_init(&video->queue, V4L2_BUF_TYPE_VIDEO_OUTPUT); + uvcg_queue_init(&video->queue, V4L2_BUF_TYPE_VIDEO_OUTPUT); return 0; } -- cgit v1.1 From 3a83c16ef0e03e2ca2f1ce547a7cba53a62d0e0d Mon Sep 17 00:00:00 2001 From: Andrzej Pietrasiewicz Date: Tue, 9 Sep 2014 02:02:09 +0300 Subject: usb: gadget: uvc: separately compile some components of f_uvc Compile uvc_queue, uvc_v4l2, uvc_video separately so that later they can be all combined in a separately compiled f_uvc. Signed-off-by: Andrzej Pietrasiewicz Tested-by: Michael Grzeschik [Make uvc_v4l2_ioctl_ops non-static] [Rename __UVC__V4L2__H__ and __UVC__VIDEO__H__] [Update MAINTAINERS] Signed-off-by: Laurent Pinchart Acked-by: Andrzej Pietrasiewicz Signed-off-by: Felipe Balbi --- drivers/usb/gadget/function/f_uvc.c | 2 ++ drivers/usb/gadget/function/f_uvc.h | 8 +++++++ drivers/usb/gadget/function/uvc.h | 1 + drivers/usb/gadget/function/uvc_queue.c | 39 ++++++++++++++------------------- drivers/usb/gadget/function/uvc_queue.h | 33 ++++++++++++++++++++++++++++ drivers/usb/gadget/function/uvc_v4l2.c | 6 +++-- drivers/usb/gadget/function/uvc_v4l2.h | 22 +++++++++++++++++++ drivers/usb/gadget/function/uvc_video.c | 10 ++++----- drivers/usb/gadget/function/uvc_video.h | 24 ++++++++++++++++++++ drivers/usb/gadget/legacy/Makefile | 2 +- drivers/usb/gadget/legacy/webcam.c | 6 +---- 11 files changed, 117 insertions(+), 36 deletions(-) create mode 100644 drivers/usb/gadget/function/uvc_v4l2.h create mode 100644 drivers/usb/gadget/function/uvc_video.h (limited to 'drivers/usb') diff --git a/drivers/usb/gadget/function/f_uvc.c b/drivers/usb/gadget/function/f_uvc.c index ae5bcb4..825080d 100644 --- a/drivers/usb/gadget/function/f_uvc.c +++ b/drivers/usb/gadget/function/f_uvc.c @@ -27,6 +27,8 @@ #include #include "uvc.h" +#include "uvc_v4l2.h" +#include "uvc_video.h" unsigned int uvc_gadget_trace_param; static unsigned int streaming_interval; diff --git a/drivers/usb/gadget/function/f_uvc.h b/drivers/usb/gadget/function/f_uvc.h index 74b9602..71b38dd 100644 --- a/drivers/usb/gadget/function/f_uvc.h +++ b/drivers/usb/gadget/function/f_uvc.h @@ -16,6 +16,14 @@ #include #include +#include "uvc.h" + +void uvc_function_setup_continue(struct uvc_device *uvc); + +void uvc_function_connect(struct uvc_device *uvc); + +void uvc_function_disconnect(struct uvc_device *uvc); + int uvc_bind_config(struct usb_configuration *c, const struct uvc_descriptor_header * const *fs_control, const struct uvc_descriptor_header * const *hs_control, diff --git a/drivers/usb/gadget/function/uvc.h b/drivers/usb/gadget/function/uvc.h index 0a283b1..f67695c 100644 --- a/drivers/usb/gadget/function/uvc.h +++ b/drivers/usb/gadget/function/uvc.h @@ -53,6 +53,7 @@ struct uvc_event #ifdef __KERNEL__ #include /* For usb_endpoint_* */ +#include #include #include #include diff --git a/drivers/usb/gadget/function/uvc_queue.c b/drivers/usb/gadget/function/uvc_queue.c index 1e1bc73..8ea8b3b 100644 --- a/drivers/usb/gadget/function/uvc_queue.c +++ b/drivers/usb/gadget/function/uvc_queue.c @@ -126,8 +126,7 @@ static struct vb2_ops uvc_queue_qops = { .wait_finish = uvc_wait_finish, }; -static int uvcg_queue_init(struct uvc_video_queue *queue, - enum v4l2_buf_type type) +int uvcg_queue_init(struct uvc_video_queue *queue, enum v4l2_buf_type type) { int ret; @@ -154,7 +153,7 @@ static int uvcg_queue_init(struct uvc_video_queue *queue, /* * Free the video buffers. */ -static void uvcg_free_buffers(struct uvc_video_queue *queue) +void uvcg_free_buffers(struct uvc_video_queue *queue) { mutex_lock(&queue->mutex); vb2_queue_release(&queue->queue); @@ -164,7 +163,7 @@ static void uvcg_free_buffers(struct uvc_video_queue *queue) /* * Allocate the video buffers. */ -static int uvcg_alloc_buffers(struct uvc_video_queue *queue, +int uvcg_alloc_buffers(struct uvc_video_queue *queue, struct v4l2_requestbuffers *rb) { int ret; @@ -176,8 +175,7 @@ static int uvcg_alloc_buffers(struct uvc_video_queue *queue, return ret ? ret : rb->count; } -static int uvcg_query_buffer(struct uvc_video_queue *queue, - struct v4l2_buffer *buf) +int uvcg_query_buffer(struct uvc_video_queue *queue, struct v4l2_buffer *buf) { int ret; @@ -188,8 +186,7 @@ static int uvcg_query_buffer(struct uvc_video_queue *queue, return ret; } -static int uvcg_queue_buffer(struct uvc_video_queue *queue, - struct v4l2_buffer *buf) +int uvcg_queue_buffer(struct uvc_video_queue *queue, struct v4l2_buffer *buf) { unsigned long flags; int ret; @@ -213,8 +210,8 @@ done: * Dequeue a video buffer. If nonblocking is false, block until a buffer is * available. */ -static int uvcg_dequeue_buffer(struct uvc_video_queue *queue, - struct v4l2_buffer *buf, int nonblocking) +int uvcg_dequeue_buffer(struct uvc_video_queue *queue, struct v4l2_buffer *buf, + int nonblocking) { int ret; @@ -231,8 +228,8 @@ static int uvcg_dequeue_buffer(struct uvc_video_queue *queue, * This function implements video queue polling and is intended to be used by * the device poll handler. */ -static unsigned int uvcg_queue_poll(struct uvc_video_queue *queue, - struct file *file, poll_table *wait) +unsigned int uvcg_queue_poll(struct uvc_video_queue *queue, struct file *file, + poll_table *wait) { unsigned int ret; @@ -243,8 +240,7 @@ static unsigned int uvcg_queue_poll(struct uvc_video_queue *queue, return ret; } -static int uvcg_queue_mmap(struct uvc_video_queue *queue, - struct vm_area_struct *vma) +int uvcg_queue_mmap(struct uvc_video_queue *queue, struct vm_area_struct *vma) { int ret; @@ -261,9 +257,8 @@ static int uvcg_queue_mmap(struct uvc_video_queue *queue, * * NO-MMU arch need this function to make mmap() work correctly. */ -static unsigned long uvcg_queue_get_unmapped_area( - struct uvc_video_queue *queue, - unsigned long pgoff) +unsigned long uvcg_queue_get_unmapped_area(struct uvc_video_queue *queue, + unsigned long pgoff) { unsigned long ret; @@ -286,7 +281,7 @@ static unsigned long uvcg_queue_get_unmapped_area( * This function acquires the irq spinlock and can be called from interrupt * context. */ -static void uvcg_queue_cancel(struct uvc_video_queue *queue, int disconnect) +void uvcg_queue_cancel(struct uvc_video_queue *queue, int disconnect) { struct uvc_buffer *buf; unsigned long flags; @@ -327,7 +322,7 @@ static void uvcg_queue_cancel(struct uvc_video_queue *queue, int disconnect) * This function can't be called from interrupt context. Use * uvcg_queue_cancel() instead. */ -static int uvcg_queue_enable(struct uvc_video_queue *queue, int enable) +int uvcg_queue_enable(struct uvc_video_queue *queue, int enable) { unsigned long flags; int ret = 0; @@ -364,8 +359,8 @@ done: } /* called with &queue_irqlock held.. */ -static struct uvc_buffer *uvcg_queue_next_buffer(struct uvc_video_queue *queue, - struct uvc_buffer *buf) +struct uvc_buffer *uvcg_queue_next_buffer(struct uvc_video_queue *queue, + struct uvc_buffer *buf) { struct uvc_buffer *nextbuf; @@ -393,7 +388,7 @@ static struct uvc_buffer *uvcg_queue_next_buffer(struct uvc_video_queue *queue, return nextbuf; } -static struct uvc_buffer *uvcg_queue_head(struct uvc_video_queue *queue) +struct uvc_buffer *uvcg_queue_head(struct uvc_video_queue *queue) { struct uvc_buffer *buf = NULL; diff --git a/drivers/usb/gadget/function/uvc_queue.h b/drivers/usb/gadget/function/uvc_queue.h index 8e76ce9..03919c7 100644 --- a/drivers/usb/gadget/function/uvc_queue.h +++ b/drivers/usb/gadget/function/uvc_queue.h @@ -57,6 +57,39 @@ static inline int uvc_queue_streaming(struct uvc_video_queue *queue) return vb2_is_streaming(&queue->queue); } +int uvcg_queue_init(struct uvc_video_queue *queue, enum v4l2_buf_type type); + +void uvcg_free_buffers(struct uvc_video_queue *queue); + +int uvcg_alloc_buffers(struct uvc_video_queue *queue, + struct v4l2_requestbuffers *rb); + +int uvcg_query_buffer(struct uvc_video_queue *queue, struct v4l2_buffer *buf); + +int uvcg_queue_buffer(struct uvc_video_queue *queue, struct v4l2_buffer *buf); + +int uvcg_dequeue_buffer(struct uvc_video_queue *queue, + struct v4l2_buffer *buf, int nonblocking); + +unsigned int uvcg_queue_poll(struct uvc_video_queue *queue, + struct file *file, poll_table *wait); + +int uvcg_queue_mmap(struct uvc_video_queue *queue, struct vm_area_struct *vma); + +#ifndef CONFIG_MMU +unsigned long uvcg_queue_get_unmapped_area(struct uvc_video_queue *queue, + unsigned long pgoff); +#endif /* CONFIG_MMU */ + +void uvcg_queue_cancel(struct uvc_video_queue *queue, int disconnect); + +int uvcg_queue_enable(struct uvc_video_queue *queue, int enable); + +struct uvc_buffer *uvcg_queue_next_buffer(struct uvc_video_queue *queue, + struct uvc_buffer *buf); + +struct uvc_buffer *uvcg_queue_head(struct uvc_video_queue *queue); + #endif /* __KERNEL__ */ #endif /* _UVC_QUEUE_H_ */ diff --git a/drivers/usb/gadget/function/uvc_v4l2.c b/drivers/usb/gadget/function/uvc_v4l2.c index c85730e..b52f268 100644 --- a/drivers/usb/gadget/function/uvc_v4l2.c +++ b/drivers/usb/gadget/function/uvc_v4l2.c @@ -23,8 +23,10 @@ #include #include +#include "f_uvc.h" #include "uvc.h" #include "uvc_queue.h" +#include "uvc_video.h" /* -------------------------------------------------------------------------- * Requests handling @@ -259,7 +261,7 @@ uvc_v4l2_ioctl_default(struct file *file, void *fh, bool valid_prio, } } -static const struct v4l2_ioctl_ops uvc_v4l2_ioctl_ops = { +const struct v4l2_ioctl_ops uvc_v4l2_ioctl_ops = { .vidioc_querycap = uvc_v4l2_querycap, .vidioc_g_fmt_vid_out = uvc_v4l2_get_format, .vidioc_s_fmt_vid_out = uvc_v4l2_set_format, @@ -350,7 +352,7 @@ static unsigned long uvc_v4l2_get_unmapped_area(struct file *file, } #endif -static struct v4l2_file_operations uvc_v4l2_fops = { +struct v4l2_file_operations uvc_v4l2_fops = { .owner = THIS_MODULE, .open = uvc_v4l2_open, .release = uvc_v4l2_release, diff --git a/drivers/usb/gadget/function/uvc_v4l2.h b/drivers/usb/gadget/function/uvc_v4l2.h new file mode 100644 index 0000000..2683b92 --- /dev/null +++ b/drivers/usb/gadget/function/uvc_v4l2.h @@ -0,0 +1,22 @@ +/* + * uvc_v4l2.h -- USB Video Class Gadget driver + * + * Copyright (C) 2009-2010 + * Laurent Pinchart (laurent.pinchart@ideasonboard.com) + * + * Copyright (c) 2013 Samsung Electronics Co., Ltd. + * http://www.samsung.com + * Author: Andrzej Pietrasiewicz + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef __UVC_V4L2_H__ +#define __UVC_V4L2_H__ + +extern const struct v4l2_ioctl_ops uvc_v4l2_ioctl_ops; +extern struct v4l2_file_operations uvc_v4l2_fops; + +#endif /* __UVC_V4L2_H__ */ diff --git a/drivers/usb/gadget/function/uvc_video.c b/drivers/usb/gadget/function/uvc_video.c index be6749e..b0528e8 100644 --- a/drivers/usb/gadget/function/uvc_video.c +++ b/drivers/usb/gadget/function/uvc_video.c @@ -15,6 +15,7 @@ #include #include #include +#include #include @@ -279,8 +280,7 @@ error: * This function fills the available USB requests (listed in req_free) with * video data from the queued buffers. */ -static int -uvcg_video_pump(struct uvc_video *video) +int uvcg_video_pump(struct uvc_video *video) { struct uvc_video_queue *queue = &video->queue; struct usb_request *req; @@ -339,8 +339,7 @@ uvcg_video_pump(struct uvc_video *video) /* * Enable or disable the video stream. */ -static int -uvcg_video_enable(struct uvc_video *video, int enable) +int uvcg_video_enable(struct uvc_video *video, int enable) { unsigned int i; int ret; @@ -378,8 +377,7 @@ uvcg_video_enable(struct uvc_video *video, int enable) /* * Initialize the UVC video stream. */ -static int -uvcg_video_init(struct uvc_video *video) +int uvcg_video_init(struct uvc_video *video) { INIT_LIST_HEAD(&video->req_free); spin_lock_init(&video->req_lock); diff --git a/drivers/usb/gadget/function/uvc_video.h b/drivers/usb/gadget/function/uvc_video.h new file mode 100644 index 0000000..ef00f06 --- /dev/null +++ b/drivers/usb/gadget/function/uvc_video.h @@ -0,0 +1,24 @@ +/* + * uvc_video.h -- USB Video Class Gadget driver + * + * Copyright (C) 2009-2010 + * Laurent Pinchart (laurent.pinchart@ideasonboard.com) + * + * Copyright (c) 2013 Samsung Electronics Co., Ltd. + * http://www.samsung.com + * Author: Andrzej Pietrasiewicz + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ +#ifndef __UVC_VIDEO_H__ +#define __UVC_VIDEO_H__ + +int uvcg_video_pump(struct uvc_video *video); + +int uvcg_video_enable(struct uvc_video *video, int enable); + +int uvcg_video_init(struct uvc_video *video); + +#endif /* __UVC_VIDEO_H__ */ diff --git a/drivers/usb/gadget/legacy/Makefile b/drivers/usb/gadget/legacy/Makefile index 7f485f2..ed7367e 100644 --- a/drivers/usb/gadget/legacy/Makefile +++ b/drivers/usb/gadget/legacy/Makefile @@ -19,7 +19,7 @@ g_multi-y := multi.o g_hid-y := hid.o g_dbgp-y := dbgp.o g_nokia-y := nokia.o -g_webcam-y := webcam.o +g_webcam-y := webcam.o ../function/uvc_queue.o ../function/uvc_v4l2.o ../function/uvc_video.o g_ncm-y := ncm.o g_acm_ms-y := acm_ms.o g_tcm_usb_gadget-y := tcm_usb_gadget.o diff --git a/drivers/usb/gadget/legacy/webcam.c b/drivers/usb/gadget/legacy/webcam.c index f826622..5d02849 100644 --- a/drivers/usb/gadget/legacy/webcam.c +++ b/drivers/usb/gadget/legacy/webcam.c @@ -12,10 +12,9 @@ #include #include +#include #include -#include "f_uvc.h" - /* * Kbuild is not very cooperative with respect to linking separately * compiled library objects into one module. So for now we won't use @@ -23,9 +22,6 @@ * the runtime footprint, and giving us at least some parts of what * a "gcc --combine ... part1.c part2.c part3.c ... " build would. */ -#include "uvc_queue.c" -#include "uvc_video.c" -#include "uvc_v4l2.c" #include "f_uvc.c" USB_GADGET_COMPOSITE_OPTIONS(); -- cgit v1.1 From 6d11ed76c45dd7c8322c2d03575f2164cc725c18 Mon Sep 17 00:00:00 2001 From: Andrzej Pietrasiewicz Date: Tue, 9 Sep 2014 02:02:10 +0300 Subject: usb: gadget: f_uvc: convert f_uvc to new function interface Use the new function registration interface. It is required in order to integrate configfs support. Signed-off-by: Andrzej Pietrasiewicz Tested-by: Michael Grzeschik [Updated copyright years] Signed-off-by: Laurent Pinchart Acked-by: Andrzej Pietrasiewicz Signed-off-by: Felipe Balbi --- drivers/usb/gadget/Kconfig | 3 + drivers/usb/gadget/function/Makefile | 2 + drivers/usb/gadget/function/f_uvc.c | 204 +++++++++++++++++++++++++++++------ drivers/usb/gadget/function/u_uvc.h | 39 +++++++ drivers/usb/gadget/legacy/webcam.c | 1 + 5 files changed, 219 insertions(+), 30 deletions(-) create mode 100644 drivers/usb/gadget/function/u_uvc.h (limited to 'drivers/usb') diff --git a/drivers/usb/gadget/Kconfig b/drivers/usb/gadget/Kconfig index 68302aa..c4880fc 100644 --- a/drivers/usb/gadget/Kconfig +++ b/drivers/usb/gadget/Kconfig @@ -187,6 +187,9 @@ config USB_F_UAC1 config USB_F_UAC2 tristate +config USB_F_UVC + tristate + choice tristate "USB Gadget Drivers" default USB_ETH diff --git a/drivers/usb/gadget/function/Makefile b/drivers/usb/gadget/function/Makefile index a49fdf7b..90701aa 100644 --- a/drivers/usb/gadget/function/Makefile +++ b/drivers/usb/gadget/function/Makefile @@ -36,3 +36,5 @@ usb_f_uac1-y := f_uac1.o u_uac1.o obj-$(CONFIG_USB_F_UAC1) += usb_f_uac1.o usb_f_uac2-y := f_uac2.o obj-$(CONFIG_USB_F_UAC2) += usb_f_uac2.o +usb_f_uvc-y := f_uvc.o uvc_queue.o uvc_v4l2.o uvc_video.o +obj-$(CONFIG_USB_F_UVC) += usb_f_uvc.o diff --git a/drivers/usb/gadget/function/f_uvc.c b/drivers/usb/gadget/function/f_uvc.c index 825080d..fe50a9b 100644 --- a/drivers/usb/gadget/function/f_uvc.c +++ b/drivers/usb/gadget/function/f_uvc.c @@ -11,6 +11,7 @@ */ #include +#include #include #include #include @@ -29,11 +30,14 @@ #include "uvc.h" #include "uvc_v4l2.h" #include "uvc_video.h" +#include "u_uvc.h" unsigned int uvc_gadget_trace_param; +#ifdef USBF_UVC_INCLUDED static unsigned int streaming_interval; static unsigned int streaming_maxpacket; static unsigned int streaming_maxburst; +#endif /* -------------------------------------------------------------------------- * Function descriptors @@ -65,7 +69,7 @@ static struct usb_gadget_strings *uvc_function_strings[] = { #define UVC_STATUS_MAX_PACKET_SIZE 16 /* 16 bytes status */ -static struct usb_interface_assoc_descriptor uvc_iad __initdata = { +static struct usb_interface_assoc_descriptor uvc_iad = { .bLength = sizeof(uvc_iad), .bDescriptorType = USB_DT_INTERFACE_ASSOCIATION, .bFirstInterface = 0, @@ -76,7 +80,7 @@ static struct usb_interface_assoc_descriptor uvc_iad __initdata = { .iFunction = 0, }; -static struct usb_interface_descriptor uvc_control_intf __initdata = { +static struct usb_interface_descriptor uvc_control_intf = { .bLength = USB_DT_INTERFACE_SIZE, .bDescriptorType = USB_DT_INTERFACE, .bInterfaceNumber = UVC_INTF_VIDEO_CONTROL, @@ -88,7 +92,7 @@ static struct usb_interface_descriptor uvc_control_intf __initdata = { .iInterface = 0, }; -static struct usb_endpoint_descriptor uvc_control_ep __initdata = { +static struct usb_endpoint_descriptor uvc_control_ep = { .bLength = USB_DT_ENDPOINT_SIZE, .bDescriptorType = USB_DT_ENDPOINT, .bEndpointAddress = USB_DIR_IN, @@ -97,7 +101,7 @@ static struct usb_endpoint_descriptor uvc_control_ep __initdata = { .bInterval = 8, }; -static struct usb_ss_ep_comp_descriptor uvc_ss_control_comp __initdata = { +static struct usb_ss_ep_comp_descriptor uvc_ss_control_comp = { .bLength = sizeof(uvc_ss_control_comp), .bDescriptorType = USB_DT_SS_ENDPOINT_COMP, /* The following 3 values can be tweaked if necessary. */ @@ -106,14 +110,14 @@ static struct usb_ss_ep_comp_descriptor uvc_ss_control_comp __initdata = { .wBytesPerInterval = cpu_to_le16(UVC_STATUS_MAX_PACKET_SIZE), }; -static struct uvc_control_endpoint_descriptor uvc_control_cs_ep __initdata = { +static struct uvc_control_endpoint_descriptor uvc_control_cs_ep = { .bLength = UVC_DT_CONTROL_ENDPOINT_SIZE, .bDescriptorType = USB_DT_CS_ENDPOINT, .bDescriptorSubType = UVC_EP_INTERRUPT, .wMaxTransferSize = cpu_to_le16(UVC_STATUS_MAX_PACKET_SIZE), }; -static struct usb_interface_descriptor uvc_streaming_intf_alt0 __initdata = { +static struct usb_interface_descriptor uvc_streaming_intf_alt0 = { .bLength = USB_DT_INTERFACE_SIZE, .bDescriptorType = USB_DT_INTERFACE, .bInterfaceNumber = UVC_INTF_VIDEO_STREAMING, @@ -125,7 +129,7 @@ static struct usb_interface_descriptor uvc_streaming_intf_alt0 __initdata = { .iInterface = 0, }; -static struct usb_interface_descriptor uvc_streaming_intf_alt1 __initdata = { +static struct usb_interface_descriptor uvc_streaming_intf_alt1 = { .bLength = USB_DT_INTERFACE_SIZE, .bDescriptorType = USB_DT_INTERFACE, .bInterfaceNumber = UVC_INTF_VIDEO_STREAMING, @@ -137,7 +141,7 @@ static struct usb_interface_descriptor uvc_streaming_intf_alt1 __initdata = { .iInterface = 0, }; -static struct usb_endpoint_descriptor uvc_fs_streaming_ep __initdata = { +static struct usb_endpoint_descriptor uvc_fs_streaming_ep = { .bLength = USB_DT_ENDPOINT_SIZE, .bDescriptorType = USB_DT_ENDPOINT, .bEndpointAddress = USB_DIR_IN, @@ -148,7 +152,7 @@ static struct usb_endpoint_descriptor uvc_fs_streaming_ep __initdata = { */ }; -static struct usb_endpoint_descriptor uvc_hs_streaming_ep __initdata = { +static struct usb_endpoint_descriptor uvc_hs_streaming_ep = { .bLength = USB_DT_ENDPOINT_SIZE, .bDescriptorType = USB_DT_ENDPOINT, .bEndpointAddress = USB_DIR_IN, @@ -159,7 +163,7 @@ static struct usb_endpoint_descriptor uvc_hs_streaming_ep __initdata = { */ }; -static struct usb_endpoint_descriptor uvc_ss_streaming_ep __initdata = { +static struct usb_endpoint_descriptor uvc_ss_streaming_ep = { .bLength = USB_DT_ENDPOINT_SIZE, .bDescriptorType = USB_DT_ENDPOINT, @@ -171,7 +175,7 @@ static struct usb_endpoint_descriptor uvc_ss_streaming_ep __initdata = { */ }; -static struct usb_ss_ep_comp_descriptor uvc_ss_streaming_comp __initdata = { +static struct usb_ss_ep_comp_descriptor uvc_ss_streaming_comp = { .bLength = sizeof(uvc_ss_streaming_comp), .bDescriptorType = USB_DT_SS_ENDPOINT_COMP, /* The bMaxBurst, bmAttributes and wBytesPerInterval values will be @@ -198,6 +202,16 @@ static const struct usb_descriptor_header * const uvc_ss_streaming[] = { NULL, }; +#ifndef USBF_UVC_INCLUDED + +void uvc_set_trace_param(unsigned int trace) +{ + uvc_gadget_trace_param = trace; +} +EXPORT_SYMBOL(uvc_set_trace_param); + +#endif + /* -------------------------------------------------------------------------- * Control requests */ @@ -432,7 +446,7 @@ uvc_register_video(struct uvc_device *uvc) } \ } while (0) -static struct usb_descriptor_header ** __init +static struct usb_descriptor_header ** uvc_copy_descriptors(struct uvc_device *uvc, enum usb_device_speed speed) { struct uvc_input_header_descriptor *uvc_streaming_header; @@ -552,6 +566,7 @@ uvc_copy_descriptors(struct uvc_device *uvc, enum usb_device_speed speed) return hdr; } +#ifdef USBF_UVC_INCLUDED static void uvc_function_unbind(struct usb_configuration *c, struct usb_function *f) { @@ -573,8 +588,9 @@ uvc_function_unbind(struct usb_configuration *c, struct usb_function *f) kfree(uvc); } +#endif -static int __init +static int uvc_function_bind(struct usb_configuration *c, struct usb_function *f) { struct usb_composite_dev *cdev = c->cdev; @@ -582,10 +598,14 @@ uvc_function_bind(struct usb_configuration *c, struct usb_function *f) unsigned int max_packet_mult; unsigned int max_packet_size; struct usb_ep *ep; +#ifndef USBF_UVC_INCLUDED + struct f_uvc_opts *opts; +#endif int ret = -EINVAL; INFO(cdev, "uvc_function_bind\n"); +#ifdef USBF_UVC_INCLUDED /* Sanity check the streaming endpoint module parameters. */ streaming_interval = clamp(streaming_interval, 1U, 16U); @@ -622,6 +642,46 @@ uvc_function_bind(struct usb_configuration *c, struct usb_function *f) uvc_ss_streaming_comp.bMaxBurst = streaming_maxburst; uvc_ss_streaming_comp.wBytesPerInterval = max_packet_size * max_packet_mult * streaming_maxburst; +#else + opts = to_f_uvc_opts(f->fi); + /* Sanity check the streaming endpoint module parameters. + */ + opts->streaming_interval = clamp(opts->streaming_interval, 1U, 16U); + opts->streaming_maxpacket = clamp(opts->streaming_maxpacket, 1U, 3072U); + opts->streaming_maxburst = min(opts->streaming_maxburst, 15U); + + /* Fill in the FS/HS/SS Video Streaming specific descriptors from the + * module parameters. + * + * NOTE: We assume that the user knows what they are doing and won't + * give parameters that their UDC doesn't support. + */ + if (opts->streaming_maxpacket <= 1024) { + max_packet_mult = 1; + max_packet_size = opts->streaming_maxpacket; + } else if (opts->streaming_maxpacket <= 2048) { + max_packet_mult = 2; + max_packet_size = opts->streaming_maxpacket / 2; + } else { + max_packet_mult = 3; + max_packet_size = opts->streaming_maxpacket / 3; + } + + uvc_fs_streaming_ep.wMaxPacketSize = + min(opts->streaming_maxpacket, 1023U); + uvc_fs_streaming_ep.bInterval = opts->streaming_interval; + + uvc_hs_streaming_ep.wMaxPacketSize = max_packet_size; + uvc_hs_streaming_ep.wMaxPacketSize |= ((max_packet_mult - 1) << 11); + uvc_hs_streaming_ep.bInterval = opts->streaming_interval; + + uvc_ss_streaming_ep.wMaxPacketSize = max_packet_size; + uvc_ss_streaming_ep.bInterval = opts->streaming_interval; + uvc_ss_streaming_comp.bmAttributes = max_packet_mult - 1; + uvc_ss_streaming_comp.bMaxBurst = opts->streaming_maxburst; + uvc_ss_streaming_comp.wBytesPerInterval = + max_packet_size * max_packet_mult * opts->streaming_maxburst; +#endif /* Allocate endpoints. */ ep = usb_ep_autoconfig(cdev->gadget, &uvc_control_ep); @@ -651,6 +711,23 @@ uvc_function_bind(struct usb_configuration *c, struct usb_function *f) uvc_hs_streaming_ep.bEndpointAddress = uvc->video.ep->address; uvc_ss_streaming_ep.bEndpointAddress = uvc->video.ep->address; + /* String descriptors are global, we only need to allocate string IDs + * for the first UVC function. UVC functions beyond the first (if any) + * will reuse the same IDs. + */ + if (uvc_en_us_strings[UVC_STRING_CONTROL_IDX].id == 0) { + ret = usb_string_ids_tab(c->cdev, uvc_en_us_strings); + if (ret) + goto error; + uvc_iad.iFunction = + uvc_en_us_strings[UVC_STRING_CONTROL_IDX].id; + uvc_control_intf.iInterface = + uvc_en_us_strings[UVC_STRING_CONTROL_IDX].id; + ret = uvc_en_us_strings[UVC_STRING_STREAMING_IDX].id; + uvc_streaming_intf_alt0.iInterface = ret; + uvc_streaming_intf_alt1.iInterface = ret; + } + /* Allocate interface IDs. */ if ((ret = usb_interface_id(c, f)) < 0) goto error; @@ -730,6 +807,7 @@ error: * USB gadget function */ +#ifdef USBF_UVC_INCLUDED /** * uvc_bind_config - add a UVC function to a configuration * @c: the configuration to support the UVC instance @@ -796,23 +874,6 @@ uvc_bind_config(struct usb_configuration *c, uvc->desc.hs_streaming = hs_streaming; uvc->desc.ss_streaming = ss_streaming; - /* String descriptors are global, we only need to allocate string IDs - * for the first UVC function. UVC functions beyond the first (if any) - * will reuse the same IDs. - */ - if (uvc_en_us_strings[UVC_STRING_CONTROL_IDX].id == 0) { - ret = usb_string_ids_tab(c->cdev, uvc_en_us_strings); - if (ret) - goto error; - uvc_iad.iFunction = - uvc_en_us_strings[UVC_STRING_CONTROL_IDX].id; - uvc_control_intf.iInterface = - uvc_en_us_strings[UVC_STRING_CONTROL_IDX].id; - ret = uvc_en_us_strings[UVC_STRING_STREAMING_IDX].id; - uvc_streaming_intf_alt0.iInterface = ret; - uvc_streaming_intf_alt1.iInterface = ret; - } - /* Register the function. */ uvc->func.name = "uvc"; uvc->func.strings = uvc_function_strings; @@ -834,4 +895,87 @@ error: return ret; } +#else + +static void uvc_free_inst(struct usb_function_instance *f) +{ + struct f_uvc_opts *opts = to_f_uvc_opts(f); + + kfree(opts); +} + +static struct usb_function_instance *uvc_alloc_inst(void) +{ + struct f_uvc_opts *opts; + + opts = kzalloc(sizeof(*opts), GFP_KERNEL); + if (!opts) + return ERR_PTR(-ENOMEM); + opts->func_inst.free_func_inst = uvc_free_inst; + + return &opts->func_inst; +} + +static void uvc_free(struct usb_function *f) +{ + struct uvc_device *uvc = to_uvc(f); + + kfree(uvc); +} + +static void uvc_unbind(struct usb_configuration *c, struct usb_function *f) +{ + struct usb_composite_dev *cdev = c->cdev; + struct uvc_device *uvc = to_uvc(f); + + INFO(cdev, "%s\n", __func__); + + video_unregister_device(uvc->vdev); + v4l2_device_unregister(&uvc->v4l2_dev); + uvc->control_ep->driver_data = NULL; + uvc->video.ep->driver_data = NULL; + + uvc_en_us_strings[UVC_STRING_CONTROL_IDX].id = 0; + usb_ep_free_request(cdev->gadget->ep0, uvc->control_req); + kfree(uvc->control_buf); + + usb_free_all_descriptors(f); +} + +struct usb_function *uvc_alloc(struct usb_function_instance *fi) +{ + struct uvc_device *uvc; + struct f_uvc_opts *opts; + + uvc = kzalloc(sizeof(*uvc), GFP_KERNEL); + if (uvc == NULL) + return ERR_PTR(-ENOMEM); + + uvc->state = UVC_STATE_DISCONNECTED; + opts = to_f_uvc_opts(fi); + + uvc->desc.fs_control = opts->fs_control; + uvc->desc.ss_control = opts->ss_control; + uvc->desc.fs_streaming = opts->fs_streaming; + uvc->desc.hs_streaming = opts->hs_streaming; + uvc->desc.ss_streaming = opts->ss_streaming; + + /* Register the function. */ + uvc->func.name = "uvc"; + uvc->func.strings = uvc_function_strings; + uvc->func.bind = uvc_function_bind; + uvc->func.unbind = uvc_unbind; + uvc->func.get_alt = uvc_function_get_alt; + uvc->func.set_alt = uvc_function_set_alt; + uvc->func.disable = uvc_function_disable; + uvc->func.setup = uvc_function_setup; + uvc->func.free_func = uvc_free; + + return &uvc->func; +} + +DECLARE_USB_FUNCTION_INIT(uvc, uvc_alloc_inst, uvc_alloc); +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Laurent Pinchart"); +#endif diff --git a/drivers/usb/gadget/function/u_uvc.h b/drivers/usb/gadget/function/u_uvc.h new file mode 100644 index 0000000..2a8dfdf --- /dev/null +++ b/drivers/usb/gadget/function/u_uvc.h @@ -0,0 +1,39 @@ +/* + * u_uvc.h + * + * Utility definitions for the uvc function + * + * Copyright (c) 2013-2014 Samsung Electronics Co., Ltd. + * http://www.samsung.com + * + * Author: Andrzej Pietrasiewicz + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef U_UVC_H +#define U_UVC_H + +#include + +#define to_f_uvc_opts(f) container_of(f, struct f_uvc_opts, func_inst) + +struct f_uvc_opts { + struct usb_function_instance func_inst; + unsigned int uvc_gadget_trace_param; + unsigned int streaming_interval; + unsigned int streaming_maxpacket; + unsigned int streaming_maxburst; + const struct uvc_descriptor_header * const *fs_control; + const struct uvc_descriptor_header * const *ss_control; + const struct uvc_descriptor_header * const *fs_streaming; + const struct uvc_descriptor_header * const *hs_streaming; + const struct uvc_descriptor_header * const *ss_streaming; +}; + +void uvc_set_trace_param(unsigned int trace); + +#endif /* U_UVC_H */ + diff --git a/drivers/usb/gadget/legacy/webcam.c b/drivers/usb/gadget/legacy/webcam.c index 5d02849..50d27db 100644 --- a/drivers/usb/gadget/legacy/webcam.c +++ b/drivers/usb/gadget/legacy/webcam.c @@ -22,6 +22,7 @@ * the runtime footprint, and giving us at least some parts of what * a "gcc --combine ... part1.c part2.c part3.c ... " build would. */ +#define USBF_UVC_INCLUDED #include "f_uvc.c" USB_GADGET_COMPOSITE_OPTIONS(); -- cgit v1.1 From c913881ec6f5d17defd16dfd96fea576b17c04b9 Mon Sep 17 00:00:00 2001 From: Andrzej Pietrasiewicz Date: Tue, 9 Sep 2014 02:02:11 +0300 Subject: usb: gadget: webcam: convert webcam to new interface of f_uvc Use the new function interface of f_uvc. Signed-off-by: Andrzej Pietrasiewicz Tested-by: Michael Grzeschik Signed-off-by: Laurent Pinchart Signed-off-by: Felipe Balbi --- drivers/usb/gadget/legacy/Kconfig | 1 + drivers/usb/gadget/legacy/Makefile | 2 +- drivers/usb/gadget/legacy/webcam.c | 53 +++++++++++++++++++++++++++----------- 3 files changed, 40 insertions(+), 16 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/gadget/legacy/Kconfig b/drivers/usb/gadget/legacy/Kconfig index bbd4b85..24392d2 100644 --- a/drivers/usb/gadget/legacy/Kconfig +++ b/drivers/usb/gadget/legacy/Kconfig @@ -468,6 +468,7 @@ config USB_G_WEBCAM depends on VIDEO_DEV select USB_LIBCOMPOSITE select VIDEOBUF2_VMALLOC + select USB_F_UVC help The Webcam Gadget acts as a composite USB Audio and Video Class device. It provides a userspace API to process UVC control requests diff --git a/drivers/usb/gadget/legacy/Makefile b/drivers/usb/gadget/legacy/Makefile index ed7367e..7f485f2 100644 --- a/drivers/usb/gadget/legacy/Makefile +++ b/drivers/usb/gadget/legacy/Makefile @@ -19,7 +19,7 @@ g_multi-y := multi.o g_hid-y := hid.o g_dbgp-y := dbgp.o g_nokia-y := nokia.o -g_webcam-y := webcam.o ../function/uvc_queue.o ../function/uvc_v4l2.o ../function/uvc_video.o +g_webcam-y := webcam.o g_ncm-y := ncm.o g_acm_ms-y := acm_ms.o g_tcm_usb_gadget-y := tcm_usb_gadget.o diff --git a/drivers/usb/gadget/legacy/webcam.c b/drivers/usb/gadget/legacy/webcam.c index 50d27db..04a3da2 100644 --- a/drivers/usb/gadget/legacy/webcam.c +++ b/drivers/usb/gadget/legacy/webcam.c @@ -15,15 +15,7 @@ #include #include -/* - * Kbuild is not very cooperative with respect to linking separately - * compiled library objects into one module. So for now we won't use - * separate compilation ... ensuring init/exit sections work to shrink - * the runtime footprint, and giving us at least some parts of what - * a "gcc --combine ... part1.c part2.c part3.c ... " build would. - */ -#define USBF_UVC_INCLUDED -#include "f_uvc.c" +#include "u_uvc.h" USB_GADGET_COMPOSITE_OPTIONS(); @@ -79,6 +71,9 @@ static struct usb_gadget_strings *webcam_device_strings[] = { NULL, }; +static struct usb_function_instance *fi_uvc; +static struct usb_function *f_uvc; + static struct usb_device_descriptor webcam_device_descriptor = { .bLength = USB_DT_DEVICE_SIZE, .bDescriptorType = USB_DT_DEVICE, @@ -342,11 +337,17 @@ static const struct uvc_descriptor_header * const uvc_ss_streaming_cls[] = { static int __init webcam_config_bind(struct usb_configuration *c) { - return uvc_bind_config(c, uvc_fs_control_cls, - uvc_ss_control_cls, uvc_fs_streaming_cls, - uvc_hs_streaming_cls, uvc_ss_streaming_cls, - streaming_interval, streaming_maxpacket, - streaming_maxburst, trace); + int status = 0; + + f_uvc = usb_get_function(fi_uvc); + if (IS_ERR(f_uvc)) + return PTR_ERR(f_uvc); + + status = usb_add_function(c, f_uvc); + if (status < 0) + usb_put_function(f_uvc); + + return status; } static struct usb_configuration webcam_config_driver = { @@ -360,14 +361,36 @@ static struct usb_configuration webcam_config_driver = { static int /* __init_or_exit */ webcam_unbind(struct usb_composite_dev *cdev) { + if (!IS_ERR_OR_NULL(f_uvc)) + usb_put_function(f_uvc); + if (!IS_ERR_OR_NULL(fi_uvc)) + usb_put_function_instance(fi_uvc); return 0; } static int __init webcam_bind(struct usb_composite_dev *cdev) { + struct f_uvc_opts *uvc_opts; int ret; + fi_uvc = usb_get_function_instance("uvc"); + if (IS_ERR(fi_uvc)) + return PTR_ERR(fi_uvc); + + uvc_opts = container_of(fi_uvc, struct f_uvc_opts, func_inst); + + uvc_opts->streaming_interval = streaming_interval; + uvc_opts->streaming_maxpacket = streaming_maxpacket; + uvc_opts->streaming_maxburst = streaming_maxburst; + uvc_set_trace_param(trace); + + uvc_opts->fs_control = uvc_fs_control_cls; + uvc_opts->ss_control = uvc_ss_control_cls; + uvc_opts->fs_streaming = uvc_fs_streaming_cls; + uvc_opts->hs_streaming = uvc_hs_streaming_cls; + uvc_opts->ss_streaming = uvc_ss_streaming_cls; + /* Allocate string descriptor numbers ... note that string contents * can be overridden by the composite_dev glue. */ @@ -391,7 +414,7 @@ webcam_bind(struct usb_composite_dev *cdev) return 0; error: - webcam_unbind(cdev); + usb_put_function_instance(fi_uvc); return ret; } -- cgit v1.1 From cb47d889e651d36b4200800de1d56977b910d8a3 Mon Sep 17 00:00:00 2001 From: Andrzej Pietrasiewicz Date: Tue, 9 Sep 2014 02:02:12 +0300 Subject: usb: gadget: f_uvc: remove compatibility layer There are no users of the old interface left. Remove it. Signed-off-by: Andrzej Pietrasiewicz Tested-by: Michael Grzeschik Signed-off-by: Laurent Pinchart Signed-off-by: Felipe Balbi --- drivers/usb/gadget/function/f_uvc.c | 166 ------------------------------------ drivers/usb/gadget/function/f_uvc.h | 11 --- 2 files changed, 177 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/gadget/function/f_uvc.c b/drivers/usb/gadget/function/f_uvc.c index fe50a9b..1eff416 100644 --- a/drivers/usb/gadget/function/f_uvc.c +++ b/drivers/usb/gadget/function/f_uvc.c @@ -33,11 +33,6 @@ #include "u_uvc.h" unsigned int uvc_gadget_trace_param; -#ifdef USBF_UVC_INCLUDED -static unsigned int streaming_interval; -static unsigned int streaming_maxpacket; -static unsigned int streaming_maxburst; -#endif /* -------------------------------------------------------------------------- * Function descriptors @@ -202,16 +197,12 @@ static const struct usb_descriptor_header * const uvc_ss_streaming[] = { NULL, }; -#ifndef USBF_UVC_INCLUDED - void uvc_set_trace_param(unsigned int trace) { uvc_gadget_trace_param = trace; } EXPORT_SYMBOL(uvc_set_trace_param); -#endif - /* -------------------------------------------------------------------------- * Control requests */ @@ -566,30 +557,6 @@ uvc_copy_descriptors(struct uvc_device *uvc, enum usb_device_speed speed) return hdr; } -#ifdef USBF_UVC_INCLUDED -static void -uvc_function_unbind(struct usb_configuration *c, struct usb_function *f) -{ - struct usb_composite_dev *cdev = c->cdev; - struct uvc_device *uvc = to_uvc(f); - - INFO(cdev, "uvc_function_unbind\n"); - - video_unregister_device(uvc->vdev); - v4l2_device_unregister(&uvc->v4l2_dev); - uvc->control_ep->driver_data = NULL; - uvc->video.ep->driver_data = NULL; - - uvc_en_us_strings[UVC_STRING_CONTROL_IDX].id = 0; - usb_ep_free_request(cdev->gadget->ep0, uvc->control_req); - kfree(uvc->control_buf); - - usb_free_all_descriptors(f); - - kfree(uvc); -} -#endif - static int uvc_function_bind(struct usb_configuration *c, struct usb_function *f) { @@ -598,51 +565,11 @@ uvc_function_bind(struct usb_configuration *c, struct usb_function *f) unsigned int max_packet_mult; unsigned int max_packet_size; struct usb_ep *ep; -#ifndef USBF_UVC_INCLUDED struct f_uvc_opts *opts; -#endif int ret = -EINVAL; INFO(cdev, "uvc_function_bind\n"); -#ifdef USBF_UVC_INCLUDED - /* Sanity check the streaming endpoint module parameters. - */ - streaming_interval = clamp(streaming_interval, 1U, 16U); - streaming_maxpacket = clamp(streaming_maxpacket, 1U, 3072U); - streaming_maxburst = min(streaming_maxburst, 15U); - - /* Fill in the FS/HS/SS Video Streaming specific descriptors from the - * module parameters. - * - * NOTE: We assume that the user knows what they are doing and won't - * give parameters that their UDC doesn't support. - */ - if (streaming_maxpacket <= 1024) { - max_packet_mult = 1; - max_packet_size = streaming_maxpacket; - } else if (streaming_maxpacket <= 2048) { - max_packet_mult = 2; - max_packet_size = streaming_maxpacket / 2; - } else { - max_packet_mult = 3; - max_packet_size = streaming_maxpacket / 3; - } - - uvc_fs_streaming_ep.wMaxPacketSize = min(streaming_maxpacket, 1023U); - uvc_fs_streaming_ep.bInterval = streaming_interval; - - uvc_hs_streaming_ep.wMaxPacketSize = max_packet_size; - uvc_hs_streaming_ep.wMaxPacketSize |= ((max_packet_mult - 1) << 11); - uvc_hs_streaming_ep.bInterval = streaming_interval; - - uvc_ss_streaming_ep.wMaxPacketSize = max_packet_size; - uvc_ss_streaming_ep.bInterval = streaming_interval; - uvc_ss_streaming_comp.bmAttributes = max_packet_mult - 1; - uvc_ss_streaming_comp.bMaxBurst = streaming_maxburst; - uvc_ss_streaming_comp.wBytesPerInterval = - max_packet_size * max_packet_mult * streaming_maxburst; -#else opts = to_f_uvc_opts(f->fi); /* Sanity check the streaming endpoint module parameters. */ @@ -681,7 +608,6 @@ uvc_function_bind(struct usb_configuration *c, struct usb_function *f) uvc_ss_streaming_comp.bMaxBurst = opts->streaming_maxburst; uvc_ss_streaming_comp.wBytesPerInterval = max_packet_size * max_packet_mult * opts->streaming_maxburst; -#endif /* Allocate endpoints. */ ep = usb_ep_autoconfig(cdev->gadget, &uvc_control_ep); @@ -807,96 +733,6 @@ error: * USB gadget function */ -#ifdef USBF_UVC_INCLUDED -/** - * uvc_bind_config - add a UVC function to a configuration - * @c: the configuration to support the UVC instance - * Context: single threaded during gadget setup - * - * Returns zero on success, else negative errno. - * - * Caller must have called @uvc_setup(). Caller is also responsible for - * calling @uvc_cleanup() before module unload. - */ -int __init -uvc_bind_config(struct usb_configuration *c, - const struct uvc_descriptor_header * const *fs_control, - const struct uvc_descriptor_header * const *ss_control, - const struct uvc_descriptor_header * const *fs_streaming, - const struct uvc_descriptor_header * const *hs_streaming, - const struct uvc_descriptor_header * const *ss_streaming, - unsigned int stream_interv, unsigned int stream_maxpkt, - unsigned int stream_maxburst, unsigned int trace) -{ - struct uvc_device *uvc; - int ret = 0; - - /* TODO Check if the USB device controller supports the required - * features. - */ - if (!gadget_is_dualspeed(c->cdev->gadget)) - return -EINVAL; - - uvc = kzalloc(sizeof(*uvc), GFP_KERNEL); - if (uvc == NULL) - return -ENOMEM; - - uvc->state = UVC_STATE_DISCONNECTED; - - /* Validate the descriptors. */ - if (fs_control == NULL || fs_control[0] == NULL || - fs_control[0]->bDescriptorSubType != UVC_VC_HEADER) - goto error; - - if (ss_control == NULL || ss_control[0] == NULL || - ss_control[0]->bDescriptorSubType != UVC_VC_HEADER) - goto error; - - if (fs_streaming == NULL || fs_streaming[0] == NULL || - fs_streaming[0]->bDescriptorSubType != UVC_VS_INPUT_HEADER) - goto error; - - if (hs_streaming == NULL || hs_streaming[0] == NULL || - hs_streaming[0]->bDescriptorSubType != UVC_VS_INPUT_HEADER) - goto error; - - if (ss_streaming == NULL || ss_streaming[0] == NULL || - ss_streaming[0]->bDescriptorSubType != UVC_VS_INPUT_HEADER) - goto error; - - streaming_interval = stream_interv; - streaming_maxpacket = stream_maxpkt; - streaming_maxburst = stream_maxburst; - uvc_gadget_trace_param = trace; - uvc->desc.fs_control = fs_control; - uvc->desc.ss_control = ss_control; - uvc->desc.fs_streaming = fs_streaming; - uvc->desc.hs_streaming = hs_streaming; - uvc->desc.ss_streaming = ss_streaming; - - /* Register the function. */ - uvc->func.name = "uvc"; - uvc->func.strings = uvc_function_strings; - uvc->func.bind = uvc_function_bind; - uvc->func.unbind = uvc_function_unbind; - uvc->func.get_alt = uvc_function_get_alt; - uvc->func.set_alt = uvc_function_set_alt; - uvc->func.disable = uvc_function_disable; - uvc->func.setup = uvc_function_setup; - - ret = usb_add_function(c, &uvc->func); - if (ret) - kfree(uvc); - - return ret; - -error: - kfree(uvc); - return ret; -} - -#else - static void uvc_free_inst(struct usb_function_instance *f) { struct f_uvc_opts *opts = to_f_uvc_opts(f); @@ -977,5 +813,3 @@ struct usb_function *uvc_alloc(struct usb_function_instance *fi) DECLARE_USB_FUNCTION_INIT(uvc, uvc_alloc_inst, uvc_alloc); MODULE_LICENSE("GPL"); MODULE_AUTHOR("Laurent Pinchart"); - -#endif diff --git a/drivers/usb/gadget/function/f_uvc.h b/drivers/usb/gadget/function/f_uvc.h index 71b38dd..d0a73bd 100644 --- a/drivers/usb/gadget/function/f_uvc.h +++ b/drivers/usb/gadget/function/f_uvc.h @@ -24,16 +24,5 @@ void uvc_function_connect(struct uvc_device *uvc); void uvc_function_disconnect(struct uvc_device *uvc); -int uvc_bind_config(struct usb_configuration *c, - const struct uvc_descriptor_header * const *fs_control, - const struct uvc_descriptor_header * const *hs_control, - const struct uvc_descriptor_header * const *fs_streaming, - const struct uvc_descriptor_header * const *hs_streaming, - const struct uvc_descriptor_header * const *ss_streaming, - unsigned int streaming_interval_webcam, - unsigned int streaming_maxpacket_webcam, - unsigned int streaming_maxburst_webcam, - unsigned int uvc_gadget_trace_webcam); - #endif /* _F_UVC_H_ */ -- cgit v1.1 From 13443799b559cde593826091a7de135483b245e5 Mon Sep 17 00:00:00 2001 From: Andrzej Pietrasiewicz Date: Tue, 9 Sep 2014 02:02:13 +0300 Subject: usb: gadget: f_uvc: use usb_gstrings_attach Attach strings to gadget with usb_strings_attach. It is required for correct instantiation of functions more than once: instead of modifying the local uvc_en_us_strings a function instance specific copy is created with usb_gstrings_attach. Signed-off-by: Andrzej Pietrasiewicz Tested-by: Michael Grzeschik Signed-off-by: Laurent Pinchart Signed-off-by: Felipe Balbi --- drivers/usb/gadget/function/f_uvc.c | 28 +++++++++++----------------- 1 file changed, 11 insertions(+), 17 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/gadget/function/f_uvc.c b/drivers/usb/gadget/function/f_uvc.c index 1eff416..95dc1c6 100644 --- a/drivers/usb/gadget/function/f_uvc.c +++ b/drivers/usb/gadget/function/f_uvc.c @@ -562,6 +562,7 @@ uvc_function_bind(struct usb_configuration *c, struct usb_function *f) { struct usb_composite_dev *cdev = c->cdev; struct uvc_device *uvc = to_uvc(f); + struct usb_string *us; unsigned int max_packet_mult; unsigned int max_packet_size; struct usb_ep *ep; @@ -637,22 +638,17 @@ uvc_function_bind(struct usb_configuration *c, struct usb_function *f) uvc_hs_streaming_ep.bEndpointAddress = uvc->video.ep->address; uvc_ss_streaming_ep.bEndpointAddress = uvc->video.ep->address; - /* String descriptors are global, we only need to allocate string IDs - * for the first UVC function. UVC functions beyond the first (if any) - * will reuse the same IDs. - */ - if (uvc_en_us_strings[UVC_STRING_CONTROL_IDX].id == 0) { - ret = usb_string_ids_tab(c->cdev, uvc_en_us_strings); - if (ret) - goto error; - uvc_iad.iFunction = - uvc_en_us_strings[UVC_STRING_CONTROL_IDX].id; - uvc_control_intf.iInterface = - uvc_en_us_strings[UVC_STRING_CONTROL_IDX].id; - ret = uvc_en_us_strings[UVC_STRING_STREAMING_IDX].id; - uvc_streaming_intf_alt0.iInterface = ret; - uvc_streaming_intf_alt1.iInterface = ret; + us = usb_gstrings_attach(cdev, uvc_function_strings, + ARRAY_SIZE(uvc_en_us_strings)); + if (IS_ERR(us)) { + ret = PTR_ERR(us); + goto error; } + uvc_iad.iFunction = us[UVC_STRING_CONTROL_IDX].id; + uvc_control_intf.iInterface = us[UVC_STRING_CONTROL_IDX].id; + ret = us[UVC_STRING_STREAMING_IDX].id; + uvc_streaming_intf_alt0.iInterface = ret; + uvc_streaming_intf_alt1.iInterface = ret; /* Allocate interface IDs. */ if ((ret = usb_interface_id(c, f)) < 0) @@ -771,7 +767,6 @@ static void uvc_unbind(struct usb_configuration *c, struct usb_function *f) uvc->control_ep->driver_data = NULL; uvc->video.ep->driver_data = NULL; - uvc_en_us_strings[UVC_STRING_CONTROL_IDX].id = 0; usb_ep_free_request(cdev->gadget->ep0, uvc->control_req); kfree(uvc->control_buf); @@ -798,7 +793,6 @@ struct usb_function *uvc_alloc(struct usb_function_instance *fi) /* Register the function. */ uvc->func.name = "uvc"; - uvc->func.strings = uvc_function_strings; uvc->func.bind = uvc_function_bind; uvc->func.unbind = uvc_unbind; uvc->func.get_alt = uvc_function_get_alt; -- cgit v1.1 From 85b06f5e53d17c15844ef3cd45d0c7107f0ae45c Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Tue, 9 Sep 2014 15:06:09 +0300 Subject: usb: gadget: f_fs: signedness bug in __ffs_func_bind_do_descs() We need "idx" to be signed for the error handling to work. Fixes: 6d5c1c77bbf9 ('usb: gadget: f_fs: fix the redundant ep files problem') Acked-by: Michal Nazarewicz Signed-off-by: Dan Carpenter Signed-off-by: Felipe Balbi --- drivers/usb/gadget/function/f_fs.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'drivers/usb') diff --git a/drivers/usb/gadget/function/f_fs.c b/drivers/usb/gadget/function/f_fs.c index dc30adf..1aad353 100644 --- a/drivers/usb/gadget/function/f_fs.c +++ b/drivers/usb/gadget/function/f_fs.c @@ -2352,7 +2352,8 @@ static int __ffs_func_bind_do_descs(enum ffs_entity_type type, u8 *valuep, struct usb_endpoint_descriptor *ds = (void *)desc; struct ffs_function *func = priv; struct ffs_ep *ffs_ep; - unsigned ep_desc_id, idx; + unsigned ep_desc_id; + int idx; static const char *speed_names[] = { "full", "high", "super" }; if (type != FFS_DESCRIPTOR) -- cgit v1.1 From d8a816fc6f6a1d262798dc43d6791c3e93d2d2b5 Mon Sep 17 00:00:00 2001 From: Peter Chen Date: Tue, 9 Sep 2014 08:56:49 +0800 Subject: usb: gadget: composite: add reset API at usb_gadget_driver Add reset API at usb_gadget_driver, it calls disconnect handler currently, but may do different things in future. Acked-by: Alan Stern Signed-off-by: Peter Chen Signed-off-by: Felipe Balbi --- drivers/usb/gadget/composite.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers/usb') diff --git a/drivers/usb/gadget/composite.c b/drivers/usb/gadget/composite.c index 4514e73..3f3d6f2 100644 --- a/drivers/usb/gadget/composite.c +++ b/drivers/usb/gadget/composite.c @@ -2073,6 +2073,7 @@ static const struct usb_gadget_driver composite_driver_template = { .unbind = composite_unbind, .setup = composite_setup, + .reset = composite_disconnect, .disconnect = composite_disconnect, .suspend = composite_suspend, -- cgit v1.1 From 02f751b43f6766da4382bed322926eb99d56f516 Mon Sep 17 00:00:00 2001 From: Peter Chen Date: Tue, 9 Sep 2014 08:56:50 +0800 Subject: usb: gadget: configfs: add reset API at usb_gadget_driver Add reset API at usb_gadget_driver, it calls disconnect handler currently, but may do different things in future. Acked-by: Alan Stern Signed-off-by: Peter Chen Signed-off-by: Felipe Balbi --- drivers/usb/gadget/configfs.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers/usb') diff --git a/drivers/usb/gadget/configfs.c b/drivers/usb/gadget/configfs.c index 811c2c7..3403433 100644 --- a/drivers/usb/gadget/configfs.c +++ b/drivers/usb/gadget/configfs.c @@ -1450,6 +1450,7 @@ static const struct usb_gadget_driver configfs_driver_template = { .unbind = configfs_composite_unbind, .setup = composite_setup, + .reset = composite_disconnect, .disconnect = composite_disconnect, .max_speed = USB_SPEED_SUPER, -- cgit v1.1 From 0eba4550fc642f4a51d76bf20d2b8104ec81d8e5 Mon Sep 17 00:00:00 2001 From: Peter Chen Date: Tue, 9 Sep 2014 08:56:51 +0800 Subject: usb: gadget: gadgetfs: add reset API at usb_gadget_driver Add reset API at usb_gadget_driver, it calls disconnect handler currently, but may do different things in future. Acked-by: Alan Stern Signed-off-by: Peter Chen Signed-off-by: Felipe Balbi --- drivers/usb/gadget/legacy/inode.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers/usb') diff --git a/drivers/usb/gadget/legacy/inode.c b/drivers/usb/gadget/legacy/inode.c index e96077b..edefec2 100644 --- a/drivers/usb/gadget/legacy/inode.c +++ b/drivers/usb/gadget/legacy/inode.c @@ -1775,6 +1775,7 @@ static struct usb_gadget_driver gadgetfs_driver = { .bind = gadgetfs_bind, .unbind = gadgetfs_unbind, .setup = gadgetfs_setup, + .reset = gadgetfs_disconnect, .disconnect = gadgetfs_disconnect, .suspend = gadgetfs_suspend, -- cgit v1.1 From e45cfa2051d1b7d3378887d9576e11484e25c7d6 Mon Sep 17 00:00:00 2001 From: Peter Chen Date: Tue, 9 Sep 2014 08:56:52 +0800 Subject: usb: gadget: dbgp: add reset API at usb_gadget_driver Add reset API at usb_gadget_driver, it calls disconnect handler currently, but may do different things in future. Acked-by: Alan Stern Signed-off-by: Peter Chen Signed-off-by: Felipe Balbi --- drivers/usb/gadget/legacy/dbgp.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers/usb') diff --git a/drivers/usb/gadget/legacy/dbgp.c b/drivers/usb/gadget/legacy/dbgp.c index 225e385..1b07513 100644 --- a/drivers/usb/gadget/legacy/dbgp.c +++ b/drivers/usb/gadget/legacy/dbgp.c @@ -410,6 +410,7 @@ static __refdata struct usb_gadget_driver dbgp_driver = { .bind = dbgp_bind, .unbind = dbgp_unbind, .setup = dbgp_setup, + .reset = dbgp_disconnect, .disconnect = dbgp_disconnect, .driver = { .owner = THIS_MODULE, -- cgit v1.1 From c559a353410939c0884e83bdb0e2420a986ac53b Mon Sep 17 00:00:00 2001 From: Robert Baldyga Date: Tue, 9 Sep 2014 08:23:16 +0200 Subject: usb: gadget: f_fs: add ioctl returning ep descriptor This patch introduces ioctl named FUNCTIONFS_ENDPOINT_DESC, which returns endpoint descriptor to userspace. It works only if function is active. Signed-off-by: Robert Baldyga Acked-by: Michal Nazarewicz Signed-off-by: Felipe Balbi --- drivers/usb/gadget/function/f_fs.c | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) (limited to 'drivers/usb') diff --git a/drivers/usb/gadget/function/f_fs.c b/drivers/usb/gadget/function/f_fs.c index 1aad353..a345385 100644 --- a/drivers/usb/gadget/function/f_fs.c +++ b/drivers/usb/gadget/function/f_fs.c @@ -1026,6 +1026,29 @@ static long ffs_epfile_ioctl(struct file *file, unsigned code, case FUNCTIONFS_ENDPOINT_REVMAP: ret = epfile->ep->num; break; + case FUNCTIONFS_ENDPOINT_DESC: + { + int desc_idx; + struct usb_endpoint_descriptor *desc; + + switch (epfile->ffs->gadget->speed) { + case USB_SPEED_SUPER: + desc_idx = 2; + break; + case USB_SPEED_HIGH: + desc_idx = 1; + break; + default: + desc_idx = 0; + } + desc = epfile->ep->descs[desc_idx]; + + spin_unlock_irq(&epfile->ffs->eps_lock); + ret = copy_to_user((void *)value, desc, sizeof(*desc)); + if (ret) + ret = -EFAULT; + return ret; + } default: ret = -ENOTTY; } -- cgit v1.1 From fe00bcbf8a124980a38ce395ed6422d41be17374 Mon Sep 17 00:00:00 2001 From: Michal Nazarewicz Date: Thu, 11 Sep 2014 18:52:49 +0200 Subject: usb: f_fs: replace BUG in dead-code with less serious WARN_ON Even though the BUG() in __ffs_event_add is a dead-code, it is still better to warn rather then crash the system if that code ever gets executed. Reported-by: Felipe Balbi Signed-off-by: Michal Nazarewicz Signed-off-by: Felipe Balbi --- drivers/usb/gadget/function/f_fs.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'drivers/usb') diff --git a/drivers/usb/gadget/function/f_fs.c b/drivers/usb/gadget/function/f_fs.c index a345385..a86b0a2 100644 --- a/drivers/usb/gadget/function/f_fs.c +++ b/drivers/usb/gadget/function/f_fs.c @@ -2337,7 +2337,8 @@ static void __ffs_event_add(struct ffs_data *ffs, break; default: - BUG(); + WARN(1, "%d: unknown event, this should not happen\n", type); + return; } { -- cgit v1.1 From 1f7c51660034091dc134fcc534b7f1fa86a6e823 Mon Sep 17 00:00:00 2001 From: Subbaraya Sundeep Bhatta Date: Wed, 10 Sep 2014 19:24:04 +0530 Subject: usb: gadget: Add xilinx usb2 device support Xilinx USB2 device is a soft IP which supports both full and high speed USB 2.0 data transfers. This patch adds xilinx usb2 device driver support. Signed-off-by: Subbaraya Sundeep Bhatta Signed-off-by: Felipe Balbi --- drivers/usb/gadget/udc/Kconfig | 15 + drivers/usb/gadget/udc/Makefile | 1 + drivers/usb/gadget/udc/udc-xilinx.c | 2180 +++++++++++++++++++++++++++++++++++ 3 files changed, 2196 insertions(+) create mode 100644 drivers/usb/gadget/udc/udc-xilinx.c (limited to 'drivers/usb') diff --git a/drivers/usb/gadget/udc/Kconfig b/drivers/usb/gadget/udc/Kconfig index 0c17280..3ea287b 100644 --- a/drivers/usb/gadget/udc/Kconfig +++ b/drivers/usb/gadget/udc/Kconfig @@ -354,6 +354,21 @@ config USB_EG20T ML7213/ML7831 is completely compatible for Intel EG20T PCH. This driver can be used with Intel's Quark X1000 SOC platform + +config USB_GADGET_XILINX + tristate "Xilinx USB Driver" + depends on OF || COMPILE_TEST + help + USB peripheral controller driver for Xilinx USB2 device. + Xilinx USB2 device is a soft IP which supports both full + and high speed USB 2.0 data transfers. It has seven configurable + endpoints(bulk or interrupt or isochronous), as well as + endpoint zero(for control transfers). + + Say "y" to link the driver statically, or "m" to build a + dynamically linked module called "udc-xilinx" and force all + gadget drivers to also be dynamically linked. + # # LAST -- dummy/emulated controller # diff --git a/drivers/usb/gadget/udc/Makefile b/drivers/usb/gadget/udc/Makefile index 4096122..a7f4491 100644 --- a/drivers/usb/gadget/udc/Makefile +++ b/drivers/usb/gadget/udc/Makefile @@ -29,3 +29,4 @@ obj-$(CONFIG_USB_FUSB300) += fusb300_udc.o obj-$(CONFIG_USB_FOTG210_UDC) += fotg210-udc.o obj-$(CONFIG_USB_MV_U3D) += mv_u3d_core.o obj-$(CONFIG_USB_GR_UDC) += gr_udc.o +obj-$(CONFIG_USB_GADGET_XILINX) += udc-xilinx.o diff --git a/drivers/usb/gadget/udc/udc-xilinx.c b/drivers/usb/gadget/udc/udc-xilinx.c new file mode 100644 index 0000000..ed27e16 --- /dev/null +++ b/drivers/usb/gadget/udc/udc-xilinx.c @@ -0,0 +1,2180 @@ +/* + * Xilinx USB peripheral controller driver + * + * Copyright (C) 2004 by Thomas Rathbone + * Copyright (C) 2005 by HP Labs + * Copyright (C) 2005 by David Brownell + * Copyright (C) 2010 - 2014 Xilinx, Inc. + * + * Some parts of this driver code is based on the driver for at91-series + * USB peripheral controller (at91_udc.c). + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; + * either version 2 of the License, or (at your option) any + * later version. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* Register offsets for the USB device.*/ +#define XUSB_EP0_CONFIG_OFFSET 0x0000 /* EP0 Config Reg Offset */ +#define XUSB_SETUP_PKT_ADDR_OFFSET 0x0080 /* Setup Packet Address */ +#define XUSB_ADDRESS_OFFSET 0x0100 /* Address Register */ +#define XUSB_CONTROL_OFFSET 0x0104 /* Control Register */ +#define XUSB_STATUS_OFFSET 0x0108 /* Status Register */ +#define XUSB_FRAMENUM_OFFSET 0x010C /* Frame Number Register */ +#define XUSB_IER_OFFSET 0x0110 /* Interrupt Enable Register */ +#define XUSB_BUFFREADY_OFFSET 0x0114 /* Buffer Ready Register */ +#define XUSB_TESTMODE_OFFSET 0x0118 /* Test Mode Register */ +#define XUSB_DMA_RESET_OFFSET 0x0200 /* DMA Soft Reset Register */ +#define XUSB_DMA_CONTROL_OFFSET 0x0204 /* DMA Control Register */ +#define XUSB_DMA_DSAR_ADDR_OFFSET 0x0208 /* DMA source Address Reg */ +#define XUSB_DMA_DDAR_ADDR_OFFSET 0x020C /* DMA destination Addr Reg */ +#define XUSB_DMA_LENGTH_OFFSET 0x0210 /* DMA Length Register */ +#define XUSB_DMA_STATUS_OFFSET 0x0214 /* DMA Status Register */ + +/* Endpoint Configuration Space offsets */ +#define XUSB_EP_CFGSTATUS_OFFSET 0x00 /* Endpoint Config Status */ +#define XUSB_EP_BUF0COUNT_OFFSET 0x08 /* Buffer 0 Count */ +#define XUSB_EP_BUF1COUNT_OFFSET 0x0C /* Buffer 1 Count */ + +#define XUSB_CONTROL_USB_READY_MASK 0x80000000 /* USB ready Mask */ +#define XUSB_CONTROL_USB_RMTWAKE_MASK 0x40000000 /* Remote wake up mask */ + +/* Interrupt register related masks.*/ +#define XUSB_STATUS_GLOBAL_INTR_MASK 0x80000000 /* Global Intr Enable */ +#define XUSB_STATUS_DMADONE_MASK 0x04000000 /* DMA done Mask */ +#define XUSB_STATUS_DMAERR_MASK 0x02000000 /* DMA Error Mask */ +#define XUSB_STATUS_DMABUSY_MASK 0x80000000 /* DMA Error Mask */ +#define XUSB_STATUS_RESUME_MASK 0x01000000 /* USB Resume Mask */ +#define XUSB_STATUS_RESET_MASK 0x00800000 /* USB Reset Mask */ +#define XUSB_STATUS_SUSPEND_MASK 0x00400000 /* USB Suspend Mask */ +#define XUSB_STATUS_DISCONNECT_MASK 0x00200000 /* USB Disconnect Mask */ +#define XUSB_STATUS_FIFO_BUFF_RDY_MASK 0x00100000 /* FIFO Buff Ready Mask */ +#define XUSB_STATUS_FIFO_BUFF_FREE_MASK 0x00080000 /* FIFO Buff Free Mask */ +#define XUSB_STATUS_SETUP_PACKET_MASK 0x00040000 /* Setup packet received */ +#define XUSB_STATUS_EP1_BUFF2_COMP_MASK 0x00000200 /* EP 1 Buff 2 Processed */ +#define XUSB_STATUS_EP1_BUFF1_COMP_MASK 0x00000002 /* EP 1 Buff 1 Processed */ +#define XUSB_STATUS_EP0_BUFF2_COMP_MASK 0x00000100 /* EP 0 Buff 2 Processed */ +#define XUSB_STATUS_EP0_BUFF1_COMP_MASK 0x00000001 /* EP 0 Buff 1 Processed */ +#define XUSB_STATUS_HIGH_SPEED_MASK 0x00010000 /* USB Speed Mask */ +/* Suspend,Reset,Suspend and Disconnect Mask */ +#define XUSB_STATUS_INTR_EVENT_MASK 0x01E00000 +/* Buffers completion Mask */ +#define XUSB_STATUS_INTR_BUFF_COMP_ALL_MASK 0x0000FEFF +/* Mask for buffer 0 and buffer 1 completion for all Endpoints */ +#define XUSB_STATUS_INTR_BUFF_COMP_SHIFT_MASK 0x00000101 +#define XUSB_STATUS_EP_BUFF2_SHIFT 8 /* EP buffer offset */ + +/* Endpoint Configuration Status Register */ +#define XUSB_EP_CFG_VALID_MASK 0x80000000 /* Endpoint Valid bit */ +#define XUSB_EP_CFG_STALL_MASK 0x40000000 /* Endpoint Stall bit */ +#define XUSB_EP_CFG_DATA_TOGGLE_MASK 0x08000000 /* Endpoint Data toggle */ + +/* USB device specific global configuration constants.*/ +#define XUSB_MAX_ENDPOINTS 8 /* Maximum End Points */ +#define XUSB_EP_NUMBER_ZERO 0 /* End point Zero */ +/* DPRAM is the source address for DMA transfer */ +#define XUSB_DMA_READ_FROM_DPRAM 0x80000000 +#define XUSB_DMA_DMASR_BUSY 0x80000000 /* DMA busy */ +#define XUSB_DMA_DMASR_ERROR 0x40000000 /* DMA Error */ +/* + * When this bit is set, the DMA buffer ready bit is set by hardware upon + * DMA transfer completion. + */ +#define XUSB_DMA_BRR_CTRL 0x40000000 /* DMA bufready ctrl bit */ +/* Phase States */ +#define SETUP_PHASE 0x0000 /* Setup Phase */ +#define DATA_PHASE 0x0001 /* Data Phase */ +#define STATUS_PHASE 0x0002 /* Status Phase */ + +#define EP0_MAX_PACKET 64 /* Endpoint 0 maximum packet length */ +#define STATUSBUFF_SIZE 2 /* Buffer size for GET_STATUS command */ +#define EPNAME_SIZE 4 /* Buffer size for endpoint name */ + +/* container_of helper macros */ +#define to_udc(g) container_of((g), struct xusb_udc, gadget) +#define to_xusb_ep(ep) container_of((ep), struct xusb_ep, ep_usb) +#define to_xusb_req(req) container_of((req), struct xusb_req, usb_req) + +/** + * struct xusb_req - Xilinx USB device request structure + * @usb_req: Linux usb request structure + * @queue: usb device request queue + * @ep: pointer to xusb_endpoint structure + */ +struct xusb_req { + struct usb_request usb_req; + struct list_head queue; + struct xusb_ep *ep; +}; + +/** + * struct xusb_ep - USB end point structure. + * @ep_usb: usb endpoint instance + * @queue: endpoint message queue + * @udc: xilinx usb peripheral driver instance pointer + * @desc: pointer to the usb endpoint descriptor + * @rambase: the endpoint buffer address + * @offset: the endpoint register offset value + * @name: name of the endpoint + * @epnumber: endpoint number + * @maxpacket: maximum packet size the endpoint can store + * @buffer0count: the size of the packet recieved in the first buffer + * @buffer1count: the size of the packet received in the second buffer + * @curbufnum: current buffer of endpoint that will be processed next + * @buffer0ready: the busy state of first buffer + * @buffer1ready: the busy state of second buffer + * @is_in: endpoint direction (IN or OUT) + * @is_iso: endpoint type(isochronous or non isochronous) + */ +struct xusb_ep { + struct usb_ep ep_usb; + struct list_head queue; + struct xusb_udc *udc; + const struct usb_endpoint_descriptor *desc; + u32 rambase; + u32 offset; + char name[4]; + u16 epnumber; + u16 maxpacket; + u16 buffer0count; + u16 buffer1count; + u8 curbufnum; + bool buffer0ready; + bool buffer1ready; + bool is_in; + bool is_iso; +}; + +/** + * struct xusb_udc - USB peripheral driver structure + * @gadget: USB gadget driver instance + * @ep: an array of endpoint structures + * @driver: pointer to the usb gadget driver instance + * @setup: usb_ctrlrequest structure for control requests + * @req: pointer to dummy request for get status command + * @dev: pointer to device structure in gadget + * @usb_state: device in suspended state or not + * @remote_wkp: remote wakeup enabled by host + * @setupseqtx: tx status + * @setupseqrx: rx status + * @addr: the usb device base address + * @lock: instance of spinlock + * @dma_enabled: flag indicating whether the dma is included in the system + * @read_fn: function pointer to read device registers + * @write_fn: function pointer to write to device registers + */ +struct xusb_udc { + struct usb_gadget gadget; + struct xusb_ep ep[8]; + struct usb_gadget_driver *driver; + struct usb_ctrlrequest setup; + struct xusb_req *req; + struct device *dev; + u32 usb_state; + u32 remote_wkp; + u32 setupseqtx; + u32 setupseqrx; + void __iomem *addr; + spinlock_t lock; + bool dma_enabled; + + unsigned int (*read_fn)(void __iomem *); + void (*write_fn)(void __iomem *, u32, u32); +}; + +/* Endpoint buffer start addresses in the core */ +static u32 rambase[8] = { 0x22, 0x1000, 0x1100, 0x1200, 0x1300, 0x1400, 0x1500, + 0x1600 }; + +static const char driver_name[] = "xilinx-udc"; +static const char ep0name[] = "ep0"; + +/* Control endpoint configuration.*/ +static const struct usb_endpoint_descriptor config_bulk_out_desc = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + .bEndpointAddress = USB_DIR_OUT, + .bmAttributes = USB_ENDPOINT_XFER_BULK, + .wMaxPacketSize = cpu_to_le16(EP0_MAX_PACKET), +}; + +/** + * xudc_write32 - little endian write to device registers + * @addr: base addr of device registers + * @offset: register offset + * @val: data to be written + */ +static void xudc_write32(void __iomem *addr, u32 offset, u32 val) +{ + iowrite32(val, addr + offset); +} + +/** + * xudc_read32 - little endian read from device registers + * @addr: addr of device register + * Return: value at addr + */ +static unsigned int xudc_read32(void __iomem *addr) +{ + return ioread32(addr); +} + +/** + * xudc_write32_be - big endian write to device registers + * @addr: base addr of device registers + * @offset: register offset + * @val: data to be written + */ +static void xudc_write32_be(void __iomem *addr, u32 offset, u32 val) +{ + iowrite32be(val, addr + offset); +} + +/** + * xudc_read32_be - big endian read from device registers + * @addr: addr of device register + * Return: value at addr + */ +static unsigned int xudc_read32_be(void __iomem *addr) +{ + return ioread32be(addr); +} + +/** + * xudc_wrstatus - Sets up the usb device status stages. + * @udc: pointer to the usb device controller structure. + */ +static void xudc_wrstatus(struct xusb_udc *udc) +{ + struct xusb_ep *ep0 = &udc->ep[XUSB_EP_NUMBER_ZERO]; + u32 epcfgreg; + + epcfgreg = udc->read_fn(udc->addr + ep0->offset)| + XUSB_EP_CFG_DATA_TOGGLE_MASK; + udc->write_fn(udc->addr, ep0->offset, epcfgreg); + udc->write_fn(udc->addr, ep0->offset + XUSB_EP_BUF0COUNT_OFFSET, 0); + udc->write_fn(udc->addr, XUSB_BUFFREADY_OFFSET, 1); +} + +/** + * xudc_epconfig - Configures the given endpoint. + * @ep: pointer to the usb device endpoint structure. + * @udc: pointer to the usb peripheral controller structure. + * + * This function configures a specific endpoint with the given configuration + * data. + */ +static void xudc_epconfig(struct xusb_ep *ep, struct xusb_udc *udc) +{ + u32 epcfgreg; + + /* + * Configure the end point direction, type, Max Packet Size and the + * EP buffer location. + */ + epcfgreg = ((ep->is_in << 29) | (ep->is_iso << 28) | + (ep->ep_usb.maxpacket << 15) | (ep->rambase)); + udc->write_fn(udc->addr, ep->offset, epcfgreg); + + /* Set the Buffer count and the Buffer ready bits.*/ + udc->write_fn(udc->addr, ep->offset + XUSB_EP_BUF0COUNT_OFFSET, + ep->buffer0count); + udc->write_fn(udc->addr, ep->offset + XUSB_EP_BUF1COUNT_OFFSET, + ep->buffer1count); + if (ep->buffer0ready) + udc->write_fn(udc->addr, XUSB_BUFFREADY_OFFSET, + 1 << ep->epnumber); + if (ep->buffer1ready) + udc->write_fn(udc->addr, XUSB_BUFFREADY_OFFSET, + 1 << (ep->epnumber + XUSB_STATUS_EP_BUFF2_SHIFT)); +} + +/** + * xudc_start_dma - Starts DMA transfer. + * @ep: pointer to the usb device endpoint structure. + * @src: DMA source address. + * @dst: DMA destination address. + * @length: number of bytes to transfer. + * + * Return: 0 on success, error code on failure + * + * This function starts DMA transfer by writing to DMA source, + * destination and lenth registers. + */ +static int xudc_start_dma(struct xusb_ep *ep, dma_addr_t src, + dma_addr_t dst, u32 length) +{ + struct xusb_udc *udc = ep->udc; + int rc = 0; + u32 timeout = 500; + u32 reg; + + /* + * Set the addresses in the DMA source and + * destination registers and then set the length + * into the DMA length register. + */ + udc->write_fn(udc->addr, XUSB_DMA_DSAR_ADDR_OFFSET, src); + udc->write_fn(udc->addr, XUSB_DMA_DDAR_ADDR_OFFSET, dst); + udc->write_fn(udc->addr, XUSB_DMA_LENGTH_OFFSET, length); + + /* + * Wait till DMA transaction is complete and + * check whether the DMA transaction was + * successful. + */ + do { + reg = udc->read_fn(udc->addr + XUSB_DMA_STATUS_OFFSET); + if (!(reg & XUSB_DMA_DMASR_BUSY)) + break; + + /* + * We can't sleep here, because it's also called from + * interrupt context. + */ + timeout--; + if (!timeout) { + dev_err(udc->dev, "DMA timeout\n"); + return -ETIMEDOUT; + } + udelay(1); + } while (1); + + if ((udc->read_fn(udc->addr + XUSB_DMA_STATUS_OFFSET) & + XUSB_DMA_DMASR_ERROR) == XUSB_DMA_DMASR_ERROR){ + dev_err(udc->dev, "DMA Error\n"); + rc = -EINVAL; + } + + return rc; +} + +/** + * xudc_dma_send - Sends IN data using DMA. + * @ep: pointer to the usb device endpoint structure. + * @req: pointer to the usb request structure. + * @buffer: pointer to data to be sent. + * @length: number of bytes to send. + * + * Return: 0 on success, -EAGAIN if no buffer is free and error + * code on failure. + * + * This function sends data using DMA. + */ +static int xudc_dma_send(struct xusb_ep *ep, struct xusb_req *req, + u8 *buffer, u32 length) +{ + u32 *eprambase; + dma_addr_t src; + dma_addr_t dst; + struct xusb_udc *udc = ep->udc; + + src = req->usb_req.dma + req->usb_req.actual; + if (req->usb_req.length) + dma_sync_single_for_device(udc->dev, src, + length, DMA_TO_DEVICE); + if (!ep->curbufnum && !ep->buffer0ready) { + /* Get the Buffer address and copy the transmit data.*/ + eprambase = (u32 __force *)(udc->addr + ep->rambase); + dst = virt_to_phys(eprambase); + udc->write_fn(udc->addr, ep->offset + + XUSB_EP_BUF0COUNT_OFFSET, length); + udc->write_fn(udc->addr, XUSB_DMA_CONTROL_OFFSET, + XUSB_DMA_BRR_CTRL | (1 << ep->epnumber)); + ep->buffer0ready = 1; + ep->curbufnum = 1; + } else if (ep->curbufnum && !ep->buffer1ready) { + /* Get the Buffer address and copy the transmit data.*/ + eprambase = (u32 __force *)(udc->addr + ep->rambase + + ep->ep_usb.maxpacket); + dst = virt_to_phys(eprambase); + udc->write_fn(udc->addr, ep->offset + + XUSB_EP_BUF1COUNT_OFFSET, length); + udc->write_fn(udc->addr, XUSB_DMA_CONTROL_OFFSET, + XUSB_DMA_BRR_CTRL | (1 << (ep->epnumber + + XUSB_STATUS_EP_BUFF2_SHIFT))); + ep->buffer1ready = 1; + ep->curbufnum = 0; + } else { + /* None of ping pong buffers are ready currently .*/ + return -EAGAIN; + } + + return xudc_start_dma(ep, src, dst, length); +} + +/** + * xudc_dma_receive - Receives OUT data using DMA. + * @ep: pointer to the usb device endpoint structure. + * @req: pointer to the usb request structure. + * @buffer: pointer to storage buffer of received data. + * @length: number of bytes to receive. + * + * Return: 0 on success, -EAGAIN if no buffer is free and error + * code on failure. + * + * This function receives data using DMA. + */ +static int xudc_dma_receive(struct xusb_ep *ep, struct xusb_req *req, + u8 *buffer, u32 length) +{ + u32 *eprambase; + dma_addr_t src; + dma_addr_t dst; + struct xusb_udc *udc = ep->udc; + + dst = req->usb_req.dma + req->usb_req.actual; + if (!ep->curbufnum && !ep->buffer0ready) { + /* Get the Buffer address and copy the transmit data */ + eprambase = (u32 __force *)(udc->addr + ep->rambase); + src = virt_to_phys(eprambase); + udc->write_fn(udc->addr, XUSB_DMA_CONTROL_OFFSET, + XUSB_DMA_BRR_CTRL | XUSB_DMA_READ_FROM_DPRAM | + (1 << ep->epnumber)); + ep->buffer0ready = 1; + ep->curbufnum = 1; + } else if (ep->curbufnum && !ep->buffer1ready) { + /* Get the Buffer address and copy the transmit data */ + eprambase = (u32 __force *)(udc->addr + + ep->rambase + ep->ep_usb.maxpacket); + src = virt_to_phys(eprambase); + udc->write_fn(udc->addr, XUSB_DMA_CONTROL_OFFSET, + XUSB_DMA_BRR_CTRL | XUSB_DMA_READ_FROM_DPRAM | + (1 << (ep->epnumber + + XUSB_STATUS_EP_BUFF2_SHIFT))); + ep->buffer1ready = 1; + ep->curbufnum = 0; + } else { + /* None of the ping-pong buffers are ready currently */ + return -EAGAIN; + } + + return xudc_start_dma(ep, src, dst, length); +} + +/** + * xudc_eptxrx - Transmits or receives data to or from an endpoint. + * @ep: pointer to the usb endpoint configuration structure. + * @req: pointer to the usb request structure. + * @bufferptr: pointer to buffer containing the data to be sent. + * @bufferlen: The number of data bytes to be sent. + * + * Return: 0 on success, -EAGAIN if no buffer is free. + * + * This function copies the transmit/receive data to/from the end point buffer + * and enables the buffer for transmission/reception. + */ +static int xudc_eptxrx(struct xusb_ep *ep, struct xusb_req *req, + u8 *bufferptr, u32 bufferlen) +{ + u32 *eprambase; + u32 bytestosend; + int rc = 0; + struct xusb_udc *udc = ep->udc; + + bytestosend = bufferlen; + if (udc->dma_enabled) { + if (ep->is_in) + rc = xudc_dma_send(ep, req, bufferptr, bufferlen); + else + rc = xudc_dma_receive(ep, req, bufferptr, bufferlen); + return rc; + } + /* Put the transmit buffer into the correct ping-pong buffer.*/ + if (!ep->curbufnum && !ep->buffer0ready) { + /* Get the Buffer address and copy the transmit data.*/ + eprambase = (u32 __force *)(udc->addr + ep->rambase); + if (ep->is_in) { + memcpy(eprambase, bufferptr, bytestosend); + udc->write_fn(udc->addr, ep->offset + + XUSB_EP_BUF0COUNT_OFFSET, bufferlen); + } else { + memcpy(bufferptr, eprambase, bytestosend); + } + /* + * Enable the buffer for transmission. + */ + udc->write_fn(udc->addr, XUSB_BUFFREADY_OFFSET, + 1 << ep->epnumber); + ep->buffer0ready = 1; + ep->curbufnum = 1; + } else if (ep->curbufnum && !ep->buffer1ready) { + /* Get the Buffer address and copy the transmit data.*/ + eprambase = (u32 __force *)(udc->addr + ep->rambase + + ep->ep_usb.maxpacket); + if (ep->is_in) { + memcpy(eprambase, bufferptr, bytestosend); + udc->write_fn(udc->addr, ep->offset + + XUSB_EP_BUF1COUNT_OFFSET, bufferlen); + } else { + memcpy(bufferptr, eprambase, bytestosend); + } + /* + * Enable the buffer for transmission. + */ + udc->write_fn(udc->addr, XUSB_BUFFREADY_OFFSET, + 1 << (ep->epnumber + XUSB_STATUS_EP_BUFF2_SHIFT)); + ep->buffer1ready = 1; + ep->curbufnum = 0; + } else { + /* None of the ping-pong buffers are ready currently */ + return -EAGAIN; + } + return rc; +} + +/** + * xudc_done - Exeutes the endpoint data transfer completion tasks. + * @ep: pointer to the usb device endpoint structure. + * @req: pointer to the usb request structure. + * @status: Status of the data transfer. + * + * Deletes the message from the queue and updates data transfer completion + * status. + */ +static void xudc_done(struct xusb_ep *ep, struct xusb_req *req, int status) +{ + struct xusb_udc *udc = ep->udc; + + list_del_init(&req->queue); + + if (req->usb_req.status == -EINPROGRESS) + req->usb_req.status = status; + else + status = req->usb_req.status; + + if (status && status != -ESHUTDOWN) + dev_dbg(udc->dev, "%s done %p, status %d\n", + ep->ep_usb.name, req, status); + /* unmap request if DMA is present*/ + if (udc->dma_enabled && ep->epnumber && req->usb_req.length) + usb_gadget_unmap_request(&udc->gadget, &req->usb_req, + ep->is_in); + + if (req->usb_req.complete) { + spin_unlock(&udc->lock); + req->usb_req.complete(&ep->ep_usb, &req->usb_req); + spin_lock(&udc->lock); + } +} + +/** + * xudc_read_fifo - Reads the data from the given endpoint buffer. + * @ep: pointer to the usb device endpoint structure. + * @req: pointer to the usb request structure. + * + * Return: 0 if request is completed and -EAGAIN if not completed. + * + * Pulls OUT packet data from the endpoint buffer. + */ +static int xudc_read_fifo(struct xusb_ep *ep, struct xusb_req *req) +{ + u8 *buf; + u32 is_short, count, bufferspace; + u8 bufoffset; + u8 two_pkts = 0; + int ret; + int retval = -EAGAIN; + struct xusb_udc *udc = ep->udc; + + if (ep->buffer0ready && ep->buffer1ready) { + dev_dbg(udc->dev, "Packet NOT ready!\n"); + return retval; + } +top: + if (ep->curbufnum) + bufoffset = XUSB_EP_BUF1COUNT_OFFSET; + else + bufoffset = XUSB_EP_BUF0COUNT_OFFSET; + + count = udc->read_fn(udc->addr + ep->offset + bufoffset); + + if (!ep->buffer0ready && !ep->buffer1ready) + two_pkts = 1; + + buf = req->usb_req.buf + req->usb_req.actual; + prefetchw(buf); + bufferspace = req->usb_req.length - req->usb_req.actual; + is_short = count < ep->ep_usb.maxpacket; + + if (unlikely(!bufferspace)) { + /* + * This happens when the driver's buffer + * is smaller than what the host sent. + * discard the extra data. + */ + if (req->usb_req.status != -EOVERFLOW) + dev_dbg(udc->dev, "%s overflow %d\n", + ep->ep_usb.name, count); + req->usb_req.status = -EOVERFLOW; + xudc_done(ep, req, -EOVERFLOW); + return 0; + } + + ret = xudc_eptxrx(ep, req, buf, count); + switch (ret) { + case 0: + req->usb_req.actual += min(count, bufferspace); + dev_dbg(udc->dev, "read %s, %d bytes%s req %p %d/%d\n", + ep->ep_usb.name, count, is_short ? "/S" : "", req, + req->usb_req.actual, req->usb_req.length); + bufferspace -= count; + /* Completion */ + if ((req->usb_req.actual == req->usb_req.length) || is_short) { + if (udc->dma_enabled && req->usb_req.length) + dma_sync_single_for_cpu(udc->dev, + req->usb_req.dma, + req->usb_req.actual, + DMA_FROM_DEVICE); + xudc_done(ep, req, 0); + return 0; + } + if (two_pkts) { + two_pkts = 0; + goto top; + } + break; + case -EAGAIN: + dev_dbg(udc->dev, "receive busy\n"); + break; + case -EINVAL: + case -ETIMEDOUT: + /* DMA error, dequeue the request */ + xudc_done(ep, req, -ECONNRESET); + retval = 0; + break; + } + + return retval; +} + +/** + * xudc_write_fifo - Writes data into the given endpoint buffer. + * @ep: pointer to the usb device endpoint structure. + * @req: pointer to the usb request structure. + * + * Return: 0 if request is completed and -EAGAIN if not completed. + * + * Loads endpoint buffer for an IN packet. + */ +static int xudc_write_fifo(struct xusb_ep *ep, struct xusb_req *req) +{ + u32 max; + u32 length; + int ret; + int retval = -EAGAIN; + struct xusb_udc *udc = ep->udc; + int is_last, is_short = 0; + u8 *buf; + + max = le16_to_cpu(ep->desc->wMaxPacketSize); + buf = req->usb_req.buf + req->usb_req.actual; + prefetch(buf); + length = req->usb_req.length - req->usb_req.actual; + length = min(length, max); + + ret = xudc_eptxrx(ep, req, buf, length); + switch (ret) { + case 0: + req->usb_req.actual += length; + if (unlikely(length != max)) { + is_last = is_short = 1; + } else { + if (likely(req->usb_req.length != + req->usb_req.actual) || req->usb_req.zero) + is_last = 0; + else + is_last = 1; + } + dev_dbg(udc->dev, "%s: wrote %s %d bytes%s%s %d left %p\n", + __func__, ep->ep_usb.name, length, is_last ? "/L" : "", + is_short ? "/S" : "", + req->usb_req.length - req->usb_req.actual, req); + /* completion */ + if (is_last) { + xudc_done(ep, req, 0); + retval = 0; + } + break; + case -EAGAIN: + dev_dbg(udc->dev, "Send busy\n"); + break; + case -EINVAL: + case -ETIMEDOUT: + /* DMA error, dequeue the request */ + xudc_done(ep, req, -ECONNRESET); + retval = 0; + break; + } + + return retval; +} + +/** + * xudc_nuke - Cleans up the data transfer message list. + * @ep: pointer to the usb device endpoint structure. + * @status: Status of the data transfer. + */ +static void xudc_nuke(struct xusb_ep *ep, int status) +{ + struct xusb_req *req; + + while (!list_empty(&ep->queue)) { + req = list_first_entry(&ep->queue, struct xusb_req, queue); + xudc_done(ep, req, status); + } +} + +/** + * xudc_ep_set_halt - Stalls/unstalls the given endpoint. + * @_ep: pointer to the usb device endpoint structure. + * @value: value to indicate stall/unstall. + * + * Return: 0 for success and error value on failure + */ +static int xudc_ep_set_halt(struct usb_ep *_ep, int value) +{ + struct xusb_ep *ep = to_xusb_ep(_ep); + struct xusb_udc *udc; + unsigned long flags; + u32 epcfgreg; + + if (!_ep || (!ep->desc && ep->epnumber)) { + pr_debug("%s: bad ep or descriptor\n", __func__); + return -EINVAL; + } + udc = ep->udc; + + if (ep->is_in && (!list_empty(&ep->queue)) && value) { + dev_dbg(udc->dev, "requests pending can't halt\n"); + return -EAGAIN; + } + + if (ep->buffer0ready || ep->buffer1ready) { + dev_dbg(udc->dev, "HW buffers busy can't halt\n"); + return -EAGAIN; + } + + spin_lock_irqsave(&udc->lock, flags); + + if (value) { + /* Stall the device.*/ + epcfgreg = udc->read_fn(udc->addr + ep->offset); + epcfgreg |= XUSB_EP_CFG_STALL_MASK; + udc->write_fn(udc->addr, ep->offset, epcfgreg); + } else { + /* Unstall the device.*/ + epcfgreg = udc->read_fn(udc->addr + ep->offset); + epcfgreg &= ~XUSB_EP_CFG_STALL_MASK; + udc->write_fn(udc->addr, ep->offset, epcfgreg); + if (ep->epnumber) { + /* Reset the toggle bit.*/ + epcfgreg = udc->read_fn(ep->udc->addr + ep->offset); + epcfgreg &= ~XUSB_EP_CFG_DATA_TOGGLE_MASK; + udc->write_fn(udc->addr, ep->offset, epcfgreg); + } + } + + spin_unlock_irqrestore(&udc->lock, flags); + return 0; +} + +/** + * xudc_ep_enable - Enables the given endpoint. + * @ep: pointer to the xusb endpoint structure. + * @desc: pointer to usb endpoint descriptor. + * + * Return: 0 for success and error value on failure + */ +static int __xudc_ep_enable(struct xusb_ep *ep, + const struct usb_endpoint_descriptor *desc) +{ + struct xusb_udc *udc = ep->udc; + u32 tmp; + u32 epcfg; + u32 ier; + u16 maxpacket; + + ep->is_in = ((desc->bEndpointAddress & USB_DIR_IN) != 0); + /* Bit 3...0:endpoint number */ + ep->epnumber = (desc->bEndpointAddress & 0x0f); + ep->desc = desc; + ep->ep_usb.desc = desc; + tmp = desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK; + ep->ep_usb.maxpacket = maxpacket = le16_to_cpu(desc->wMaxPacketSize); + + switch (tmp) { + case USB_ENDPOINT_XFER_CONTROL: + dev_dbg(udc->dev, "only one control endpoint\n"); + /* NON- ISO */ + ep->is_iso = 0; + return -EINVAL; + case USB_ENDPOINT_XFER_INT: + /* NON- ISO */ + ep->is_iso = 0; + if (maxpacket > 64) { + dev_dbg(udc->dev, "bogus maxpacket %d\n", maxpacket); + return -EINVAL; + } + break; + case USB_ENDPOINT_XFER_BULK: + /* NON- ISO */ + ep->is_iso = 0; + if (!(is_power_of_2(maxpacket) && maxpacket >= 8 && + maxpacket <= 512)) { + dev_dbg(udc->dev, "bogus maxpacket %d\n", maxpacket); + return -EINVAL; + } + break; + case USB_ENDPOINT_XFER_ISOC: + /* ISO */ + ep->is_iso = 1; + break; + } + + ep->buffer0ready = 0; + ep->buffer1ready = 0; + ep->curbufnum = 0; + ep->rambase = rambase[ep->epnumber]; + xudc_epconfig(ep, udc); + + dev_dbg(udc->dev, "Enable Endpoint %d max pkt is %d\n", + ep->epnumber, maxpacket); + + /* Enable the End point.*/ + epcfg = udc->read_fn(udc->addr + ep->offset); + epcfg |= XUSB_EP_CFG_VALID_MASK; + udc->write_fn(udc->addr, ep->offset, epcfg); + if (ep->epnumber) + ep->rambase <<= 2; + + /* Enable buffer completion interrupts for endpoint */ + ier = udc->read_fn(udc->addr + XUSB_IER_OFFSET); + ier |= (XUSB_STATUS_INTR_BUFF_COMP_SHIFT_MASK << ep->epnumber); + udc->write_fn(udc->addr, XUSB_IER_OFFSET, ier); + + /* for OUT endpoint set buffers ready to receive */ + if (ep->epnumber && !ep->is_in) { + udc->write_fn(udc->addr, XUSB_BUFFREADY_OFFSET, + 1 << ep->epnumber); + ep->buffer0ready = 1; + udc->write_fn(udc->addr, XUSB_BUFFREADY_OFFSET, + (1 << (ep->epnumber + + XUSB_STATUS_EP_BUFF2_SHIFT))); + ep->buffer1ready = 1; + } + + return 0; +} + +/** + * xudc_ep_enable - Enables the given endpoint. + * @_ep: pointer to the usb endpoint structure. + * @desc: pointer to usb endpoint descriptor. + * + * Return: 0 for success and error value on failure + */ +static int xudc_ep_enable(struct usb_ep *_ep, + const struct usb_endpoint_descriptor *desc) +{ + struct xusb_ep *ep; + struct xusb_udc *udc; + unsigned long flags; + int ret; + + if (!_ep || !desc || desc->bDescriptorType != USB_DT_ENDPOINT) { + pr_debug("%s: bad ep or descriptor\n", __func__); + return -EINVAL; + } + + ep = to_xusb_ep(_ep); + udc = ep->udc; + + if (!udc->driver || udc->gadget.speed == USB_SPEED_UNKNOWN) { + dev_dbg(udc->dev, "bogus device state\n"); + return -ESHUTDOWN; + } + + spin_lock_irqsave(&udc->lock, flags); + ret = __xudc_ep_enable(ep, desc); + spin_unlock_irqrestore(&udc->lock, flags); + + return ret; +} + +/** + * xudc_ep_disable - Disables the given endpoint. + * @_ep: pointer to the usb endpoint structure. + * + * Return: 0 for success and error value on failure + */ +static int xudc_ep_disable(struct usb_ep *_ep) +{ + struct xusb_ep *ep; + unsigned long flags; + u32 epcfg; + struct xusb_udc *udc; + + if (!_ep) { + pr_debug("%s: invalid ep\n", __func__); + return -EINVAL; + } + + ep = to_xusb_ep(_ep); + udc = ep->udc; + + spin_lock_irqsave(&udc->lock, flags); + + xudc_nuke(ep, -ESHUTDOWN); + + /* Restore the endpoint's pristine config */ + ep->desc = NULL; + ep->ep_usb.desc = NULL; + + dev_dbg(udc->dev, "USB Ep %d disable\n ", ep->epnumber); + /* Disable the endpoint.*/ + epcfg = udc->read_fn(udc->addr + ep->offset); + epcfg &= ~XUSB_EP_CFG_VALID_MASK; + udc->write_fn(udc->addr, ep->offset, epcfg); + + spin_unlock_irqrestore(&udc->lock, flags); + return 0; +} + +/** + * xudc_ep_alloc_request - Initializes the request queue. + * @_ep: pointer to the usb endpoint structure. + * @gfp_flags: Flags related to the request call. + * + * Return: pointer to request structure on success and a NULL on failure. + */ +static struct usb_request *xudc_ep_alloc_request(struct usb_ep *_ep, + gfp_t gfp_flags) +{ + struct xusb_ep *ep = to_xusb_ep(_ep); + struct xusb_udc *udc; + struct xusb_req *req; + + udc = ep->udc; + req = kzalloc(sizeof(*req), gfp_flags); + if (!req) { + dev_err(udc->dev, "%s:not enough memory", __func__); + return NULL; + } + + req->ep = ep; + INIT_LIST_HEAD(&req->queue); + return &req->usb_req; +} + +/** + * xudc_free_request - Releases the request from queue. + * @_ep: pointer to the usb device endpoint structure. + * @_req: pointer to the usb request structure. + */ +static void xudc_free_request(struct usb_ep *_ep, struct usb_request *_req) +{ + struct xusb_req *req = to_xusb_req(_req); + + kfree(req); +} + +/** + * xudc_ep0_queue - Adds the request to endpoint 0 queue. + * @ep0: pointer to the xusb endpoint 0 structure. + * @req: pointer to the xusb request structure. + * + * Return: 0 for success and error value on failure + */ +static int __xudc_ep0_queue(struct xusb_ep *ep0, struct xusb_req *req) +{ + struct xusb_udc *udc = ep0->udc; + u32 length; + u8 *corebuf; + + if (!udc->driver || udc->gadget.speed == USB_SPEED_UNKNOWN) { + dev_dbg(udc->dev, "%s, bogus device state\n", __func__); + return -EINVAL; + } + if (!list_empty(&ep0->queue)) { + dev_dbg(udc->dev, "%s:ep0 busy\n", __func__); + return -EBUSY; + } + + req->usb_req.status = -EINPROGRESS; + req->usb_req.actual = 0; + + list_add_tail(&req->queue, &ep0->queue); + + if (udc->setup.bRequestType & USB_DIR_IN) { + prefetch(req->usb_req.buf); + length = req->usb_req.length; + corebuf = (void __force *) ((ep0->rambase << 2) + + udc->addr); + length = req->usb_req.actual = min_t(u32, length, + EP0_MAX_PACKET); + memcpy(corebuf, req->usb_req.buf, length); + udc->write_fn(udc->addr, XUSB_EP_BUF0COUNT_OFFSET, length); + udc->write_fn(udc->addr, XUSB_BUFFREADY_OFFSET, 1); + } else { + if (udc->setup.wLength) { + /* Enable EP0 buffer to receive data */ + udc->write_fn(udc->addr, XUSB_EP_BUF0COUNT_OFFSET, 0); + udc->write_fn(udc->addr, XUSB_BUFFREADY_OFFSET, 1); + } else { + xudc_wrstatus(udc); + } + } + + return 0; +} + +/** + * xudc_ep0_queue - Adds the request to endpoint 0 queue. + * @_ep: pointer to the usb endpoint 0 structure. + * @_req: pointer to the usb request structure. + * @gfp_flags: Flags related to the request call. + * + * Return: 0 for success and error value on failure + */ +static int xudc_ep0_queue(struct usb_ep *_ep, struct usb_request *_req, + gfp_t gfp_flags) +{ + struct xusb_req *req = to_xusb_req(_req); + struct xusb_ep *ep0 = to_xusb_ep(_ep); + struct xusb_udc *udc = ep0->udc; + unsigned long flags; + int ret; + + spin_lock_irqsave(&udc->lock, flags); + ret = __xudc_ep0_queue(ep0, req); + spin_unlock_irqrestore(&udc->lock, flags); + + return ret; +} + +/** + * xudc_ep_queue - Adds the request to endpoint queue. + * @_ep: pointer to the usb endpoint structure. + * @_req: pointer to the usb request structure. + * @gfp_flags: Flags related to the request call. + * + * Return: 0 for success and error value on failure + */ +static int xudc_ep_queue(struct usb_ep *_ep, struct usb_request *_req, + gfp_t gfp_flags) +{ + struct xusb_req *req = to_xusb_req(_req); + struct xusb_ep *ep = to_xusb_ep(_ep); + struct xusb_udc *udc = ep->udc; + int ret; + unsigned long flags; + + if (!ep->desc) { + dev_dbg(udc->dev, "%s:queing request to disabled %s\n", + __func__, ep->name); + return -ESHUTDOWN; + } + + if (!udc->driver || udc->gadget.speed == USB_SPEED_UNKNOWN) { + dev_dbg(udc->dev, "%s, bogus device state\n", __func__); + return -EINVAL; + } + + spin_lock_irqsave(&udc->lock, flags); + + _req->status = -EINPROGRESS; + _req->actual = 0; + + if (udc->dma_enabled) { + ret = usb_gadget_map_request(&udc->gadget, &req->usb_req, + ep->is_in); + if (ret) { + dev_dbg(udc->dev, "gadget_map failed ep%d\n", + ep->epnumber); + spin_unlock_irqrestore(&udc->lock, flags); + return -EAGAIN; + } + } + + if (list_empty(&ep->queue)) { + if (ep->is_in) { + dev_dbg(udc->dev, "xudc_write_fifo from ep_queue\n"); + if (!xudc_write_fifo(ep, req)) + req = NULL; + } else { + dev_dbg(udc->dev, "xudc_read_fifo from ep_queue\n"); + if (!xudc_read_fifo(ep, req)) + req = NULL; + } + } + + if (req != NULL) + list_add_tail(&req->queue, &ep->queue); + + spin_unlock_irqrestore(&udc->lock, flags); + return 0; +} + +/** + * xudc_ep_dequeue - Removes the request from the queue. + * @_ep: pointer to the usb device endpoint structure. + * @_req: pointer to the usb request structure. + * + * Return: 0 for success and error value on failure + */ +static int xudc_ep_dequeue(struct usb_ep *_ep, struct usb_request *_req) +{ + struct xusb_ep *ep = to_xusb_ep(_ep); + struct xusb_req *req = to_xusb_req(_req); + struct xusb_udc *udc = ep->udc; + unsigned long flags; + + spin_lock_irqsave(&udc->lock, flags); + /* Make sure it's actually queued on this endpoint */ + list_for_each_entry(req, &ep->queue, queue) { + if (&req->usb_req == _req) + break; + } + if (&req->usb_req != _req) { + spin_unlock_irqrestore(&ep->udc->lock, flags); + return -EINVAL; + } + xudc_done(ep, req, -ECONNRESET); + spin_unlock_irqrestore(&udc->lock, flags); + + return 0; +} + +/** + * xudc_ep0_enable - Enables the given endpoint. + * @ep: pointer to the usb endpoint structure. + * @desc: pointer to usb endpoint descriptor. + * + * Return: error always. + * + * endpoint 0 enable should not be called by gadget layer. + */ +static int xudc_ep0_enable(struct usb_ep *ep, + const struct usb_endpoint_descriptor *desc) +{ + return -EINVAL; +} + +/** + * xudc_ep0_disable - Disables the given endpoint. + * @ep: pointer to the usb endpoint structure. + * + * Return: error always. + * + * endpoint 0 disable should not be called by gadget layer. + */ +static int xudc_ep0_disable(struct usb_ep *ep) +{ + return -EINVAL; +} + +static const struct usb_ep_ops xusb_ep0_ops = { + .enable = xudc_ep0_enable, + .disable = xudc_ep0_disable, + .alloc_request = xudc_ep_alloc_request, + .free_request = xudc_free_request, + .queue = xudc_ep0_queue, + .dequeue = xudc_ep_dequeue, + .set_halt = xudc_ep_set_halt, +}; + +static const struct usb_ep_ops xusb_ep_ops = { + .enable = xudc_ep_enable, + .disable = xudc_ep_disable, + .alloc_request = xudc_ep_alloc_request, + .free_request = xudc_free_request, + .queue = xudc_ep_queue, + .dequeue = xudc_ep_dequeue, + .set_halt = xudc_ep_set_halt, +}; + +/** + * xudc_get_frame - Reads the current usb frame number. + * @gadget: pointer to the usb gadget structure. + * + * Return: current frame number for success and error value on failure. + */ +static int xudc_get_frame(struct usb_gadget *gadget) +{ + struct xusb_udc *udc; + int frame; + + if (!gadget) + return -ENODEV; + + udc = to_udc(gadget); + frame = udc->read_fn(udc->addr + XUSB_FRAMENUM_OFFSET); + return frame; +} + +/** + * xudc_wakeup - Send remote wakeup signal to host + * @gadget: pointer to the usb gadget structure. + * + * Return: 0 on success and error on failure + */ +static int xudc_wakeup(struct usb_gadget *gadget) +{ + struct xusb_udc *udc = to_udc(gadget); + u32 crtlreg; + int status = -EINVAL; + unsigned long flags; + + spin_lock_irqsave(&udc->lock, flags); + + /* Remote wake up not enabled by host */ + if (!udc->remote_wkp) + goto done; + + crtlreg = udc->read_fn(udc->addr + XUSB_CONTROL_OFFSET); + crtlreg |= XUSB_CONTROL_USB_RMTWAKE_MASK; + /* set remote wake up bit */ + udc->write_fn(udc->addr, XUSB_CONTROL_OFFSET, crtlreg); + /* + * wait for a while and reset remote wake up bit since this bit + * is not cleared by HW after sending remote wakeup to host. + */ + mdelay(2); + + crtlreg &= ~XUSB_CONTROL_USB_RMTWAKE_MASK; + udc->write_fn(udc->addr, XUSB_CONTROL_OFFSET, crtlreg); + status = 0; +done: + spin_unlock_irqrestore(&udc->lock, flags); + return status; +} + +/** + * xudc_pullup - start/stop USB traffic + * @gadget: pointer to the usb gadget structure. + * @is_on: flag to start or stop + * + * Return: 0 always + * + * This function starts/stops SIE engine of IP based on is_on. + */ +static int xudc_pullup(struct usb_gadget *gadget, int is_on) +{ + struct xusb_udc *udc = to_udc(gadget); + unsigned long flags; + u32 crtlreg; + + spin_lock_irqsave(&udc->lock, flags); + + crtlreg = udc->read_fn(udc->addr + XUSB_CONTROL_OFFSET); + if (is_on) + crtlreg |= XUSB_CONTROL_USB_READY_MASK; + else + crtlreg &= ~XUSB_CONTROL_USB_READY_MASK; + + udc->write_fn(udc->addr, XUSB_CONTROL_OFFSET, crtlreg); + + spin_unlock_irqrestore(&udc->lock, flags); + + return 0; +} + +/** + * xudc_eps_init - initialize endpoints. + * @udc: pointer to the usb device controller structure. + */ +static void xudc_eps_init(struct xusb_udc *udc) +{ + u32 ep_number; + + INIT_LIST_HEAD(&udc->gadget.ep_list); + + for (ep_number = 0; ep_number < XUSB_MAX_ENDPOINTS; ep_number++) { + struct xusb_ep *ep = &udc->ep[ep_number]; + + if (ep_number) { + list_add_tail(&ep->ep_usb.ep_list, + &udc->gadget.ep_list); + usb_ep_set_maxpacket_limit(&ep->ep_usb, + (unsigned short) ~0); + snprintf(ep->name, EPNAME_SIZE, "ep%d", ep_number); + ep->ep_usb.name = ep->name; + ep->ep_usb.ops = &xusb_ep_ops; + } else { + ep->ep_usb.name = ep0name; + usb_ep_set_maxpacket_limit(&ep->ep_usb, EP0_MAX_PACKET); + ep->ep_usb.ops = &xusb_ep0_ops; + } + + ep->udc = udc; + ep->epnumber = ep_number; + ep->desc = NULL; + /* + * The configuration register address offset between + * each endpoint is 0x10. + */ + ep->offset = XUSB_EP0_CONFIG_OFFSET + (ep_number * 0x10); + ep->is_in = 0; + ep->is_iso = 0; + ep->maxpacket = 0; + xudc_epconfig(ep, udc); + + /* Initialize one queue per endpoint */ + INIT_LIST_HEAD(&ep->queue); + } +} + +/** + * xudc_stop_activity - Stops any further activity on the device. + * @udc: pointer to the usb device controller structure. + */ +static void xudc_stop_activity(struct xusb_udc *udc) +{ + int i; + struct xusb_ep *ep; + + for (i = 0; i < XUSB_MAX_ENDPOINTS; i++) { + ep = &udc->ep[i]; + xudc_nuke(ep, -ESHUTDOWN); + } +} + +/** + * xudc_start - Starts the device. + * @gadget: pointer to the usb gadget structure + * @driver: pointer to gadget driver structure + * + * Return: zero on success and error on failure + */ +static int xudc_start(struct usb_gadget *gadget, + struct usb_gadget_driver *driver) +{ + struct xusb_udc *udc = to_udc(gadget); + struct xusb_ep *ep0 = &udc->ep[XUSB_EP_NUMBER_ZERO]; + const struct usb_endpoint_descriptor *desc = &config_bulk_out_desc; + unsigned long flags; + int ret = 0; + + spin_lock_irqsave(&udc->lock, flags); + + if (udc->driver) { + dev_err(udc->dev, "%s is already bound to %s\n", + udc->gadget.name, udc->driver->driver.name); + ret = -EBUSY; + goto err; + } + + /* hook up the driver */ + udc->driver = driver; + udc->gadget.speed = driver->max_speed; + + /* Enable the control endpoint. */ + ret = __xudc_ep_enable(ep0, desc); + + /* Set device address and remote wakeup to 0 */ + udc->write_fn(udc->addr, XUSB_ADDRESS_OFFSET, 0); + udc->remote_wkp = 0; +err: + spin_unlock_irqrestore(&udc->lock, flags); + return ret; +} + +/** + * xudc_stop - stops the device. + * @gadget: pointer to the usb gadget structure + * @driver: pointer to usb gadget driver structure + * + * Return: zero always + */ +static int xudc_stop(struct usb_gadget *gadget, + struct usb_gadget_driver *driver) +{ + struct xusb_udc *udc = to_udc(gadget); + unsigned long flags; + + spin_lock_irqsave(&udc->lock, flags); + + udc->gadget.speed = USB_SPEED_UNKNOWN; + udc->driver = NULL; + + /* Set device address and remote wakeup to 0 */ + udc->write_fn(udc->addr, XUSB_ADDRESS_OFFSET, 0); + udc->remote_wkp = 0; + + xudc_stop_activity(udc); + + spin_unlock_irqrestore(&udc->lock, flags); + + return 0; +} + +static const struct usb_gadget_ops xusb_udc_ops = { + .get_frame = xudc_get_frame, + .wakeup = xudc_wakeup, + .pullup = xudc_pullup, + .udc_start = xudc_start, + .udc_stop = xudc_stop, +}; + +/** + * xudc_clear_stall_all_ep - clears stall of every endpoint. + * @udc: pointer to the udc structure. + */ +static void xudc_clear_stall_all_ep(struct xusb_udc *udc) +{ + struct xusb_ep *ep; + u32 epcfgreg; + int i; + + for (i = 0; i < XUSB_MAX_ENDPOINTS; i++) { + ep = &udc->ep[i]; + epcfgreg = udc->read_fn(udc->addr + ep->offset); + epcfgreg &= ~XUSB_EP_CFG_STALL_MASK; + udc->write_fn(udc->addr, ep->offset, epcfgreg); + if (ep->epnumber) { + /* Reset the toggle bit.*/ + epcfgreg = udc->read_fn(udc->addr + ep->offset); + epcfgreg &= ~XUSB_EP_CFG_DATA_TOGGLE_MASK; + udc->write_fn(udc->addr, ep->offset, epcfgreg); + } + } +} + +/** + * xudc_startup_handler - The usb device controller interrupt handler. + * @udc: pointer to the udc structure. + * @intrstatus: The mask value containing the interrupt sources. + * + * This function handles the RESET,SUSPEND,RESUME and DISCONNECT interrupts. + */ +static void xudc_startup_handler(struct xusb_udc *udc, u32 intrstatus) +{ + u32 intrreg; + + if (intrstatus & XUSB_STATUS_RESET_MASK) { + + dev_dbg(udc->dev, "Reset\n"); + + if (intrstatus & XUSB_STATUS_HIGH_SPEED_MASK) + udc->gadget.speed = USB_SPEED_HIGH; + else + udc->gadget.speed = USB_SPEED_FULL; + + xudc_stop_activity(udc); + xudc_clear_stall_all_ep(udc); + udc->write_fn(udc->addr, XUSB_TESTMODE_OFFSET, 0); + + /* Set device address and remote wakeup to 0 */ + udc->write_fn(udc->addr, XUSB_ADDRESS_OFFSET, 0); + udc->remote_wkp = 0; + + /* Enable the suspend, resume and disconnect */ + intrreg = udc->read_fn(udc->addr + XUSB_IER_OFFSET); + intrreg |= XUSB_STATUS_SUSPEND_MASK | XUSB_STATUS_RESUME_MASK | + XUSB_STATUS_DISCONNECT_MASK; + udc->write_fn(udc->addr, XUSB_IER_OFFSET, intrreg); + } + if (intrstatus & XUSB_STATUS_SUSPEND_MASK) { + + dev_dbg(udc->dev, "Suspend\n"); + + /* Enable the reset, resume and disconnect */ + intrreg = udc->read_fn(udc->addr + XUSB_IER_OFFSET); + intrreg |= XUSB_STATUS_RESET_MASK | XUSB_STATUS_RESUME_MASK | + XUSB_STATUS_DISCONNECT_MASK; + udc->write_fn(udc->addr, XUSB_IER_OFFSET, intrreg); + + udc->usb_state = USB_STATE_SUSPENDED; + + if (udc->driver->suspend) { + spin_unlock(&udc->lock); + udc->driver->suspend(&udc->gadget); + spin_lock(&udc->lock); + } + } + if (intrstatus & XUSB_STATUS_RESUME_MASK) { + bool condition = (udc->usb_state != USB_STATE_SUSPENDED); + + dev_WARN_ONCE(udc->dev, condition, + "Resume IRQ while not suspended\n"); + + dev_dbg(udc->dev, "Resume\n"); + + /* Enable the reset, suspend and disconnect */ + intrreg = udc->read_fn(udc->addr + XUSB_IER_OFFSET); + intrreg |= XUSB_STATUS_RESET_MASK | XUSB_STATUS_SUSPEND_MASK | + XUSB_STATUS_DISCONNECT_MASK; + udc->write_fn(udc->addr, XUSB_IER_OFFSET, intrreg); + + udc->usb_state = 0; + + if (udc->driver->resume) { + spin_unlock(&udc->lock); + udc->driver->resume(&udc->gadget); + spin_lock(&udc->lock); + } + } + if (intrstatus & XUSB_STATUS_DISCONNECT_MASK) { + + dev_dbg(udc->dev, "Disconnect\n"); + + /* Enable the reset, resume and suspend */ + intrreg = udc->read_fn(udc->addr + XUSB_IER_OFFSET); + intrreg |= XUSB_STATUS_RESET_MASK | XUSB_STATUS_RESUME_MASK | + XUSB_STATUS_SUSPEND_MASK; + udc->write_fn(udc->addr, XUSB_IER_OFFSET, intrreg); + + if (udc->driver && udc->driver->disconnect) { + spin_unlock(&udc->lock); + udc->driver->disconnect(&udc->gadget); + spin_lock(&udc->lock); + } + } +} + +/** + * xudc_ep0_stall - Stall endpoint zero. + * @udc: pointer to the udc structure. + * + * This function stalls endpoint zero. + */ +static void xudc_ep0_stall(struct xusb_udc *udc) +{ + u32 epcfgreg; + struct xusb_ep *ep0 = &udc->ep[XUSB_EP_NUMBER_ZERO]; + + epcfgreg = udc->read_fn(udc->addr + ep0->offset); + epcfgreg |= XUSB_EP_CFG_STALL_MASK; + udc->write_fn(udc->addr, ep0->offset, epcfgreg); +} + +/** + * xudc_setaddress - executes SET_ADDRESS command + * @udc: pointer to the udc structure. + * + * This function executes USB SET_ADDRESS command + */ +static void xudc_setaddress(struct xusb_udc *udc) +{ + struct xusb_ep *ep0 = &udc->ep[0]; + struct xusb_req *req = udc->req; + int ret; + + req->usb_req.length = 0; + ret = __xudc_ep0_queue(ep0, req); + if (ret == 0) + return; + + dev_err(udc->dev, "Can't respond to SET ADDRESS request\n"); + xudc_ep0_stall(udc); +} + +/** + * xudc_getstatus - executes GET_STATUS command + * @udc: pointer to the udc structure. + * + * This function executes USB GET_STATUS command + */ +static void xudc_getstatus(struct xusb_udc *udc) +{ + struct xusb_ep *ep0 = &udc->ep[0]; + struct xusb_req *req = udc->req; + struct xusb_ep *target_ep; + u16 status = 0; + u32 epcfgreg; + int epnum; + u32 halt; + int ret; + + switch (udc->setup.bRequestType & USB_RECIP_MASK) { + case USB_RECIP_DEVICE: + /* Get device status */ + status = 1 << USB_DEVICE_SELF_POWERED; + if (udc->remote_wkp) + status |= (1 << USB_DEVICE_REMOTE_WAKEUP); + break; + case USB_RECIP_INTERFACE: + break; + case USB_RECIP_ENDPOINT: + epnum = udc->setup.wIndex & USB_ENDPOINT_NUMBER_MASK; + target_ep = &udc->ep[epnum]; + epcfgreg = udc->read_fn(udc->addr + target_ep->offset); + halt = epcfgreg & XUSB_EP_CFG_STALL_MASK; + if (udc->setup.wIndex & USB_DIR_IN) { + if (!target_ep->is_in) + goto stall; + } else { + if (target_ep->is_in) + goto stall; + } + if (halt) + status = 1 << USB_ENDPOINT_HALT; + break; + default: + goto stall; + } + + req->usb_req.length = 2; + *(u16 *)req->usb_req.buf = cpu_to_le16(status); + ret = __xudc_ep0_queue(ep0, req); + if (ret == 0) + return; +stall: + dev_err(udc->dev, "Can't respond to getstatus request\n"); + xudc_ep0_stall(udc); +} + +/** + * xudc_set_clear_feature - Executes the set feature and clear feature commands. + * @udc: pointer to the usb device controller structure. + * + * Processes the SET_FEATURE and CLEAR_FEATURE commands. + */ +static void xudc_set_clear_feature(struct xusb_udc *udc) +{ + struct xusb_ep *ep0 = &udc->ep[0]; + struct xusb_req *req = udc->req; + struct xusb_ep *target_ep; + u8 endpoint; + u8 outinbit; + u32 epcfgreg; + int flag = (udc->setup.bRequest == USB_REQ_SET_FEATURE ? 1 : 0); + int ret; + + switch (udc->setup.bRequestType) { + case USB_RECIP_DEVICE: + switch (udc->setup.wValue) { + case USB_DEVICE_TEST_MODE: + /* + * The Test Mode will be executed + * after the status phase. + */ + break; + case USB_DEVICE_REMOTE_WAKEUP: + if (flag) + udc->remote_wkp = 1; + else + udc->remote_wkp = 0; + break; + default: + xudc_ep0_stall(udc); + break; + } + break; + case USB_RECIP_ENDPOINT: + if (!udc->setup.wValue) { + endpoint = udc->setup.wIndex & USB_ENDPOINT_NUMBER_MASK; + target_ep = &udc->ep[endpoint]; + outinbit = udc->setup.wIndex & USB_ENDPOINT_DIR_MASK; + outinbit = outinbit >> 7; + + /* Make sure direction matches.*/ + if (outinbit != target_ep->is_in) { + xudc_ep0_stall(udc); + return; + } + epcfgreg = udc->read_fn(udc->addr + target_ep->offset); + if (!endpoint) { + /* Clear the stall.*/ + epcfgreg &= ~XUSB_EP_CFG_STALL_MASK; + udc->write_fn(udc->addr, + target_ep->offset, epcfgreg); + } else { + if (flag) { + epcfgreg |= XUSB_EP_CFG_STALL_MASK; + udc->write_fn(udc->addr, + target_ep->offset, + epcfgreg); + } else { + /* Unstall the endpoint.*/ + epcfgreg &= ~(XUSB_EP_CFG_STALL_MASK | + XUSB_EP_CFG_DATA_TOGGLE_MASK); + udc->write_fn(udc->addr, + target_ep->offset, + epcfgreg); + } + } + } + break; + default: + xudc_ep0_stall(udc); + return; + } + + req->usb_req.length = 0; + ret = __xudc_ep0_queue(ep0, req); + if (ret == 0) + return; + + dev_err(udc->dev, "Can't respond to SET/CLEAR FEATURE\n"); + xudc_ep0_stall(udc); +} + +/** + * xudc_handle_setup - Processes the setup packet. + * @udc: pointer to the usb device controller structure. + * + * Process setup packet and delegate to gadget layer. + */ +static void xudc_handle_setup(struct xusb_udc *udc) +{ + struct xusb_ep *ep0 = &udc->ep[0]; + struct usb_ctrlrequest setup; + u32 *ep0rambase; + + /* Load up the chapter 9 command buffer.*/ + ep0rambase = (u32 __force *) (udc->addr + XUSB_SETUP_PKT_ADDR_OFFSET); + memcpy(&setup, ep0rambase, 8); + + udc->setup = setup; + udc->setup.wValue = cpu_to_le16(setup.wValue); + udc->setup.wIndex = cpu_to_le16(setup.wIndex); + udc->setup.wLength = cpu_to_le16(setup.wLength); + + /* Clear previous requests */ + xudc_nuke(ep0, -ECONNRESET); + + if (udc->setup.bRequestType & USB_DIR_IN) { + /* Execute the get command.*/ + udc->setupseqrx = STATUS_PHASE; + udc->setupseqtx = DATA_PHASE; + } else { + /* Execute the put command.*/ + udc->setupseqrx = DATA_PHASE; + udc->setupseqtx = STATUS_PHASE; + } + + switch (udc->setup.bRequest) { + case USB_REQ_GET_STATUS: + /* Data+Status phase form udc */ + if ((udc->setup.bRequestType & + (USB_DIR_IN | USB_TYPE_MASK)) != + (USB_DIR_IN | USB_TYPE_STANDARD)) + break; + xudc_getstatus(udc); + return; + case USB_REQ_SET_ADDRESS: + /* Status phase from udc */ + if (udc->setup.bRequestType != (USB_DIR_OUT | + USB_TYPE_STANDARD | USB_RECIP_DEVICE)) + break; + xudc_setaddress(udc); + return; + case USB_REQ_CLEAR_FEATURE: + case USB_REQ_SET_FEATURE: + /* Requests with no data phase, status phase from udc */ + if ((udc->setup.bRequestType & USB_TYPE_MASK) + != USB_TYPE_STANDARD) + break; + xudc_set_clear_feature(udc); + return; + default: + break; + } + + spin_unlock(&udc->lock); + if (udc->driver->setup(&udc->gadget, &setup) < 0) + xudc_ep0_stall(udc); + spin_lock(&udc->lock); +} + +/** + * xudc_ep0_out - Processes the endpoint 0 OUT token. + * @udc: pointer to the usb device controller structure. + */ +static void xudc_ep0_out(struct xusb_udc *udc) +{ + struct xusb_ep *ep0 = &udc->ep[0]; + struct xusb_req *req; + u8 *ep0rambase; + unsigned int bytes_to_rx; + void *buffer; + + req = list_first_entry(&ep0->queue, struct xusb_req, queue); + + switch (udc->setupseqrx) { + case STATUS_PHASE: + /* + * This resets both state machines for the next + * Setup packet. + */ + udc->setupseqrx = SETUP_PHASE; + udc->setupseqtx = SETUP_PHASE; + req->usb_req.actual = req->usb_req.length; + xudc_done(ep0, req, 0); + break; + case DATA_PHASE: + bytes_to_rx = udc->read_fn(udc->addr + + XUSB_EP_BUF0COUNT_OFFSET); + /* Copy the data to be received from the DPRAM. */ + ep0rambase = (u8 __force *) (udc->addr + + (ep0->rambase << 2)); + buffer = req->usb_req.buf + req->usb_req.actual; + req->usb_req.actual = req->usb_req.actual + bytes_to_rx; + memcpy(buffer, ep0rambase, bytes_to_rx); + + if (req->usb_req.length == req->usb_req.actual) { + /* Data transfer completed get ready for Status stage */ + xudc_wrstatus(udc); + } else { + /* Enable EP0 buffer to receive data */ + udc->write_fn(udc->addr, XUSB_EP_BUF0COUNT_OFFSET, 0); + udc->write_fn(udc->addr, XUSB_BUFFREADY_OFFSET, 1); + } + break; + default: + break; + } +} + +/** + * xudc_ep0_in - Processes the endpoint 0 IN token. + * @udc: pointer to the usb device controller structure. + */ +static void xudc_ep0_in(struct xusb_udc *udc) +{ + struct xusb_ep *ep0 = &udc->ep[0]; + struct xusb_req *req; + unsigned int bytes_to_tx; + void *buffer; + u32 epcfgreg; + u16 count = 0; + u16 length; + u8 *ep0rambase; + u8 test_mode = udc->setup.wIndex >> 8; + + req = list_first_entry(&ep0->queue, struct xusb_req, queue); + bytes_to_tx = req->usb_req.length - req->usb_req.actual; + + switch (udc->setupseqtx) { + case STATUS_PHASE: + switch (udc->setup.bRequest) { + case USB_REQ_SET_ADDRESS: + /* Set the address of the device.*/ + udc->write_fn(udc->addr, XUSB_ADDRESS_OFFSET, + udc->setup.wValue); + break; + case USB_REQ_SET_FEATURE: + if (udc->setup.bRequestType == + USB_RECIP_DEVICE) { + if (udc->setup.wValue == + USB_DEVICE_TEST_MODE) + udc->write_fn(udc->addr, + XUSB_TESTMODE_OFFSET, + test_mode); + } + break; + } + req->usb_req.actual = req->usb_req.length; + xudc_done(ep0, req, 0); + break; + case DATA_PHASE: + if (!bytes_to_tx) { + /* + * We're done with data transfer, next + * will be zero length OUT with data toggle of + * 1. Setup data_toggle. + */ + epcfgreg = udc->read_fn(udc->addr + ep0->offset); + epcfgreg |= XUSB_EP_CFG_DATA_TOGGLE_MASK; + udc->write_fn(udc->addr, ep0->offset, epcfgreg); + udc->setupseqtx = STATUS_PHASE; + } else { + length = count = min_t(u32, bytes_to_tx, + EP0_MAX_PACKET); + /* Copy the data to be transmitted into the DPRAM. */ + ep0rambase = (u8 __force *) (udc->addr + + (ep0->rambase << 2)); + buffer = req->usb_req.buf + req->usb_req.actual; + req->usb_req.actual = req->usb_req.actual + length; + memcpy(ep0rambase, buffer, length); + } + udc->write_fn(udc->addr, XUSB_EP_BUF0COUNT_OFFSET, count); + udc->write_fn(udc->addr, XUSB_BUFFREADY_OFFSET, 1); + break; + default: + break; + } +} + +/** + * xudc_ctrl_ep_handler - Endpoint 0 interrupt handler. + * @udc: pointer to the udc structure. + * @intrstatus: It's the mask value for the interrupt sources on endpoint 0. + * + * Processes the commands received during enumeration phase. + */ +static void xudc_ctrl_ep_handler(struct xusb_udc *udc, u32 intrstatus) +{ + + if (intrstatus & XUSB_STATUS_SETUP_PACKET_MASK) { + xudc_handle_setup(udc); + } else { + if (intrstatus & XUSB_STATUS_FIFO_BUFF_RDY_MASK) + xudc_ep0_out(udc); + else if (intrstatus & XUSB_STATUS_FIFO_BUFF_FREE_MASK) + xudc_ep0_in(udc); + } +} + +/** + * xudc_nonctrl_ep_handler - Non control endpoint interrupt handler. + * @udc: pointer to the udc structure. + * @epnum: End point number for which the interrupt is to be processed + * @intrstatus: mask value for interrupt sources of endpoints other + * than endpoint 0. + * + * Processes the buffer completion interrupts. + */ +static void xudc_nonctrl_ep_handler(struct xusb_udc *udc, u8 epnum, + u32 intrstatus) +{ + + struct xusb_req *req; + struct xusb_ep *ep; + + ep = &udc->ep[epnum]; + /* Process the End point interrupts.*/ + if (intrstatus & (XUSB_STATUS_EP0_BUFF1_COMP_MASK << epnum)) + ep->buffer0ready = 0; + if (intrstatus & (XUSB_STATUS_EP0_BUFF2_COMP_MASK << epnum)) + ep->buffer1ready = 0; + + if (list_empty(&ep->queue)) + return; + + req = list_first_entry(&ep->queue, struct xusb_req, queue); + + if (ep->is_in) + xudc_write_fifo(ep, req); + else + xudc_read_fifo(ep, req); +} + +/** + * xudc_irq - The main interrupt handler. + * @irq: The interrupt number. + * @_udc: pointer to the usb device controller structure. + * + * Return: IRQ_HANDLED after the interrupt is handled. + */ +static irqreturn_t xudc_irq(int irq, void *_udc) +{ + struct xusb_udc *udc = _udc; + u32 intrstatus; + u32 ier; + u8 index; + u32 bufintr; + unsigned long flags; + + spin_lock_irqsave(&udc->lock, flags); + + /* + * Event interrupts are level sensitive hence first disable + * IER, read ISR and figure out active interrupts. + */ + ier = udc->read_fn(udc->addr + XUSB_IER_OFFSET); + ier &= ~XUSB_STATUS_INTR_EVENT_MASK; + udc->write_fn(udc->addr, XUSB_IER_OFFSET, ier); + + /* Read the Interrupt Status Register.*/ + intrstatus = udc->read_fn(udc->addr + XUSB_STATUS_OFFSET); + + /* Call the handler for the event interrupt.*/ + if (intrstatus & XUSB_STATUS_INTR_EVENT_MASK) { + /* + * Check if there is any action to be done for : + * - USB Reset received {XUSB_STATUS_RESET_MASK} + * - USB Suspend received {XUSB_STATUS_SUSPEND_MASK} + * - USB Resume received {XUSB_STATUS_RESUME_MASK} + * - USB Disconnect received {XUSB_STATUS_DISCONNECT_MASK} + */ + xudc_startup_handler(udc, intrstatus); + } + + /* Check the buffer completion interrupts */ + if (intrstatus & XUSB_STATUS_INTR_BUFF_COMP_ALL_MASK) { + /* Enable Reset, Suspend, Resume and Disconnect */ + ier = udc->read_fn(udc->addr + XUSB_IER_OFFSET); + ier |= XUSB_STATUS_INTR_EVENT_MASK; + udc->write_fn(udc->addr, XUSB_IER_OFFSET, ier); + + if (intrstatus & XUSB_STATUS_EP0_BUFF1_COMP_MASK) + xudc_ctrl_ep_handler(udc, intrstatus); + + for (index = 1; index < 8; index++) { + bufintr = ((intrstatus & + (XUSB_STATUS_EP1_BUFF1_COMP_MASK << + (index - 1))) || (intrstatus & + (XUSB_STATUS_EP1_BUFF2_COMP_MASK << + (index - 1)))); + if (bufintr) { + xudc_nonctrl_ep_handler(udc, index, + intrstatus); + } + } + } + + spin_unlock_irqrestore(&udc->lock, flags); + return IRQ_HANDLED; +} + +/** + * xudc_probe - The device probe function for driver initialization. + * @pdev: pointer to the platform device structure. + * + * Return: 0 for success and error value on failure + */ +static int xudc_probe(struct platform_device *pdev) +{ + struct device_node *np = pdev->dev.of_node; + struct resource *res; + struct xusb_udc *udc; + struct xusb_ep *ep0; + int irq; + int ret; + u32 ier; + u8 *buff; + + udc = devm_kzalloc(&pdev->dev, sizeof(*udc), GFP_KERNEL); + if (!udc) + return -ENOMEM; + + /* Create a dummy request for GET_STATUS, SET_ADDRESS */ + udc->req = devm_kzalloc(&pdev->dev, sizeof(struct xusb_req), + GFP_KERNEL); + if (!udc->req) + return -ENOMEM; + + buff = devm_kzalloc(&pdev->dev, STATUSBUFF_SIZE, GFP_KERNEL); + if (!buff) + return -ENOMEM; + + udc->req->usb_req.buf = buff; + + /* Map the registers */ + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + udc->addr = devm_ioremap_resource(&pdev->dev, res); + if (!udc->addr) + return -ENOMEM; + + irq = platform_get_irq(pdev, 0); + if (irq < 0) { + dev_err(&pdev->dev, "unable to get irq\n"); + return irq; + } + ret = devm_request_irq(&pdev->dev, irq, xudc_irq, 0, + dev_name(&pdev->dev), udc); + if (ret < 0) { + dev_dbg(&pdev->dev, "unable to request irq %d", irq); + goto fail; + } + + udc->dma_enabled = of_property_read_bool(np, "xlnx,has-builtin-dma"); + + /* Setup gadget structure */ + udc->gadget.ops = &xusb_udc_ops; + udc->gadget.max_speed = USB_SPEED_HIGH; + udc->gadget.speed = USB_SPEED_UNKNOWN; + udc->gadget.ep0 = &udc->ep[XUSB_EP_NUMBER_ZERO].ep_usb; + udc->gadget.name = driver_name; + + spin_lock_init(&udc->lock); + + /* Check for IP endianness */ + udc->write_fn = xudc_write32_be; + udc->read_fn = xudc_read32_be; + udc->write_fn(udc->addr, XUSB_TESTMODE_OFFSET, TEST_J); + if ((udc->read_fn(udc->addr + XUSB_TESTMODE_OFFSET)) + != TEST_J) { + udc->write_fn = xudc_write32; + udc->read_fn = xudc_read32; + } + udc->write_fn(udc->addr, XUSB_TESTMODE_OFFSET, 0); + + xudc_eps_init(udc); + + ep0 = &udc->ep[0]; + + /* Set device address to 0.*/ + udc->write_fn(udc->addr, XUSB_ADDRESS_OFFSET, 0); + + ret = usb_add_gadget_udc(&pdev->dev, &udc->gadget); + if (ret) + goto fail; + + udc->dev = &udc->gadget.dev; + + /* Enable the interrupts.*/ + ier = XUSB_STATUS_GLOBAL_INTR_MASK | XUSB_STATUS_INTR_EVENT_MASK | + XUSB_STATUS_FIFO_BUFF_RDY_MASK | XUSB_STATUS_FIFO_BUFF_FREE_MASK | + XUSB_STATUS_SETUP_PACKET_MASK | + XUSB_STATUS_INTR_BUFF_COMP_ALL_MASK; + + udc->write_fn(udc->addr, XUSB_IER_OFFSET, ier); + + platform_set_drvdata(pdev, udc); + + dev_vdbg(&pdev->dev, "%s at 0x%08X mapped to 0x%08X %s\n", + driver_name, (u32)res->start, (u32 __force)udc->addr, + udc->dma_enabled ? "with DMA" : "without DMA"); + + return 0; +fail: + dev_err(&pdev->dev, "probe failed, %d\n", ret); + return ret; +} + +/** + * xudc_remove - Releases the resources allocated during the initialization. + * @pdev: pointer to the platform device structure. + * + * Return: 0 always + */ +static int xudc_remove(struct platform_device *pdev) +{ + struct xusb_udc *udc = platform_get_drvdata(pdev); + + usb_del_gadget_udc(&udc->gadget); + + return 0; +} + +/* Match table for of_platform binding */ +static const struct of_device_id usb_of_match[] = { + { .compatible = "xlnx,usb2-device-4.00.a", }, + { /* end of list */ }, +}; +MODULE_DEVICE_TABLE(of, usb_of_match); + +static struct platform_driver xudc_driver = { + .driver = { + .name = driver_name, + .of_match_table = usb_of_match, + }, + .probe = xudc_probe, + .remove = xudc_remove, +}; + +module_platform_driver(xudc_driver); + +MODULE_DESCRIPTION("Xilinx udc driver"); +MODULE_AUTHOR("Xilinx, Inc"); +MODULE_LICENSE("GPL"); -- cgit v1.1 From 974a70bdecea5296db1b643e4046ef208e99c592 Mon Sep 17 00:00:00 2001 From: Peter Chen Date: Fri, 12 Sep 2014 09:32:41 +0800 Subject: usb: gadget: udc-core: add utility for bus reset The udc driver can notify the udc core that bus reset occurs by calling this utility, the core will notify gadget driver this information and update gadget state accordingly. Signed-off-by: Peter Chen Signed-off-by: Felipe Balbi --- drivers/usb/gadget/udc/udc-core.c | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) (limited to 'drivers/usb') diff --git a/drivers/usb/gadget/udc/udc-core.c b/drivers/usb/gadget/udc/udc-core.c index b0d9817..ba661c6 100644 --- a/drivers/usb/gadget/udc/udc-core.c +++ b/drivers/usb/gadget/udc/udc-core.c @@ -124,6 +124,23 @@ EXPORT_SYMBOL_GPL(usb_gadget_set_state); /* ------------------------------------------------------------------------- */ /** + * usb_gadget_udc_reset - notifies the udc core that bus reset occurs + * @gadget: The gadget which bus reset occurs + * @driver: The gadget driver we want to notify + * + * If the udc driver has bus reset handler, it needs to call this when the bus + * reset occurs, it notifies the gadget driver that the bus reset occurs as + * well as updates gadget state. + */ +void usb_gadget_udc_reset(struct usb_gadget *gadget, + struct usb_gadget_driver *driver) +{ + driver->reset(gadget); + usb_gadget_set_state(gadget, USB_STATE_DEFAULT); +} +EXPORT_SYMBOL_GPL(usb_gadget_udc_reset); + +/** * usb_gadget_udc_start - tells usb device controller to start up * @gadget: The gadget we want to get started * @driver: The driver we want to bind to @gadget -- cgit v1.1 From d9152161b4bfd131a8253a5b9fcd8ba9b10277c4 Mon Sep 17 00:00:00 2001 From: "Ivan T. Ivanov" Date: Fri, 12 Sep 2014 14:28:07 -0500 Subject: usb: dwc3: Add Qualcomm DWC3 glue layer driver DWC3 glue layer is hardware layer around Synopsys DesignWare USB3 core. Its purpose is to supply Synopsys IP with required clocks, voltages and interface it with the rest of the SoC. Signed-off-by: Ivan T. Ivanov Signed-off-by: Andy Gross Signed-off-by: Felipe Balbi --- drivers/usb/dwc3/Kconfig | 8 +++ drivers/usb/dwc3/Makefile | 1 + drivers/usb/dwc3/dwc3-qcom.c | 131 +++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 140 insertions(+) create mode 100644 drivers/usb/dwc3/dwc3-qcom.c (limited to 'drivers/usb') diff --git a/drivers/usb/dwc3/Kconfig b/drivers/usb/dwc3/Kconfig index 5238251..f4e5cc6 100644 --- a/drivers/usb/dwc3/Kconfig +++ b/drivers/usb/dwc3/Kconfig @@ -89,6 +89,14 @@ config USB_DWC3_ST inside (i.e. STiH407). Say 'Y' or 'M' if you have one such device. +config USB_DWC3_QCOM + tristate "Qualcomm Platforms" + depends on ARCH_QCOM || COMPILE_TEST + default USB_DWC3 + help + Recent Qualcomm SoCs ship with one DesignWare Core USB3 IP inside, + say 'Y' or 'M' if you have one such device. + comment "Debugging features" config USB_DWC3_DEBUG diff --git a/drivers/usb/dwc3/Makefile b/drivers/usb/dwc3/Makefile index 3e88c77..bb34fbc 100644 --- a/drivers/usb/dwc3/Makefile +++ b/drivers/usb/dwc3/Makefile @@ -36,4 +36,5 @@ obj-$(CONFIG_USB_DWC3_OMAP) += dwc3-omap.o obj-$(CONFIG_USB_DWC3_EXYNOS) += dwc3-exynos.o obj-$(CONFIG_USB_DWC3_PCI) += dwc3-pci.o obj-$(CONFIG_USB_DWC3_KEYSTONE) += dwc3-keystone.o +obj-$(CONFIG_USB_DWC3_QCOM) += dwc3-qcom.o obj-$(CONFIG_USB_DWC3_ST) += dwc3-st.o diff --git a/drivers/usb/dwc3/dwc3-qcom.c b/drivers/usb/dwc3/dwc3-qcom.c new file mode 100644 index 0000000..611f8e7 --- /dev/null +++ b/drivers/usb/dwc3/dwc3-qcom.c @@ -0,0 +1,131 @@ +/* Copyright (c) 2013-2014, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include +#include +#include +#include +#include + +struct dwc3_qcom { + struct device *dev; + + struct clk *core_clk; + struct clk *iface_clk; + struct clk *sleep_clk; +}; + +static int dwc3_qcom_probe(struct platform_device *pdev) +{ + struct device_node *node = pdev->dev.of_node; + struct dwc3_qcom *qdwc; + int ret; + + qdwc = devm_kzalloc(&pdev->dev, sizeof(*qdwc), GFP_KERNEL); + if (!qdwc) + return -ENOMEM; + + platform_set_drvdata(pdev, qdwc); + + qdwc->dev = &pdev->dev; + + qdwc->core_clk = devm_clk_get(qdwc->dev, "core"); + if (IS_ERR(qdwc->core_clk)) { + dev_err(qdwc->dev, "failed to get core clock\n"); + return PTR_ERR(qdwc->core_clk); + } + + qdwc->iface_clk = devm_clk_get(qdwc->dev, "iface"); + if (IS_ERR(qdwc->iface_clk)) { + dev_dbg(qdwc->dev, "failed to get optional iface clock\n"); + qdwc->iface_clk = NULL; + } + + qdwc->sleep_clk = devm_clk_get(qdwc->dev, "sleep"); + if (IS_ERR(qdwc->sleep_clk)) { + dev_dbg(qdwc->dev, "failed to get optional sleep clock\n"); + qdwc->sleep_clk = NULL; + } + + ret = clk_prepare_enable(qdwc->core_clk); + if (ret) { + dev_err(qdwc->dev, "failed to enable core clock\n"); + goto err_core; + } + + ret = clk_prepare_enable(qdwc->iface_clk); + if (ret) { + dev_err(qdwc->dev, "failed to enable optional iface clock\n"); + goto err_iface; + } + + ret = clk_prepare_enable(qdwc->sleep_clk); + if (ret) { + dev_err(qdwc->dev, "failed to enable optional sleep clock\n"); + goto err_sleep; + } + + ret = of_platform_populate(node, NULL, NULL, qdwc->dev); + if (ret) { + dev_err(qdwc->dev, "failed to register core - %d\n", ret); + goto err_clks; + } + + return 0; + +err_clks: + clk_disable_unprepare(qdwc->sleep_clk); +err_sleep: + clk_disable_unprepare(qdwc->iface_clk); +err_iface: + clk_disable_unprepare(qdwc->core_clk); +err_core: + return ret; +} + +static int dwc3_qcom_remove(struct platform_device *pdev) +{ + struct dwc3_qcom *qdwc = platform_get_drvdata(pdev); + + of_platform_depopulate(&pdev->dev); + + clk_disable_unprepare(qdwc->sleep_clk); + clk_disable_unprepare(qdwc->iface_clk); + clk_disable_unprepare(qdwc->core_clk); + + return 0; +} + +static const struct of_device_id of_dwc3_match[] = { + { .compatible = "qcom,dwc3" }, + { /* Sentinel */ } +}; +MODULE_DEVICE_TABLE(of, of_dwc3_match); + +static struct platform_driver dwc3_qcom_driver = { + .probe = dwc3_qcom_probe, + .remove = dwc3_qcom_remove, + .driver = { + .name = "qcom-dwc3", + .owner = THIS_MODULE, + .of_match_table = of_dwc3_match, + }, +}; + +module_platform_driver(dwc3_qcom_driver); + +MODULE_ALIAS("platform:qcom-dwc3"); +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("DesignWare USB3 QCOM Glue Layer"); +MODULE_AUTHOR("Ivan T. Ivanov "); -- cgit v1.1 From 1b0bf88fd8b845aef4300c7c0feca774265dd1c4 Mon Sep 17 00:00:00 2001 From: Robert Baldyga Date: Tue, 9 Sep 2014 08:23:17 +0200 Subject: usb: gadget: f_fs: virtual endpoint address mapping This patch introduces virtual endpoint address mapping. It separates function logic form physical endpoint addresses making it more hardware independent. Following modifications changes user space API, so to enable them user have to switch on the FUNCTIONFS_VIRTUAL_ADDR flag in descriptors. Endpoints are now refered using virtual endpoint addresses chosen by user in endpoint descpriptors. This applies to each context when endpoint address can be used: - when accessing endpoint files in FunctionFS filesystemi (in file name), - in setup requests directed to specific endpoint (in wIndex field), - in descriptors returned by FUNCTIONFS_ENDPOINT_DESC ioctl. In endpoint file names the endpoint address number is formatted as double-digit hexadecimal value ("ep%02x") which has few advantages - it is easy to parse, allows to easly recognize endpoint direction basing on its name (IN endpoint number starts with digit 8, and OUT with 0) which can be useful for debugging purpose, and it makes easier to introduce further features allowing to use each endpoint number in both directions to have more endpoints available for function if hardware supports this (for example we could have ep01 which is endpoint 1 with OUT direction, and ep81 which is endpoint 1 with IN direction). Physical endpoint address can be still obtained using ioctl named FUNCTIONFS_ENDPOINT_REVMAP, but now it's not neccesary to handle USB transactions properly. Signed-off-by: Robert Baldyga Acked-by: Michal Nazarewicz Signed-off-by: Felipe Balbi --- drivers/usb/gadget/function/f_fs.c | 23 +++++++++++++++++++++-- drivers/usb/gadget/function/u_fs.h | 2 ++ 2 files changed, 23 insertions(+), 2 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/gadget/function/f_fs.c b/drivers/usb/gadget/function/f_fs.c index e4afe8d..4ad11e0 100644 --- a/drivers/usb/gadget/function/f_fs.c +++ b/drivers/usb/gadget/function/f_fs.c @@ -1557,7 +1557,10 @@ static int ffs_epfiles_create(struct ffs_data *ffs) epfile->ffs = ffs; mutex_init(&epfile->mutex); init_waitqueue_head(&epfile->wait); - sprintf(epfiles->name, "ep%u", i); + if (ffs->user_flags & FUNCTIONFS_VIRTUAL_ADDR) + sprintf(epfiles->name, "ep%02x", ffs->eps_addrmap[i]); + else + sprintf(epfiles->name, "ep%u", i); if (!unlikely(ffs_sb_create_file(ffs->sb, epfiles->name, epfile, &ffs_epfile_operations, &epfile->dentry))) { @@ -2106,10 +2109,12 @@ static int __ffs_data_got_descs(struct ffs_data *ffs, break; case FUNCTIONFS_DESCRIPTORS_MAGIC_V2: flags = get_unaligned_le32(data + 8); + ffs->user_flags = flags; if (flags & ~(FUNCTIONFS_HAS_FS_DESC | FUNCTIONFS_HAS_HS_DESC | FUNCTIONFS_HAS_SS_DESC | - FUNCTIONFS_HAS_MS_OS_DESC)) { + FUNCTIONFS_HAS_MS_OS_DESC | + FUNCTIONFS_VIRTUAL_ADDR)) { ret = -ENOSYS; goto error; } @@ -2466,7 +2471,13 @@ static int __ffs_func_bind_do_descs(enum ffs_entity_type type, u8 *valuep, } else { struct usb_request *req; struct usb_ep *ep; + u8 bEndpointAddress; + /* + * We back up bEndpointAddress because autoconfig overwrites + * it with physical endpoint address. + */ + bEndpointAddress = ds->bEndpointAddress; pr_vdebug("autoconfig\n"); ep = usb_ep_autoconfig(func->gadget, ds); if (unlikely(!ep)) @@ -2481,6 +2492,12 @@ static int __ffs_func_bind_do_descs(enum ffs_entity_type type, u8 *valuep, ffs_ep->req = req; func->eps_revmap[ds->bEndpointAddress & USB_ENDPOINT_NUMBER_MASK] = idx + 1; + /* + * If we use virtual address mapping, we restore + * original bEndpointAddress value. + */ + if (func->ffs->user_flags & FUNCTIONFS_VIRTUAL_ADDR) + ds->bEndpointAddress = bEndpointAddress; } ffs_dump_mem(": Rewritten ep desc", ds, ds->bLength); @@ -2925,6 +2942,8 @@ static int ffs_func_setup(struct usb_function *f, ret = ffs_func_revmap_ep(func, le16_to_cpu(creq->wIndex)); if (unlikely(ret < 0)) return ret; + if (func->ffs->user_flags & FUNCTIONFS_VIRTUAL_ADDR) + ret = func->ffs->eps_addrmap[ret]; break; default: diff --git a/drivers/usb/gadget/function/u_fs.h b/drivers/usb/gadget/function/u_fs.h index d48897e..cd128e3 100644 --- a/drivers/usb/gadget/function/u_fs.h +++ b/drivers/usb/gadget/function/u_fs.h @@ -224,6 +224,8 @@ struct ffs_data { void *ms_os_descs_ext_prop_name_avail; void *ms_os_descs_ext_prop_data_avail; + unsigned user_flags; + u8 eps_addrmap[15]; unsigned short strings_count; -- cgit v1.1 From 4a6698b80cfe36dd4e3c6bc30ab81b4e0a837f64 Mon Sep 17 00:00:00 2001 From: Fengguang Wu Date: Tue, 16 Sep 2014 17:26:46 +0300 Subject: usb: gadget: uvc: uvc_alloc() can be static The function isn't called from outside of its compilation unit, make it static. Signed-off-by: Fengguang Wu Acked-by: Andrzej Pietrasiewicz Signed-off-by: Laurent Pinchart Signed-off-by: Felipe Balbi --- drivers/usb/gadget/function/f_uvc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/usb') diff --git a/drivers/usb/gadget/function/f_uvc.c b/drivers/usb/gadget/function/f_uvc.c index 95dc1c6..bf9abf4 100644 --- a/drivers/usb/gadget/function/f_uvc.c +++ b/drivers/usb/gadget/function/f_uvc.c @@ -773,7 +773,7 @@ static void uvc_unbind(struct usb_configuration *c, struct usb_function *f) usb_free_all_descriptors(f); } -struct usb_function *uvc_alloc(struct usb_function_instance *fi) +static struct usb_function *uvc_alloc(struct usb_function_instance *fi) { struct uvc_device *uvc; struct f_uvc_opts *opts; -- cgit v1.1 From e102609f107269fbc04af21548e78e99c02b6204 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Tue, 16 Sep 2014 17:26:47 +0300 Subject: usb: gadget: uvc: Fix endianness mismatches The struct usb_endpoint_descriptor wMaxPacketSize field the struct usb_ss_ep_comp_descriptor wBytesPerInterval field are stored in little-endian format. Convert the values from CPU order to little endian before storing the values. Signed-off-by: Laurent Pinchart Signed-off-by: Felipe Balbi --- drivers/usb/gadget/function/f_uvc.c | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/gadget/function/f_uvc.c b/drivers/usb/gadget/function/f_uvc.c index bf9abf4..e126439 100644 --- a/drivers/usb/gadget/function/f_uvc.c +++ b/drivers/usb/gadget/function/f_uvc.c @@ -596,19 +596,20 @@ uvc_function_bind(struct usb_configuration *c, struct usb_function *f) } uvc_fs_streaming_ep.wMaxPacketSize = - min(opts->streaming_maxpacket, 1023U); + cpu_to_le16(min(opts->streaming_maxpacket, 1023U)); uvc_fs_streaming_ep.bInterval = opts->streaming_interval; - uvc_hs_streaming_ep.wMaxPacketSize = max_packet_size; - uvc_hs_streaming_ep.wMaxPacketSize |= ((max_packet_mult - 1) << 11); + uvc_hs_streaming_ep.wMaxPacketSize = + cpu_to_le16(max_packet_size | ((max_packet_mult - 1) << 11)); uvc_hs_streaming_ep.bInterval = opts->streaming_interval; - uvc_ss_streaming_ep.wMaxPacketSize = max_packet_size; + uvc_ss_streaming_ep.wMaxPacketSize = cpu_to_le16(max_packet_size); uvc_ss_streaming_ep.bInterval = opts->streaming_interval; uvc_ss_streaming_comp.bmAttributes = max_packet_mult - 1; uvc_ss_streaming_comp.bMaxBurst = opts->streaming_maxburst; uvc_ss_streaming_comp.wBytesPerInterval = - max_packet_size * max_packet_mult * opts->streaming_maxburst; + cpu_to_le16(max_packet_size * max_packet_mult * + opts->streaming_maxburst); /* Allocate endpoints. */ ep = usb_ep_autoconfig(cdev->gadget, &uvc_control_ep); -- cgit v1.1 From 6dd5b021bd6c735a6a1515d06dab1478fc461dfd Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Tue, 16 Sep 2014 17:26:48 +0300 Subject: usb: gadget: uvc: Simplify uvcg_video_pump by using local variable Use the local queue variable instead of computing it every time. Signed-off-by: Laurent Pinchart Signed-off-by: Felipe Balbi --- drivers/usb/gadget/function/uvc_video.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/gadget/function/uvc_video.c b/drivers/usb/gadget/function/uvc_video.c index b0528e8..c3e1f27 100644 --- a/drivers/usb/gadget/function/uvc_video.c +++ b/drivers/usb/gadget/function/uvc_video.c @@ -309,10 +309,10 @@ int uvcg_video_pump(struct uvc_video *video) /* Retrieve the first available video buffer and fill the * request, protected by the video queue irqlock. */ - spin_lock_irqsave(&video->queue.irqlock, flags); - buf = uvcg_queue_head(&video->queue); + spin_lock_irqsave(&queue->irqlock, flags); + buf = uvcg_queue_head(queue); if (buf == NULL) { - spin_unlock_irqrestore(&video->queue.irqlock, flags); + spin_unlock_irqrestore(&queue->irqlock, flags); break; } @@ -323,11 +323,11 @@ int uvcg_video_pump(struct uvc_video *video) if (ret < 0) { printk(KERN_INFO "Failed to queue request (%d)\n", ret); usb_ep_set_halt(video->ep); - spin_unlock_irqrestore(&video->queue.irqlock, flags); + spin_unlock_irqrestore(&queue->irqlock, flags); uvcg_queue_cancel(queue, 0); break; } - spin_unlock_irqrestore(&video->queue.irqlock, flags); + spin_unlock_irqrestore(&queue->irqlock, flags); } spin_lock_irqsave(&video->req_lock, flags); -- cgit v1.1 From 468bcc2a2ca071f652009d2d20d97f2437630cae Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Mon, 15 Sep 2014 09:03:24 -0500 Subject: usb: musb: dsps: kill OTG timer on suspend if we don't make sure to kill the timer, it could expire after we have already gated our clocks. That will trigger a Data Abort exception because we would try to access register while clock is gated. Fix that bug. Cc: # v3.14+ Fixes 869c597 (usb: musb: dsps: add support for suspend and resume) Tested-by: Dave Gerlach Signed-off-by: Felipe Balbi --- drivers/usb/musb/musb_dsps.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'drivers/usb') diff --git a/drivers/usb/musb/musb_dsps.c b/drivers/usb/musb/musb_dsps.c index c791ba5..154bcf1 100644 --- a/drivers/usb/musb/musb_dsps.c +++ b/drivers/usb/musb/musb_dsps.c @@ -870,6 +870,7 @@ static int dsps_suspend(struct device *dev) struct musb *musb = platform_get_drvdata(glue->musb); void __iomem *mbase = musb->ctrl_base; + del_timer_sync(&glue->timer); glue->context.control = dsps_readl(mbase, wrp->control); glue->context.epintr = dsps_readl(mbase, wrp->epintr_set); glue->context.coreintr = dsps_readl(mbase, wrp->coreintr_set); @@ -895,6 +896,7 @@ static int dsps_resume(struct device *dev) dsps_writel(mbase, wrp->mode, glue->context.mode); dsps_writel(mbase, wrp->tx_mode, glue->context.tx_mode); dsps_writel(mbase, wrp->rx_mode, glue->context.rx_mode); + setup_timer(&glue->timer, otg_timer, (unsigned long) musb); return 0; } -- cgit v1.1 From af54954ad02091506ced45588215d389d606f74e Mon Sep 17 00:00:00 2001 From: Andreas Larsson Date: Mon, 15 Sep 2014 12:42:27 +0200 Subject: usb: gadget: udc_core: Use right kobj when calling sysfs_notify The state attribute is connected to the kobj of the udc, not the gadget. Signed-off-by: Andreas Larsson Signed-off-by: Felipe Balbi --- drivers/usb/gadget/udc/udc-core.c | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) (limited to 'drivers/usb') diff --git a/drivers/usb/gadget/udc/udc-core.c b/drivers/usb/gadget/udc/udc-core.c index ba661c6..ad1ceac 100644 --- a/drivers/usb/gadget/udc/udc-core.c +++ b/drivers/usb/gadget/udc/udc-core.c @@ -109,8 +109,20 @@ EXPORT_SYMBOL_GPL(usb_gadget_unmap_request); static void usb_gadget_state_work(struct work_struct *work) { struct usb_gadget *gadget = work_to_gadget(work); + struct usb_udc *udc = NULL; + + mutex_lock(&udc_lock); + list_for_each_entry(udc, &udc_list, list) + if (udc->gadget == gadget) + goto found; + mutex_unlock(&udc_lock); + + return; + +found: + mutex_unlock(&udc_lock); - sysfs_notify(&gadget->dev.kobj, NULL, "state"); + sysfs_notify(&udc->dev.kobj, NULL, "state"); } void usb_gadget_set_state(struct usb_gadget *gadget, -- cgit v1.1 From 5b484989a998074762281a1ae71b2d88f26f66d6 Mon Sep 17 00:00:00 2001 From: Andreas Larsson Date: Mon, 15 Sep 2014 12:32:54 +0200 Subject: usb: gadget: gr_udc: Add bounce buffer to handle odd sized OUT requests This adds a bounce buffer that handles the end of OUT requests where req.length is not divisible by ep->ep.maxpacket. Before this, such requests were rejected as the DMA engine cannot restrict itself to buffers that are smaller than ep->ep.maxpacket. Signed-off-by: Andreas Larsson Signed-off-by: Felipe Balbi --- drivers/usb/gadget/udc/gr_udc.c | 81 ++++++++++++++++++++++++++++++----------- drivers/usb/gadget/udc/gr_udc.h | 7 ++++ 2 files changed, 67 insertions(+), 21 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/gadget/udc/gr_udc.c b/drivers/usb/gadget/udc/gr_udc.c index 08df5c4..ecd10b5 100644 --- a/drivers/usb/gadget/udc/gr_udc.c +++ b/drivers/usb/gadget/udc/gr_udc.c @@ -318,8 +318,26 @@ static void gr_finish_request(struct gr_ep *ep, struct gr_request *req, usb_gadget_unmap_request(&dev->gadget, &req->req, ep->is_in); gr_free_dma_desc_chain(dev, req); - if (ep->is_in) /* For OUT, actual gets updated bit by bit */ + if (ep->is_in) { /* For OUT, req->req.actual gets updated bit by bit */ req->req.actual = req->req.length; + } else if (req->oddlen && req->req.actual > req->evenlen) { + /* + * Copy to user buffer in this case where length was not evenly + * divisible by ep->ep.maxpacket and the last descriptor was + * actually used. + */ + char *buftail = ((char *)req->req.buf + req->evenlen); + + memcpy(buftail, ep->tailbuf, req->oddlen); + + if (req->req.actual > req->req.length) { + /* We got more data than was requested */ + dev_dbg(ep->dev->dev, "Overflow for ep %s\n", + ep->ep.name); + gr_dbgprint_request("OVFL", ep, req); + req->req.status = -EOVERFLOW; + } + } if (!status) { if (ep->is_in) @@ -379,6 +397,15 @@ static void gr_start_dma(struct gr_ep *ep) /* A descriptor should already have been allocated */ BUG_ON(!req->curr_desc); + /* + * The DMA controller can not handle smaller OUT buffers than + * ep->ep.maxpacket. It could lead to buffer overruns if an unexpectedly + * long packet are received. Therefore an internal bounce buffer gets + * used when such a request gets enabled. + */ + if (!ep->is_in && req->oddlen) + req->last_desc->data = ep->tailbuf_paddr; + wmb(); /* Make sure all is settled before handing it over to DMA */ /* Set the descriptor pointer in the hardware */ @@ -480,11 +507,11 @@ static int gr_setup_out_desc_list(struct gr_ep *ep, struct gr_request *req, dma_addr_t start = req->req.dma + bytes_used; u16 size = min(bytes_left, ep->bytes_per_buffer); - /* Should not happen however - gr_queue stops such lengths */ - if (size < ep->bytes_per_buffer) - dev_warn(ep->dev->dev, - "Buffer overrun risk: %u < %u bytes/buffer\n", - size, ep->bytes_per_buffer); + if (size < ep->bytes_per_buffer) { + /* Prepare using bounce buffer */ + req->evenlen = req->req.length - bytes_left; + req->oddlen = size; + } ret = gr_add_dma_desc(ep, req, start, size, gfp_flags); if (ret) @@ -584,18 +611,6 @@ static int gr_queue(struct gr_ep *ep, struct gr_request *req, gfp_t gfp_flags) return -EINVAL; } - /* - * The DMA controller can not handle smaller OUT buffers than - * maxpacket. It could lead to buffer overruns if unexpectedly long - * packet are received. - */ - if (!ep->is_in && (req->req.length % ep->ep.maxpacket) != 0) { - dev_err(dev->dev, - "OUT request length %d is not multiple of maxpacket\n", - req->req.length); - return -EMSGSIZE; - } - if (unlikely(!dev->driver || dev->gadget.speed == USB_SPEED_UNKNOWN)) { dev_err(dev->dev, "-ESHUTDOWN"); return -ESHUTDOWN; @@ -1286,8 +1301,8 @@ static int gr_handle_out_ep(struct gr_ep *ep) if (ctrl & GR_DESC_OUT_CTRL_SE) req->setup = 1; - if (len < ep->ep.maxpacket || req->req.actual == req->req.length) { - /* Short packet or the expected size - we are done */ + if (len < ep->ep.maxpacket || req->req.actual >= req->req.length) { + /* Short packet or >= expected size - we are done */ if ((ep == &dev->epo[0]) && (dev->ep0state == GR_EP0_OSTATUS)) { /* @@ -2015,6 +2030,11 @@ static int gr_ep_init(struct gr_udc *dev, int num, int is_in, u32 maxplimit) } list_add_tail(&ep->ep_list, &dev->ep_list); + ep->tailbuf = dma_alloc_coherent(dev->dev, ep->ep.maxpacket_limit, + &ep->tailbuf_paddr, GFP_ATOMIC); + if (!ep->tailbuf) + return -ENOMEM; + return 0; } @@ -2067,9 +2087,24 @@ static int gr_udc_init(struct gr_udc *dev) return 0; } +static void gr_ep_remove(struct gr_udc *dev, int num, int is_in) +{ + struct gr_ep *ep; + + if (is_in) + ep = &dev->epi[num]; + else + ep = &dev->epo[num]; + + if (ep->tailbuf) + dma_free_coherent(dev->dev, ep->ep.maxpacket_limit, + ep->tailbuf, ep->tailbuf_paddr); +} + static int gr_remove(struct platform_device *pdev) { struct gr_udc *dev = platform_get_drvdata(pdev); + int i; if (dev->added) usb_del_gadget_udc(&dev->gadget); /* Shuts everything down */ @@ -2084,6 +2119,11 @@ static int gr_remove(struct platform_device *pdev) gr_free_request(&dev->epi[0].ep, &dev->ep0reqi->req); gr_free_request(&dev->epo[0].ep, &dev->ep0reqo->req); + for (i = 0; i < dev->nepo; i++) + gr_ep_remove(dev, i, 0); + for (i = 0; i < dev->nepi; i++) + gr_ep_remove(dev, i, 1); + return 0; } static int gr_request_irq(struct gr_udc *dev, int irq) @@ -2131,7 +2171,6 @@ static int gr_probe(struct platform_device *pdev) dev->gadget.name = driver_name; dev->gadget.max_speed = USB_SPEED_HIGH; dev->gadget.ops = &gr_ops; - dev->gadget.quirk_ep_out_aligned_size = true; spin_lock_init(&dev->lock); dev->regs = regs; diff --git a/drivers/usb/gadget/udc/gr_udc.h b/drivers/usb/gadget/udc/gr_udc.h index 8388897..4297c4e 100644 --- a/drivers/usb/gadget/udc/gr_udc.h +++ b/drivers/usb/gadget/udc/gr_udc.h @@ -156,6 +156,10 @@ struct gr_ep { struct list_head queue; struct list_head ep_list; + + /* Bounce buffer for end of "odd" sized OUT requests */ + void *tailbuf; + dma_addr_t tailbuf_paddr; }; struct gr_request { @@ -167,6 +171,9 @@ struct gr_request { struct gr_dma_desc *curr_desc; /* Current descriptor */ struct gr_dma_desc *last_desc; /* Last in the chain */ + u16 evenlen; /* Size of even length head (if oddlen != 0) */ + u16 oddlen; /* Size of odd length tail if buffer length is "odd" */ + u8 setup; /* Setup packet */ }; -- cgit v1.1 From 72a65a0d19c16de36e970ca6981732b5e8f7f4c4 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Thu, 18 Sep 2014 09:41:39 -0500 Subject: Revert "usb: gadget: composite: dequeue cdev->req before free its buffer" This reverts commit be0a8887bb931af0e21531da20c41533effbb0d6. The original commit f2267089ea17fa97b796b1b4247e3f8957655df3 (usb: gadget: composite: dequeue cdev->req before free it in composite_dev_cleanup) ended up being reverted because it caused more issues then fixed. We will also revert this counter part commit so we start clean to properly add that idea back. Cc: Li Jun Signed-of-by: Felipe Balbi --- drivers/usb/gadget/composite.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/usb') diff --git a/drivers/usb/gadget/composite.c b/drivers/usb/gadget/composite.c index 3f3d6f2..e07eddb 100644 --- a/drivers/usb/gadget/composite.c +++ b/drivers/usb/gadget/composite.c @@ -1955,8 +1955,8 @@ void composite_dev_cleanup(struct usb_composite_dev *cdev) usb_ep_free_request(cdev->gadget->ep0, cdev->os_desc_req); } if (cdev->req) { - usb_ep_dequeue(cdev->gadget->ep0, cdev->req); kfree(cdev->req->buf); + usb_ep_dequeue(cdev->gadget->ep0, cdev->req); usb_ep_free_request(cdev->gadget->ep0, cdev->req); } cdev->next_string_id = 0; -- cgit v1.1