From d5638fcf15bc65584877f00d4b0094cc4a66ad4e Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Mon, 2 Feb 2015 17:14:12 -0600 Subject: usb: musb: gadget: get rid of stop_activity() that function is pretty close to a no-op by now, all we need is a call to musb_stop(). Signed-off-by: Felipe Balbi --- drivers/usb/musb/musb_gadget.c | 40 +--------------------------------------- 1 file changed, 1 insertion(+), 39 deletions(-) diff --git a/drivers/usb/musb/musb_gadget.c b/drivers/usb/musb/musb_gadget.c index b2d9040c7685..4c481cd66c77 100644 --- a/drivers/usb/musb/musb_gadget.c +++ b/drivers/usb/musb/musb_gadget.c @@ -1876,44 +1876,6 @@ err: return retval; } -static void stop_activity(struct musb *musb, struct usb_gadget_driver *driver) -{ - int i; - struct musb_hw_ep *hw_ep; - - /* don't disconnect if it's not connected */ - if (musb->g.speed == USB_SPEED_UNKNOWN) - driver = NULL; - else - musb->g.speed = USB_SPEED_UNKNOWN; - - /* deactivate the hardware */ - if (musb->softconnect) { - musb->softconnect = 0; - musb_pullup(musb, 0); - } - musb_stop(musb); - - /* killing any outstanding requests will quiesce the driver; - * then report disconnect - */ - if (driver) { - for (i = 0, hw_ep = musb->endpoints; - i < musb->nr_endpoints; - i++, hw_ep++) { - musb_ep_select(musb->mregs, i); - if (hw_ep->is_shared_fifo /* || !epnum */) { - nuke(&hw_ep->ep_in, -ESHUTDOWN); - } else { - if (hw_ep->max_packet_sz_tx) - nuke(&hw_ep->ep_in, -ESHUTDOWN); - if (hw_ep->max_packet_sz_rx) - nuke(&hw_ep->ep_out, -ESHUTDOWN); - } - } - } -} - /* * Unregister the gadget driver. Used by gadget drivers when * unregistering themselves from the controller. @@ -1940,7 +1902,7 @@ static int musb_gadget_stop(struct usb_gadget *g) (void) musb_gadget_vbus_draw(&musb->g, 0); musb->xceiv->otg->state = OTG_STATE_UNDEFINED; - stop_activity(musb, NULL); + musb_stop(musb); otg_set_peripheral(musb->xceiv->otg, NULL); musb->is_active = 0; -- cgit v1.2.3 From e3c93e1a3f35be4cf1493d3ccfb0c6d9209e4922 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Mon, 30 Dec 2013 12:33:53 -0600 Subject: usb: musb: core: fix TX/RX endpoint order As per Mentor Graphics' documentation, we should always handle TX endpoints before RX endpoints. This patch fixes that error while also updating some hard-to-read comments which were scattered around musb_interrupt(). This patch should be backported as far back as possible since this error has been in the driver since it's conception. Cc: Signed-off-by: Felipe Balbi --- drivers/usb/musb/musb_core.c | 44 ++++++++++++++++++++++++++------------------ 1 file changed, 26 insertions(+), 18 deletions(-) diff --git a/drivers/usb/musb/musb_core.c b/drivers/usb/musb/musb_core.c index 067920f2d570..461bfe8efcf2 100644 --- a/drivers/usb/musb/musb_core.c +++ b/drivers/usb/musb/musb_core.c @@ -1597,16 +1597,30 @@ irqreturn_t musb_interrupt(struct musb *musb) is_host_active(musb) ? "host" : "peripheral", musb->int_usb, musb->int_tx, musb->int_rx); - /* the core can interrupt us for multiple reasons; docs have - * a generic interrupt flowchart to follow + /** + * According to Mentor Graphics' documentation, flowchart on page 98, + * IRQ should be handled as follows: + * + * . Resume IRQ + * . Session Request IRQ + * . VBUS Error IRQ + * . Suspend IRQ + * . Connect IRQ + * . Disconnect IRQ + * . Reset/Babble IRQ + * . SOF IRQ (we're not using this one) + * . Endpoint 0 IRQ + * . TX Endpoints + * . RX Endpoints + * + * We will be following that flowchart in order to avoid any problems + * that might arise with internal Finite State Machine. */ + if (musb->int_usb) retval |= musb_stage0_irq(musb, musb->int_usb, devctl); - /* "stage 1" is handling endpoint irqs */ - - /* handle endpoint 0 first */ if (musb->int_tx & 1) { if (is_host_active(musb)) retval |= musb_h_ep0_irq(musb); @@ -1614,37 +1628,31 @@ irqreturn_t musb_interrupt(struct musb *musb) retval |= musb_g_ep0_irq(musb); } - /* RX on endpoints 1-15 */ - reg = musb->int_rx >> 1; + reg = musb->int_tx >> 1; ep_num = 1; while (reg) { if (reg & 1) { - /* musb_ep_select(musb->mregs, ep_num); */ - /* REVISIT just retval = ep->rx_irq(...) */ retval = IRQ_HANDLED; if (is_host_active(musb)) - musb_host_rx(musb, ep_num); + musb_host_tx(musb, ep_num); else - musb_g_rx(musb, ep_num); + musb_g_tx(musb, ep_num); } - reg >>= 1; ep_num++; } - /* TX on endpoints 1-15 */ - reg = musb->int_tx >> 1; + reg = musb->int_rx >> 1; ep_num = 1; while (reg) { if (reg & 1) { - /* musb_ep_select(musb->mregs, ep_num); */ - /* REVISIT just retval |= ep->tx_irq(...) */ retval = IRQ_HANDLED; if (is_host_active(musb)) - musb_host_tx(musb, ep_num); + musb_host_rx(musb, ep_num); else - musb_g_tx(musb, ep_num); + musb_g_rx(musb, ep_num); } + reg >>= 1; ep_num++; } -- cgit v1.2.3 From 31a0ede0de49a5897d7d97c68228ae79f86c38f0 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Mon, 30 Dec 2013 12:42:38 -0600 Subject: usb: musb: core: improve musb_interrupt() a bit instead of using manually spelled out bit-shits and iterate over each of the 16-bits (one for each endpoint) on each direction, we can make use of for_each_set_bit() which internally uses find_first_bit(). This makes the code slightly more readable while also making we only iterate over bits which are actually set. Signed-off-by: Felipe Balbi --- drivers/usb/musb/musb_core.c | 52 ++++++++++++++++++++------------------------ 1 file changed, 24 insertions(+), 28 deletions(-) diff --git a/drivers/usb/musb/musb_core.c b/drivers/usb/musb/musb_core.c index 461bfe8efcf2..e59ae7395ba8 100644 --- a/drivers/usb/musb/musb_core.c +++ b/drivers/usb/musb/musb_core.c @@ -1587,9 +1587,12 @@ static int musb_core_init(u16 musb_type, struct musb *musb) irqreturn_t musb_interrupt(struct musb *musb) { irqreturn_t retval = IRQ_NONE; + unsigned long status; + unsigned long epnum; u8 devctl; - int ep_num; - u32 reg; + + if (!musb->int_usb && !musb->int_tx && !musb->int_rx) + return IRQ_NONE; devctl = musb_readb(musb->mregs, MUSB_DEVCTL); @@ -1618,43 +1621,36 @@ irqreturn_t musb_interrupt(struct musb *musb) */ if (musb->int_usb) - retval |= musb_stage0_irq(musb, musb->int_usb, - devctl); + retval |= musb_stage0_irq(musb, musb->int_usb, devctl); if (musb->int_tx & 1) { if (is_host_active(musb)) retval |= musb_h_ep0_irq(musb); else retval |= musb_g_ep0_irq(musb); + + /* we have just handled endpoint 0 IRQ, clear it */ + musb->int_tx &= ~BIT(0); } - reg = musb->int_tx >> 1; - ep_num = 1; - while (reg) { - if (reg & 1) { - retval = IRQ_HANDLED; - if (is_host_active(musb)) - musb_host_tx(musb, ep_num); - else - musb_g_tx(musb, ep_num); - } - reg >>= 1; - ep_num++; + status = musb->int_tx; + + for_each_set_bit(epnum, &status, 16) { + retval = IRQ_HANDLED; + if (is_host_active(musb)) + musb_host_tx(musb, epnum); + else + musb_g_tx(musb, epnum); } - reg = musb->int_rx >> 1; - ep_num = 1; - while (reg) { - if (reg & 1) { - retval = IRQ_HANDLED; - if (is_host_active(musb)) - musb_host_rx(musb, ep_num); - else - musb_g_rx(musb, ep_num); - } + status = musb->int_rx; - reg >>= 1; - ep_num++; + for_each_set_bit(epnum, &status, 16) { + retval = IRQ_HANDLED; + if (is_host_active(musb)) + musb_host_rx(musb, epnum); + else + musb_g_rx(musb, epnum); } return retval; -- cgit v1.2.3 From 3da1f6ee3563f84395e5d334349a4c9e25d8cbcb Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Tue, 2 Sep 2014 15:19:43 -0500 Subject: usb: dwc3: core: only reset res->start in case of error That trick is only needed if we end up with an error, so there's no point in messing that outside of an error path. In fact doing so causes problems when removing dwc3.ko, problems which commit c5a1fbc (usb: dwc3: dwc3-omap: Fix the crash on module removal) mistakenly tried to fix. Signed-off-by: Felipe Balbi --- drivers/usb/dwc3/core.c | 56 ++++++++++++++++++++++++++++++------------------- 1 file changed, 34 insertions(+), 22 deletions(-) diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c index 9f0e209b8f6c..cd59e919e27e 100644 --- a/drivers/usb/dwc3/core.c +++ b/drivers/usb/dwc3/core.c @@ -774,17 +774,13 @@ static int dwc3_probe(struct platform_device *pdev) * since it will be requested by the xhci-plat driver. */ regs = devm_ioremap_resource(dev, res); - if (IS_ERR(regs)) - return PTR_ERR(regs); + if (IS_ERR(regs)) { + ret = PTR_ERR(regs); + goto err0; + } dwc->regs = regs; dwc->regs_size = resource_size(res); - /* - * restore res->start back to its original value so that, - * in case the probe is deferred, we don't end up getting error in - * request the memory region the next time probe is called. - */ - res->start -= DWC3_GLOBALS_REGS_START; /* default to highest possible threshold */ lpm_nyet_threshold = 0xff; @@ -878,7 +874,7 @@ static int dwc3_probe(struct platform_device *pdev) ret = dwc3_core_get_phy(dwc); if (ret) - return ret; + goto err0; spin_lock_init(&dwc->lock); platform_set_drvdata(pdev, dwc); @@ -899,7 +895,7 @@ static int dwc3_probe(struct platform_device *pdev) if (ret) { dev_err(dwc->dev, "failed to allocate event buffers\n"); ret = -ENOMEM; - goto err0; + goto err1; } if (IS_ENABLED(CONFIG_USB_DWC3_HOST)) @@ -913,65 +909,81 @@ static int dwc3_probe(struct platform_device *pdev) ret = dwc3_core_init(dwc); if (ret) { dev_err(dev, "failed to initialize core\n"); - goto err0; + goto err1; } usb_phy_set_suspend(dwc->usb2_phy, 0); usb_phy_set_suspend(dwc->usb3_phy, 0); ret = phy_power_on(dwc->usb2_generic_phy); if (ret < 0) - goto err1; + goto err2; ret = phy_power_on(dwc->usb3_generic_phy); if (ret < 0) - goto err_usb2phy_power; + goto err3; ret = dwc3_event_buffers_setup(dwc); if (ret) { dev_err(dwc->dev, "failed to setup event buffers\n"); - goto err_usb3phy_power; + goto err4; } ret = dwc3_core_init_mode(dwc); if (ret) - goto err2; + goto err5; ret = dwc3_debugfs_init(dwc); if (ret) { dev_err(dev, "failed to initialize debugfs\n"); - goto err3; + goto err6; } pm_runtime_allow(dev); return 0; -err3: +err6: dwc3_core_exit_mode(dwc); -err2: +err5: dwc3_event_buffers_cleanup(dwc); -err_usb3phy_power: +err4: phy_power_off(dwc->usb3_generic_phy); -err_usb2phy_power: +err3: phy_power_off(dwc->usb2_generic_phy); -err1: +err2: usb_phy_set_suspend(dwc->usb2_phy, 1); usb_phy_set_suspend(dwc->usb3_phy, 1); dwc3_core_exit(dwc); -err0: +err1: dwc3_free_event_buffers(dwc); +err0: + /* + * restore res->start back to its original value so that, in case the + * probe is deferred, we don't end up getting error in request the + * memory region the next time probe is called. + */ + res->start -= DWC3_GLOBALS_REGS_START; + return ret; } static int dwc3_remove(struct platform_device *pdev) { struct dwc3 *dwc = platform_get_drvdata(pdev); + struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + + /* + * restore res->start back to its original value so that, in case the + * probe is deferred, we don't end up getting error in request the + * memory region the next time probe is called. + */ + res->start -= DWC3_GLOBALS_REGS_START; dwc3_debugfs_exit(dwc); dwc3_core_exit_mode(dwc); -- cgit v1.2.3 From 3d0184d087573b7606de45a8e4f01b6222caa47a Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Tue, 2 Sep 2014 14:12:26 -0500 Subject: usb: dwc3: omap: call of_platform_depopulate() instead This patch fixes a bug where removing dwc3-omap.ko would not trigger removal of dwc3.ko. of_platform_depopulate() already bakes an easy to use API for removing all our children which were populated during probe(); Let's use that one instead of cooking our own solution. Note that this is kind of a revert of commit c5a1fbc (usb: dwc3: dwc3-omap: Fix the crash on module removal) although we can't simply revert that because a direct call to platform_device_unregister would also be flakey. Signed-off-by: Felipe Balbi --- drivers/usb/dwc3/dwc3-omap.c | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) diff --git a/drivers/usb/dwc3/dwc3-omap.c b/drivers/usb/dwc3/dwc3-omap.c index 52e0c4e5e48e..edba5348be18 100644 --- a/drivers/usb/dwc3/dwc3-omap.c +++ b/drivers/usb/dwc3/dwc3-omap.c @@ -325,15 +325,6 @@ static irqreturn_t dwc3_omap_interrupt(int irq, void *_omap) return IRQ_HANDLED; } -static int dwc3_omap_remove_core(struct device *dev, void *c) -{ - struct platform_device *pdev = to_platform_device(dev); - - of_device_unregister(pdev); - - return 0; -} - static void dwc3_omap_enable_irqs(struct dwc3_omap *omap) { u32 reg; @@ -600,7 +591,7 @@ static int dwc3_omap_remove(struct platform_device *pdev) if (omap->extcon_id_dev.edev) extcon_unregister_interest(&omap->extcon_id_dev); dwc3_omap_disable_irqs(omap); - device_for_each_child(&pdev->dev, NULL, dwc3_omap_remove_core); + of_platform_depopulate(omap->dev); pm_runtime_put_sync(&pdev->dev); pm_runtime_disable(&pdev->dev); -- cgit v1.2.3 From 8f2c9544aba636134303105ecb164190a39dece4 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Thu, 4 Sep 2014 13:14:49 -0500 Subject: usb: dwc3: gadget: drop unnecessary loop when cleaning up TRBs Now that we're using XFERINPROGRESS for all endpoint types (except Control), we will *always* be completing one TRB at a time, so it's safe to remove the loop from dwc3_cleanup_done_reqs. Signed-off-by: Felipe Balbi --- drivers/usb/dwc3/gadget.c | 37 ++++++++++++++++--------------------- 1 file changed, 16 insertions(+), 21 deletions(-) diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c index a03a485205c7..8946c32047e9 100644 --- a/drivers/usb/dwc3/gadget.c +++ b/drivers/usb/dwc3/gadget.c @@ -1855,32 +1855,27 @@ static int dwc3_cleanup_done_reqs(struct dwc3 *dwc, struct dwc3_ep *dep, unsigned int i; int ret; + req = next_request(&dep->req_queued); + if (!req) { + WARN_ON_ONCE(1); + return 1; + } + i = 0; do { - req = next_request(&dep->req_queued); - if (!req) { - WARN_ON_ONCE(1); - return 1; - } - i = 0; - do { - slot = req->start_slot + i; - if ((slot == DWC3_TRB_NUM - 1) && + slot = req->start_slot + i; + if ((slot == DWC3_TRB_NUM - 1) && usb_endpoint_xfer_isoc(dep->endpoint.desc)) - slot++; - slot %= DWC3_TRB_NUM; - trb = &dep->trb_pool[slot]; - - ret = __dwc3_cleanup_done_trbs(dwc, dep, req, trb, - event, status); - if (ret) - break; - }while (++i < req->request.num_mapped_sgs); - - dwc3_gadget_giveback(dep, req, status); + slot++; + slot %= DWC3_TRB_NUM; + trb = &dep->trb_pool[slot]; + ret = __dwc3_cleanup_done_trbs(dwc, dep, req, trb, + event, status); if (ret) break; - } while (1); + } while (++i < req->request.num_mapped_sgs); + + dwc3_gadget_giveback(dep, req, status); if (usb_endpoint_xfer_isoc(dep->endpoint.desc) && list_empty(&dep->req_queued)) { -- cgit v1.2.3 From 25140ce627f43df1d425d591ac3d360b48ae24e1 Mon Sep 17 00:00:00 2001 From: Joe Perches Date: Sat, 21 Feb 2015 18:53:37 -0800 Subject: usb: gadget: udc: pxa27x_udc: Remove use of seq_printf return value The seq_printf return value, because it's frequently misused, will eventually be converted to void. See: commit 1f33c41c03da ("seq_file: Rename seq_overflow() to seq_has_overflowed() and make public") While there, simplify the error handler logic by returning immediately and remove the unnecessary labels. Tested-by: Robert Jarzmik Signed-off-by: Joe Perches Signed-off-by: Felipe Balbi --- drivers/usb/gadget/udc/pxa27x_udc.c | 132 ++++++++++++++++-------------------- 1 file changed, 60 insertions(+), 72 deletions(-) diff --git a/drivers/usb/gadget/udc/pxa27x_udc.c b/drivers/usb/gadget/udc/pxa27x_udc.c index 6a855fc9bd84..486f7546de8c 100644 --- a/drivers/usb/gadget/udc/pxa27x_udc.c +++ b/drivers/usb/gadget/udc/pxa27x_udc.c @@ -93,50 +93,46 @@ static void handle_ep(struct pxa_ep *ep); static int state_dbg_show(struct seq_file *s, void *p) { struct pxa_udc *udc = s->private; - int pos = 0, ret; u32 tmp; - ret = -ENODEV; if (!udc->driver) - goto out; + return -ENODEV; /* basic device status */ - pos += seq_printf(s, DRIVER_DESC "\n" - "%s version: %s\nGadget driver: %s\n", - driver_name, DRIVER_VERSION, - udc->driver ? udc->driver->driver.name : "(none)"); + seq_printf(s, DRIVER_DESC "\n" + "%s version: %s\n" + "Gadget driver: %s\n", + driver_name, DRIVER_VERSION, + udc->driver ? udc->driver->driver.name : "(none)"); tmp = udc_readl(udc, UDCCR); - pos += seq_printf(s, - "udccr=0x%0x(%s%s%s%s%s%s%s%s%s%s), " - "con=%d,inter=%d,altinter=%d\n", tmp, - (tmp & UDCCR_OEN) ? " oen":"", - (tmp & UDCCR_AALTHNP) ? " aalthnp":"", - (tmp & UDCCR_AHNP) ? " rem" : "", - (tmp & UDCCR_BHNP) ? " rstir" : "", - (tmp & UDCCR_DWRE) ? " dwre" : "", - (tmp & UDCCR_SMAC) ? " smac" : "", - (tmp & UDCCR_EMCE) ? " emce" : "", - (tmp & UDCCR_UDR) ? " udr" : "", - (tmp & UDCCR_UDA) ? " uda" : "", - (tmp & UDCCR_UDE) ? " ude" : "", - (tmp & UDCCR_ACN) >> UDCCR_ACN_S, - (tmp & UDCCR_AIN) >> UDCCR_AIN_S, - (tmp & UDCCR_AAISN) >> UDCCR_AAISN_S); + seq_printf(s, + "udccr=0x%0x(%s%s%s%s%s%s%s%s%s%s), con=%d,inter=%d,altinter=%d\n", + tmp, + (tmp & UDCCR_OEN) ? " oen":"", + (tmp & UDCCR_AALTHNP) ? " aalthnp":"", + (tmp & UDCCR_AHNP) ? " rem" : "", + (tmp & UDCCR_BHNP) ? " rstir" : "", + (tmp & UDCCR_DWRE) ? " dwre" : "", + (tmp & UDCCR_SMAC) ? " smac" : "", + (tmp & UDCCR_EMCE) ? " emce" : "", + (tmp & UDCCR_UDR) ? " udr" : "", + (tmp & UDCCR_UDA) ? " uda" : "", + (tmp & UDCCR_UDE) ? " ude" : "", + (tmp & UDCCR_ACN) >> UDCCR_ACN_S, + (tmp & UDCCR_AIN) >> UDCCR_AIN_S, + (tmp & UDCCR_AAISN) >> UDCCR_AAISN_S); /* registers for device and ep0 */ - pos += seq_printf(s, "udcicr0=0x%08x udcicr1=0x%08x\n", - udc_readl(udc, UDCICR0), udc_readl(udc, UDCICR1)); - pos += seq_printf(s, "udcisr0=0x%08x udcisr1=0x%08x\n", - udc_readl(udc, UDCISR0), udc_readl(udc, UDCISR1)); - pos += seq_printf(s, "udcfnr=%d\n", udc_readl(udc, UDCFNR)); - pos += seq_printf(s, "irqs: reset=%lu, suspend=%lu, resume=%lu, " - "reconfig=%lu\n", - udc->stats.irqs_reset, udc->stats.irqs_suspend, - udc->stats.irqs_resume, udc->stats.irqs_reconfig); - - ret = 0; -out: - return ret; + seq_printf(s, "udcicr0=0x%08x udcicr1=0x%08x\n", + udc_readl(udc, UDCICR0), udc_readl(udc, UDCICR1)); + seq_printf(s, "udcisr0=0x%08x udcisr1=0x%08x\n", + udc_readl(udc, UDCISR0), udc_readl(udc, UDCISR1)); + seq_printf(s, "udcfnr=%d\n", udc_readl(udc, UDCFNR)); + seq_printf(s, "irqs: reset=%lu, suspend=%lu, resume=%lu, reconfig=%lu\n", + udc->stats.irqs_reset, udc->stats.irqs_suspend, + udc->stats.irqs_resume, udc->stats.irqs_reconfig); + + return 0; } static int queues_dbg_show(struct seq_file *s, void *p) @@ -144,75 +140,67 @@ static int queues_dbg_show(struct seq_file *s, void *p) struct pxa_udc *udc = s->private; struct pxa_ep *ep; struct pxa27x_request *req; - int pos = 0, i, maxpkt, ret; + int i, maxpkt; - ret = -ENODEV; if (!udc->driver) - goto out; + return -ENODEV; /* dump endpoint queues */ for (i = 0; i < NR_PXA_ENDPOINTS; i++) { ep = &udc->pxa_ep[i]; maxpkt = ep->fifo_size; - pos += seq_printf(s, "%-12s max_pkt=%d %s\n", - EPNAME(ep), maxpkt, "pio"); + seq_printf(s, "%-12s max_pkt=%d %s\n", + EPNAME(ep), maxpkt, "pio"); if (list_empty(&ep->queue)) { - pos += seq_printf(s, "\t(nothing queued)\n"); + seq_puts(s, "\t(nothing queued)\n"); continue; } list_for_each_entry(req, &ep->queue, queue) { - pos += seq_printf(s, "\treq %p len %d/%d buf %p\n", - &req->req, req->req.actual, - req->req.length, req->req.buf); + seq_printf(s, "\treq %p len %d/%d buf %p\n", + &req->req, req->req.actual, + req->req.length, req->req.buf); } } - ret = 0; -out: - return ret; + return 0; } static int eps_dbg_show(struct seq_file *s, void *p) { struct pxa_udc *udc = s->private; struct pxa_ep *ep; - int pos = 0, i, ret; + int i; u32 tmp; - ret = -ENODEV; if (!udc->driver) - goto out; + return -ENODEV; ep = &udc->pxa_ep[0]; tmp = udc_ep_readl(ep, UDCCSR); - pos += seq_printf(s, "udccsr0=0x%03x(%s%s%s%s%s%s%s)\n", tmp, - (tmp & UDCCSR0_SA) ? " sa" : "", - (tmp & UDCCSR0_RNE) ? " rne" : "", - (tmp & UDCCSR0_FST) ? " fst" : "", - (tmp & UDCCSR0_SST) ? " sst" : "", - (tmp & UDCCSR0_DME) ? " dme" : "", - (tmp & UDCCSR0_IPR) ? " ipr" : "", - (tmp & UDCCSR0_OPC) ? " opc" : ""); + seq_printf(s, "udccsr0=0x%03x(%s%s%s%s%s%s%s)\n", + tmp, + (tmp & UDCCSR0_SA) ? " sa" : "", + (tmp & UDCCSR0_RNE) ? " rne" : "", + (tmp & UDCCSR0_FST) ? " fst" : "", + (tmp & UDCCSR0_SST) ? " sst" : "", + (tmp & UDCCSR0_DME) ? " dme" : "", + (tmp & UDCCSR0_IPR) ? " ipr" : "", + (tmp & UDCCSR0_OPC) ? " opc" : ""); for (i = 0; i < NR_PXA_ENDPOINTS; i++) { ep = &udc->pxa_ep[i]; tmp = i? udc_ep_readl(ep, UDCCR) : udc_readl(udc, UDCCR); - pos += seq_printf(s, "%-12s: " - "IN %lu(%lu reqs), OUT %lu(%lu reqs), " - "irqs=%lu, udccr=0x%08x, udccsr=0x%03x, " - "udcbcr=%d\n", - EPNAME(ep), - ep->stats.in_bytes, ep->stats.in_ops, - ep->stats.out_bytes, ep->stats.out_ops, - ep->stats.irqs, - tmp, udc_ep_readl(ep, UDCCSR), - udc_ep_readl(ep, UDCBCR)); + seq_printf(s, "%-12s: IN %lu(%lu reqs), OUT %lu(%lu reqs), irqs=%lu, udccr=0x%08x, udccsr=0x%03x, udcbcr=%d\n", + EPNAME(ep), + ep->stats.in_bytes, ep->stats.in_ops, + ep->stats.out_bytes, ep->stats.out_ops, + ep->stats.irqs, + tmp, udc_ep_readl(ep, UDCCSR), + udc_ep_readl(ep, UDCBCR)); } - ret = 0; -out: - return ret; + return 0; } static int eps_dbg_open(struct inode *inode, struct file *file) -- cgit v1.2.3 From adf9c3c85615f41c08559086e0d9ecdc6cc9db71 Mon Sep 17 00:00:00 2001 From: Joseph Kogut Date: Mon, 16 Feb 2015 19:32:46 -0700 Subject: usb: move definition of PCI_VENDOR_ID_SYNOPSYS to linux/pci_ids.h Removed FIXME from usb/dwc3/dwc3-pci.c by moving definition of PCI_VENDOR_ID_SYNOPSYS shared with usb/dwc2 to linux/pci_ids.h. Signed-off-by: Joseph Kogut Signed-off-by: Felipe Balbi --- drivers/usb/dwc2/pci.c | 1 - drivers/usb/dwc3/dwc3-pci.c | 2 -- include/linux/pci_ids.h | 2 ++ 3 files changed, 2 insertions(+), 3 deletions(-) diff --git a/drivers/usb/dwc2/pci.c b/drivers/usb/dwc2/pci.c index a4e724b0a62e..6646adb1fb17 100644 --- a/drivers/usb/dwc2/pci.c +++ b/drivers/usb/dwc2/pci.c @@ -54,7 +54,6 @@ #include "core.h" #include "hcd.h" -#define PCI_VENDOR_ID_SYNOPSYS 0x16c3 #define PCI_PRODUCT_ID_HAPS_HSOTG 0xabc0 static const char dwc2_driver_name[] = "dwc2"; diff --git a/drivers/usb/dwc3/dwc3-pci.c b/drivers/usb/dwc3/dwc3-pci.c index 8d950569d557..b773fb53d6a7 100644 --- a/drivers/usb/dwc3/dwc3-pci.c +++ b/drivers/usb/dwc3/dwc3-pci.c @@ -24,8 +24,6 @@ #include "platform_data.h" -/* FIXME define these in */ -#define PCI_VENDOR_ID_SYNOPSYS 0x16c3 #define PCI_DEVICE_ID_SYNOPSYS_HAPSUSB3 0xabcd #define PCI_DEVICE_ID_INTEL_BYT 0x0f37 #define PCI_DEVICE_ID_INTEL_MRFLD 0x119e diff --git a/include/linux/pci_ids.h b/include/linux/pci_ids.h index e63c02a93f6b..38cff8f6716d 100644 --- a/include/linux/pci_ids.h +++ b/include/linux/pci_ids.h @@ -2315,6 +2315,8 @@ #define PCI_VENDOR_ID_CENATEK 0x16CA #define PCI_DEVICE_ID_CENATEK_IDE 0x0001 +#define PCI_VENDOR_ID_SYNOPSYS 0x16c3 + #define PCI_VENDOR_ID_VITESSE 0x1725 #define PCI_DEVICE_ID_VITESSE_VSC7174 0x7174 -- cgit v1.2.3 From ab7580c1479f9cc9d6a70a5184687a4d807fc612 Mon Sep 17 00:00:00 2001 From: Sudeep Holla Date: Thu, 26 Feb 2015 11:47:57 +0000 Subject: usb: isp1760: add peripheral/device controller chip id As per the SAF1761 data sheet[0], the DcChipID register represents the hardware version number (0001h) and the chip ID (1582h) for the Peripheral Controller. However as per the ISP1761 data sheet[1], the DcChipID register represents the hardware version number (0015h) and the chip ID (8210h) for the Peripheral Controller. This patch adds support for both the chip ID values. [0] http://www.nxp.com/documents/data_sheet/SAF1761.pdf [1] http://pdf.datasheetcatalog.com/datasheets2/74/742102_1.pdf Cc: Felipe Balbi Cc: Laurent Pinchart Signed-off-by: Sudeep Holla Acked-by: Laurent Pinchart Signed-off-by: Felipe Balbi --- drivers/usb/isp1760/isp1760-udc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/usb/isp1760/isp1760-udc.c b/drivers/usb/isp1760/isp1760-udc.c index 9612d7990565..6d618b3fab07 100644 --- a/drivers/usb/isp1760/isp1760-udc.c +++ b/drivers/usb/isp1760/isp1760-udc.c @@ -1411,7 +1411,7 @@ static int isp1760_udc_init(struct isp1760_udc *udc) return -ENODEV; } - if (chipid != 0x00011582) { + if (chipid != 0x00011582 && chipid != 0x00158210) { dev_err(udc->isp->dev, "udc: invalid chip ID 0x%08x\n", chipid); return -ENODEV; } -- cgit v1.2.3 From 896f7ea37f53666a72080d0958213d12ae59deaa Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Wed, 25 Feb 2015 14:03:23 -0600 Subject: usb: musb: core: remove unnecessary logical comparison devctl & MUSB_DEVCTL_HM represents a single bit, just check for the bit, there's really no need to compare the result against 0. Tested-by: Bin Liu Signed-off-by: Felipe Balbi --- drivers/usb/musb/musb_core.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/usb/musb/musb_core.c b/drivers/usb/musb/musb_core.c index e59ae7395ba8..8066dbab1045 100644 --- a/drivers/usb/musb/musb_core.c +++ b/drivers/usb/musb/musb_core.c @@ -879,7 +879,7 @@ b_host: */ if (int_usb & MUSB_INTR_RESET) { handled = IRQ_HANDLED; - if ((devctl & MUSB_DEVCTL_HM) != 0) { + if (devctl & MUSB_DEVCTL_HM) { /* * Looks like non-HS BABBLE can be ignored, but * HS BABBLE is an error condition. For HS the solution -- cgit v1.2.3 From d57a27711939dcb289b3d17ac48fca99f0fd245d Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Wed, 25 Feb 2015 14:05:15 -0600 Subject: usb: musb: core: add missing curly braces no functional changes, clean up only. Tested-by: Bin Liu Signed-off-by: Felipe Balbi --- drivers/usb/musb/musb_core.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/usb/musb/musb_core.c b/drivers/usb/musb/musb_core.c index 8066dbab1045..21ab26636631 100644 --- a/drivers/usb/musb/musb_core.c +++ b/drivers/usb/musb/musb_core.c @@ -887,9 +887,9 @@ b_host: * caused BABBLE. When HS BABBLE happens we can only * stop the session. */ - if (devctl & (MUSB_DEVCTL_FSDEV | MUSB_DEVCTL_LSDEV)) + if (devctl & (MUSB_DEVCTL_FSDEV | MUSB_DEVCTL_LSDEV)) { dev_dbg(musb->controller, "BABBLE devctl: %02x\n", devctl); - else { + } else { ERR("Stopping host session -- babble\n"); musb_writeb(musb->mregs, MUSB_DEVCTL, 0); } -- cgit v1.2.3 From 28378d5ed5ca1221479d2f94c3b346691834822f Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Thu, 26 Feb 2015 10:54:27 -0600 Subject: usb: musb: core: fix highspeed check FSDEV is set for both HIGH and FULL speeds, the correct HIGHSPEED check is done through power register's HSMODE bit. Tested-by: Bin Liu Signed-off-by: Felipe Balbi --- drivers/usb/musb/musb_core.c | 21 ++++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/drivers/usb/musb/musb_core.c b/drivers/usb/musb/musb_core.c index 21ab26636631..cf7b10e5963e 100644 --- a/drivers/usb/musb/musb_core.c +++ b/drivers/usb/musb/musb_core.c @@ -880,16 +880,23 @@ b_host: if (int_usb & MUSB_INTR_RESET) { handled = IRQ_HANDLED; if (devctl & MUSB_DEVCTL_HM) { + u8 power = musb_readl(musb->mregs, MUSB_POWER); + /* * Looks like non-HS BABBLE can be ignored, but - * HS BABBLE is an error condition. For HS the solution - * is to avoid babble in the first place and fix what - * caused BABBLE. When HS BABBLE happens we can only - * stop the session. + * HS BABBLE is an error condition. + * + * For HS the solution is to avoid babble in the first + * place and fix what caused BABBLE. + * + * When HS BABBLE happens what we can depends on which + * platform MUSB is running, because some platforms + * implemented proprietary means for 'recovering' from + * Babble conditions. One such platform is AM335x. In + * most cases, however, the only thing we can do is drop + * the session. */ - if (devctl & (MUSB_DEVCTL_FSDEV | MUSB_DEVCTL_LSDEV)) { - dev_dbg(musb->controller, "BABBLE devctl: %02x\n", devctl); - } else { + if (power & MUSB_POWER_HSMODE) { ERR("Stopping host session -- babble\n"); musb_writeb(musb->mregs, MUSB_DEVCTL, 0); } -- cgit v1.2.3 From d0cddae7926f39e8fd488f62496cfebf7a5e757d Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Thu, 26 Feb 2015 10:55:13 -0600 Subject: usb: musb: dsps: return error code if reset fails if reset fails, we should return a *negative* error code, not a positive value. Tested-by: Bin Liu Signed-off-by: Felipe Balbi --- drivers/usb/musb/musb_dsps.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/usb/musb/musb_dsps.c b/drivers/usb/musb/musb_dsps.c index a900c9877195..af614f49cd98 100644 --- a/drivers/usb/musb/musb_dsps.c +++ b/drivers/usb/musb/musb_dsps.c @@ -652,7 +652,7 @@ static int dsps_musb_reset(struct musb *musb) session_restart = 1; } - return !session_restart; + return session_restart ? 0 : -EPIPE; } static struct musb_platform_ops dsps_ops = { -- cgit v1.2.3 From d0fc0a20b5b7babe0fe1552204bd52505a4f93dd Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Wed, 25 Feb 2015 14:07:52 -0600 Subject: usb: musb: core: move babble recovery inside babble check There was already a proper place where we were checking for babble interrupts, move babble recovery there. Tested-by: Bin Liu Signed-off-by: Felipe Balbi --- drivers/usb/musb/musb_core.c | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/drivers/usb/musb/musb_core.c b/drivers/usb/musb/musb_core.c index cf7b10e5963e..9ea02d4cc2c2 100644 --- a/drivers/usb/musb/musb_core.c +++ b/drivers/usb/musb/musb_core.c @@ -899,6 +899,12 @@ b_host: if (power & MUSB_POWER_HSMODE) { ERR("Stopping host session -- babble\n"); musb_writeb(musb->mregs, MUSB_DEVCTL, 0); + + if (is_host_active(musb)) { + musb_generic_disable(musb); + schedule_delayed_work(&musb->recover_work, + msecs_to_jiffies(100)); + } } } else { dev_dbg(musb->controller, "BUS RESET as %s\n", @@ -938,13 +944,6 @@ b_host: } } - /* handle babble condition */ - if (int_usb & MUSB_INTR_BABBLE && is_host_active(musb)) { - musb_generic_disable(musb); - schedule_delayed_work(&musb->recover_work, - msecs_to_jiffies(100)); - } - #if 0 /* REVISIT ... this would be for multiplexing periodic endpoints, or * supporting transfer phasing to prevent exceeding ISO bandwidth -- cgit v1.2.3 From 0acff6b83106ef699f17828caea6545bcc716f14 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Wed, 25 Feb 2015 14:14:15 -0600 Subject: usb: musb: core: break long line no functional changes, clean up only. Tested-by: Bin Liu Signed-off-by: Felipe Balbi --- drivers/usb/musb/musb_core.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/usb/musb/musb_core.c b/drivers/usb/musb/musb_core.c index 9ea02d4cc2c2..b5dfe83dce76 100644 --- a/drivers/usb/musb/musb_core.c +++ b/drivers/usb/musb/musb_core.c @@ -534,7 +534,8 @@ static irqreturn_t musb_stage0_irq(struct musb *musb, u8 int_usb, */ if (int_usb & MUSB_INTR_RESUME) { handled = IRQ_HANDLED; - dev_dbg(musb->controller, "RESUME (%s)\n", usb_otg_state_string(musb->xceiv->otg->state)); + dev_dbg(musb->controller, "RESUME (%s)\n", + usb_otg_state_string(musb->xceiv->otg->state)); if (devctl & MUSB_DEVCTL_HM) { void __iomem *mbase = musb->mregs; -- cgit v1.2.3 From 46571889ec435f1bf29d9094f062948b26630723 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Wed, 25 Feb 2015 14:30:55 -0600 Subject: usb: musb: core: remove unnecessary reg access from resume IRQ when musb is operating as host and a remote wakeup fires up, a resume interrupt will be raised. At that point SUSPENDM bit is automatically cleared and RESUME bit is automatically set. Remove those two from IRQ handler. Tested-by: Bin Liu Signed-off-by: Felipe Balbi --- drivers/usb/musb/musb_core.c | 15 --------------- 1 file changed, 15 deletions(-) diff --git a/drivers/usb/musb/musb_core.c b/drivers/usb/musb/musb_core.c index b5dfe83dce76..3fb7d6e032f1 100644 --- a/drivers/usb/musb/musb_core.c +++ b/drivers/usb/musb/musb_core.c @@ -538,27 +538,12 @@ static irqreturn_t musb_stage0_irq(struct musb *musb, u8 int_usb, usb_otg_state_string(musb->xceiv->otg->state)); if (devctl & MUSB_DEVCTL_HM) { - void __iomem *mbase = musb->mregs; - u8 power; - switch (musb->xceiv->otg->state) { case OTG_STATE_A_SUSPEND: /* remote wakeup? later, GetPortStatus * will stop RESUME signaling */ - power = musb_readb(musb->mregs, MUSB_POWER); - if (power & MUSB_POWER_SUSPENDM) { - /* spurious */ - musb->int_usb &= ~MUSB_INTR_SUSPEND; - dev_dbg(musb->controller, "Spurious SUSPENDM\n"); - break; - } - - power &= ~MUSB_POWER_SUSPENDM; - musb_writeb(mbase, MUSB_POWER, - power | MUSB_POWER_RESUME); - musb->port1_status |= (USB_PORT_STAT_C_SUSPEND << 16) | MUSB_PORT_STAT_RESUME; -- cgit v1.2.3 From b2c7361bd07f94e6280507a20e0541870d5d7a1a Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Wed, 25 Feb 2015 14:48:50 -0600 Subject: usb: musb: core: there is no connect interrupt in peripheral mode MUSB does not generate a connect IRQ when working in peripheral mode. Tested-by: Bin Liu Signed-off-by: Felipe Balbi --- drivers/usb/musb/musb_core.c | 4 ---- 1 file changed, 4 deletions(-) diff --git a/drivers/usb/musb/musb_core.c b/drivers/usb/musb/musb_core.c index 3fb7d6e032f1..7ac69799a1db 100644 --- a/drivers/usb/musb/musb_core.c +++ b/drivers/usb/musb/musb_core.c @@ -761,10 +761,6 @@ static irqreturn_t musb_stage0_irq(struct musb *musb, u8 int_usb, musb->ep0_stage = MUSB_EP0_START; - /* flush endpoints when transitioning from Device Mode */ - if (is_peripheral_active(musb)) { - /* REVISIT HNP; just force disconnect */ - } musb->intrtxe = musb->epmask; musb_writew(musb->mregs, MUSB_INTRTXE, musb->intrtxe); musb->intrrxe = musb->epmask & 0xfffe; -- cgit v1.2.3 From 52b9e6eb07f739207bd7d4257fdfbb24592d096a Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Wed, 25 Feb 2015 16:04:39 -0600 Subject: usb: musb: dsps: remove babble check from dsps irq handler musb->int_usb already contains the correct information for musb-core to handle babble. In fact, this very check was just causing a nonsensical babble interrupt storm. With this I can get test.sh to run and, even though all tests fail with timeout, that's still better than locking up the system due to IRQ storm. Also, if I remove g_zero and load g_mass_storage, then everything works fine again. Tested-by: Bin Liu Signed-off-by: Felipe Balbi --- drivers/usb/musb/musb_core.c | 2 +- drivers/usb/musb/musb_dsps.c | 22 ---------------------- 2 files changed, 1 insertion(+), 23 deletions(-) diff --git a/drivers/usb/musb/musb_core.c b/drivers/usb/musb/musb_core.c index 7ac69799a1db..d7730c7e7938 100644 --- a/drivers/usb/musb/musb_core.c +++ b/drivers/usb/musb/musb_core.c @@ -879,7 +879,7 @@ b_host: * the session. */ if (power & MUSB_POWER_HSMODE) { - ERR("Stopping host session -- babble\n"); + dev_err(musb->controller, "Babble\n"); musb_writeb(musb->mregs, MUSB_DEVCTL, 0); if (is_host_active(musb)) { diff --git a/drivers/usb/musb/musb_dsps.c b/drivers/usb/musb/musb_dsps.c index af614f49cd98..8f96e79dd069 100644 --- a/drivers/usb/musb/musb_dsps.c +++ b/drivers/usb/musb/musb_dsps.c @@ -330,28 +330,6 @@ static irqreturn_t dsps_interrupt(int irq, void *hci) dev_dbg(musb->controller, "usbintr (%x) epintr(%x)\n", usbintr, epintr); - /* - * DRVVBUS IRQs are the only proxy we have (a very poor one!) for - * DSPS IP's missing ID change IRQ. We need an ID change IRQ to - * switch appropriately between halves of the OTG state machine. - * Managing DEVCTL.SESSION per Mentor docs requires that we know its - * value but DEVCTL.BDEVICE is invalid without DEVCTL.SESSION set. - * Also, DRVVBUS pulses for SRP (but not at 5V) ... - */ - if (is_host_active(musb) && usbintr & MUSB_INTR_BABBLE) { - pr_info("CAUTION: musb: Babble Interrupt Occurred\n"); - - /* - * When a babble condition occurs, the musb controller removes - * the session and is no longer in host mode. Hence, all - * devices connected to its root hub get disconnected. - * - * Hand this error down to the musb core isr, so it can - * recover. - */ - musb->int_usb = MUSB_INTR_BABBLE | MUSB_INTR_DISCONNECT; - musb->int_tx = musb->int_rx = 0; - } if (usbintr & ((1 << wrp->drvvbus) << wrp->usb_shift)) { int drvvbus = dsps_readl(reg_base, wrp->status); -- cgit v1.2.3 From f860f0b1ea76b9f15d24db8fa98823eb15273afb Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Thu, 26 Feb 2015 11:01:03 -0600 Subject: usb: musb: dsps: check for the single bit We want to check if that particular bit is set. It could very well be that bootloader (or romcode) has fiddled with MUSB before us which could leave other bits set in this register. Tested-by: Bin Liu Signed-off-by: Felipe Balbi --- drivers/usb/musb/musb_dsps.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/usb/musb/musb_dsps.c b/drivers/usb/musb/musb_dsps.c index 8f96e79dd069..e210b75fb6f2 100644 --- a/drivers/usb/musb/musb_dsps.c +++ b/drivers/usb/musb/musb_dsps.c @@ -475,7 +475,7 @@ static int dsps_musb_init(struct musb *musb) * logic enabled. */ val = dsps_readb(musb->mregs, MUSB_BABBLE_CTL); - if (val == MUSB_BABBLE_RCV_DISABLE) { + if (val & MUSB_BABBLE_RCV_DISABLE) { glue->sw_babble_enabled = true; val |= MUSB_BABBLE_SW_SESSION_CTRL; dsps_writeb(musb->mregs, MUSB_BABBLE_CTL, val); -- cgit v1.2.3 From a67cab72b87c7bb970bec8bc060a8946c5dfa1c5 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Thu, 26 Feb 2015 11:09:20 -0600 Subject: usb: musb: core: controller drops session automatically Whenever babble happens, MUSB controller will drop session automatically. The only case where it won't drop the session, is when we're running on AM335x and SW_SESSION_CTRL bit has been set. In that case, controller will not touch session bit so SW has a chance to recover from babble condition. Tested-by: Bin Liu Signed-off-by: Felipe Balbi --- drivers/usb/musb/musb_core.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/usb/musb/musb_core.c b/drivers/usb/musb/musb_core.c index d7730c7e7938..b86e975cba2b 100644 --- a/drivers/usb/musb/musb_core.c +++ b/drivers/usb/musb/musb_core.c @@ -880,7 +880,6 @@ b_host: */ if (power & MUSB_POWER_HSMODE) { dev_err(musb->controller, "Babble\n"); - musb_writeb(musb->mregs, MUSB_DEVCTL, 0); if (is_host_active(musb)) { musb_generic_disable(musb); -- cgit v1.2.3 From 3709ffca6485bd1b03b1fe2d9eb384dcf5db87a6 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Thu, 26 Feb 2015 11:11:33 -0600 Subject: usb: musb: dsps: add dsps_ prefix to sw_babble_control this makes it easier to filter function traces. No functional changes. Tested-by: Bin Liu Signed-off-by: Felipe Balbi --- drivers/usb/musb/musb_dsps.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/usb/musb/musb_dsps.c b/drivers/usb/musb/musb_dsps.c index e210b75fb6f2..85ebfa2c3858 100644 --- a/drivers/usb/musb/musb_dsps.c +++ b/drivers/usb/musb/musb_dsps.c @@ -549,7 +549,7 @@ static int dsps_musb_set_mode(struct musb *musb, u8 mode) return 0; } -static bool sw_babble_control(struct musb *musb) +static bool dsps_sw_babble_control(struct musb *musb) { u8 babble_ctl; bool session_restart = false; @@ -608,7 +608,7 @@ static int dsps_musb_reset(struct musb *musb) int session_restart = 0, error; if (glue->sw_babble_enabled) - session_restart = sw_babble_control(musb); + session_restart = dsps_sw_babble_control(musb); /* * In case of new silicon version babble condition can be recovered * without resetting the MUSB. But for older silicon versions, MUSB -- cgit v1.2.3 From e1eb3eb8b02c5be35ea1fedccc6c9c6d2c9b0165 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Thu, 26 Feb 2015 11:26:09 -0600 Subject: usb: musb: core: refactor IRQ enable/disable to separate functions sometimes we want to just mask/unmask interrupts without touching devctl register. For those cases, let's introduce musb_enable_interrupts and musb_disable_interrupts() Tested-by: Bin Liu Signed-off-by: Felipe Balbi --- drivers/usb/musb/musb_core.c | 37 ++++++++++++++++++++++++++----------- 1 file changed, 26 insertions(+), 11 deletions(-) diff --git a/drivers/usb/musb/musb_core.c b/drivers/usb/musb/musb_core.c index b86e975cba2b..8ae24266f1a8 100644 --- a/drivers/usb/musb/musb_core.c +++ b/drivers/usb/musb/musb_core.c @@ -507,7 +507,9 @@ void musb_hnp_stop(struct musb *musb) musb->port1_status &= ~(USB_PORT_STAT_C_CONNECTION << 16); } +static void musb_disable_interrupts(struct musb *musb); static void musb_generic_disable(struct musb *musb); + /* * Interrupt Service Routine to record USB "global" interrupts. * Since these do not happen often and signify things of @@ -977,7 +979,7 @@ b_host: /*-------------------------------------------------------------------------*/ -static void musb_generic_disable(struct musb *musb) +static void musb_disable_interrupts(struct musb *musb) { void __iomem *mbase = musb->mregs; u16 temp; @@ -989,16 +991,35 @@ static void musb_generic_disable(struct musb *musb) musb->intrrxe = 0; musb_writew(mbase, MUSB_INTRRXE, 0); - /* off */ - musb_writeb(mbase, MUSB_DEVCTL, 0); - /* flush pending interrupts */ temp = musb_readb(mbase, MUSB_INTRUSB); temp = musb_readw(mbase, MUSB_INTRTX); temp = musb_readw(mbase, MUSB_INTRRX); +} + +static void musb_enable_interrupts(struct musb *musb) +{ + void __iomem *regs = musb->mregs; + + /* Set INT enable registers, enable interrupts */ + musb->intrtxe = musb->epmask; + musb_writew(regs, MUSB_INTRTXE, musb->intrtxe); + musb->intrrxe = musb->epmask & 0xfffe; + musb_writew(regs, MUSB_INTRRXE, musb->intrrxe); + musb_writeb(regs, MUSB_INTRUSBE, 0xf7); } +static void musb_generic_disable(struct musb *musb) +{ + void __iomem *mbase = musb->mregs; + + musb_disable_interrupts(musb); + + /* off */ + musb_writeb(mbase, MUSB_DEVCTL, 0); +} + /* * Program the HDRC to start (enable interrupts, dma, etc.). */ @@ -1009,13 +1030,7 @@ void musb_start(struct musb *musb) dev_dbg(musb->controller, "<== devctl %02x\n", devctl); - /* Set INT enable registers, enable interrupts */ - musb->intrtxe = musb->epmask; - musb_writew(regs, MUSB_INTRTXE, musb->intrtxe); - musb->intrrxe = musb->epmask & 0xfffe; - musb_writew(regs, MUSB_INTRRXE, musb->intrrxe); - musb_writeb(regs, MUSB_INTRUSBE, 0xf7); - + musb_enable_interrupts(musb); musb_writeb(regs, MUSB_TESTMODE, 0); /* put into basic highspeed mode and start session */ -- cgit v1.2.3 From ba7ee8bb313c4a55066c4da30292aeb9abac82d8 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Thu, 26 Feb 2015 11:31:49 -0600 Subject: usb: musb: don't touch devctl from babble recovery We do *not* want to touch devctl at all when trying to recover from babble. All we want to do is mask IRQs until we're done without our babble recovery, at which point we will unmask IRQs. Tested-by: Bin Liu Signed-off-by: Felipe Balbi --- drivers/usb/musb/musb_core.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/drivers/usb/musb/musb_core.c b/drivers/usb/musb/musb_core.c index 8ae24266f1a8..a4e524cb8416 100644 --- a/drivers/usb/musb/musb_core.c +++ b/drivers/usb/musb/musb_core.c @@ -884,7 +884,7 @@ b_host: dev_err(musb->controller, "Babble\n"); if (is_host_active(musb)) { - musb_generic_disable(musb); + musb_disable_interrupts(musb); schedule_delayed_work(&musb->recover_work, msecs_to_jiffies(100)); } @@ -1838,8 +1838,10 @@ static void musb_recover_work(struct work_struct *data) int status, ret; ret = musb_platform_reset(musb); - if (ret) + if (ret) { + musb_enable_interrupts(musb); return; + } usb_phy_vbus_off(musb->xceiv); usleep_range(100, 200); -- cgit v1.2.3 From d5fa3e9f7398adf337f03fa3257d5e9b214078ee Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Thu, 26 Feb 2015 11:35:13 -0600 Subject: usb: musb: core: decrease delayed_work time When babble IRQ happens, we need to wait only 5.3us (320 cycles of 60MHz clock), we will give it some slack and schedule our work a 10 usecs into the future. Tested-by: Bin Liu Signed-off-by: Felipe Balbi --- drivers/usb/musb/musb_core.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/usb/musb/musb_core.c b/drivers/usb/musb/musb_core.c index a4e524cb8416..96d71fa2ae85 100644 --- a/drivers/usb/musb/musb_core.c +++ b/drivers/usb/musb/musb_core.c @@ -886,7 +886,7 @@ b_host: if (is_host_active(musb)) { musb_disable_interrupts(musb); schedule_delayed_work(&musb->recover_work, - msecs_to_jiffies(100)); + usecs_to_jiffies(10)); } } } else { -- cgit v1.2.3 From 011d0dd5400b84e593eecfc4a17fcfb6c0c5ac60 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Thu, 26 Feb 2015 14:00:52 -0600 Subject: usb: musb: dsps: do not reset musb on babble All we have to do is, really, drop session bit and let the session restart. Big thanks goes to Bin Liu for inspiring this work. Tested-by: Bin Liu Signed-off-by: Felipe Balbi --- drivers/usb/musb/musb_dsps.c | 23 ++--------------------- 1 file changed, 2 insertions(+), 21 deletions(-) diff --git a/drivers/usb/musb/musb_dsps.c b/drivers/usb/musb/musb_dsps.c index 85ebfa2c3858..a159de1225f3 100644 --- a/drivers/usb/musb/musb_dsps.c +++ b/drivers/usb/musb/musb_dsps.c @@ -604,31 +604,12 @@ static int dsps_musb_reset(struct musb *musb) { struct device *dev = musb->controller; struct dsps_glue *glue = dev_get_drvdata(dev->parent); - const struct dsps_musb_wrapper *wrp = glue->wrp; - int session_restart = 0, error; + int session_restart = 0; if (glue->sw_babble_enabled) session_restart = dsps_sw_babble_control(musb); - /* - * In case of new silicon version babble condition can be recovered - * without resetting the MUSB. But for older silicon versions, MUSB - * reset is needed - */ - if (session_restart || !glue->sw_babble_enabled) { - dev_info(musb->controller, "Restarting MUSB to recover from Babble\n"); - dsps_writel(musb->ctrl_base, wrp->control, (1 << wrp->reset)); - usleep_range(100, 200); - usb_phy_shutdown(musb->xceiv); - error = phy_power_off(musb->phy); - if (error) - dev_err(dev, "phy shutdown failed: %i\n", error); - usleep_range(100, 200); - usb_phy_init(musb->xceiv); - error = phy_power_on(musb->phy); - if (error) - dev_err(dev, "phy powerup failed: %i\n", error); + else session_restart = 1; - } return session_restart ? 0 : -EPIPE; } -- cgit v1.2.3 From b4dc38fd45b63e3da2bc98db5d283a15a637a2fa Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Thu, 26 Feb 2015 14:02:35 -0600 Subject: usb: musb: core: simplify musb_recover_work() we're not resetting musb at all, just restarting the session. This means we don't need to touch PHYs or VBUS or anything like that. Just make sure session bit is reenabled after MUSB dropped it. while at that, make sure to tell usbcore that we're dropping the session and, thus, disconnecting the device. Tested-by: Bin Liu Signed-off-by: Felipe Balbi --- drivers/usb/musb/musb_core.c | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/drivers/usb/musb/musb_core.c b/drivers/usb/musb/musb_core.c index 96d71fa2ae85..979bc2b0550f 100644 --- a/drivers/usb/musb/musb_core.c +++ b/drivers/usb/musb/musb_core.c @@ -1835,7 +1835,8 @@ static void musb_irq_work(struct work_struct *data) static void musb_recover_work(struct work_struct *data) { struct musb *musb = container_of(data, struct musb, recover_work.work); - int status, ret; + int ret; + u8 devctl; ret = musb_platform_reset(musb); if (ret) { @@ -1843,23 +1844,25 @@ static void musb_recover_work(struct work_struct *data) return; } - usb_phy_vbus_off(musb->xceiv); - usleep_range(100, 200); + /* drop session bit */ + devctl = musb_readb(musb->mregs, MUSB_DEVCTL); + devctl &= ~MUSB_DEVCTL_SESSION; + musb_writeb(musb->mregs, MUSB_DEVCTL, devctl); - usb_phy_vbus_on(musb->xceiv); - usleep_range(100, 200); + /* tell usbcore about it */ + musb_root_disconnect(musb); /* * When a babble condition occurs, the musb controller * removes the session bit and the endpoint config is lost. */ if (musb->dyn_fifo) - status = ep_config_from_table(musb); + ret = ep_config_from_table(musb); else - status = ep_config_from_hw(musb); + ret = ep_config_from_hw(musb); - /* start the session again */ - if (status == 0) + /* restart session */ + if (ret == 0) musb_start(musb); } -- cgit v1.2.3 From b28a6432405ca95b3da25630d79d2463c754a79c Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Thu, 26 Feb 2015 14:20:58 -0600 Subject: usb: musb: rename ->reset() to ->recover() recover is a much better name than reset, considering we don't really reset the IP, just run platform-specific babble recovery algorithm. while at that, also fix a typo in comment and add kdoc for recover memeber of platform_ops. Tested-by: Bin Liu Signed-off-by: Felipe Balbi --- drivers/usb/musb/musb_core.c | 2 +- drivers/usb/musb/musb_core.h | 13 +++++++------ drivers/usb/musb/musb_dsps.c | 4 ++-- 3 files changed, 10 insertions(+), 9 deletions(-) diff --git a/drivers/usb/musb/musb_core.c b/drivers/usb/musb/musb_core.c index 979bc2b0550f..bf9746287d7c 100644 --- a/drivers/usb/musb/musb_core.c +++ b/drivers/usb/musb/musb_core.c @@ -1838,7 +1838,7 @@ static void musb_recover_work(struct work_struct *data) int ret; u8 devctl; - ret = musb_platform_reset(musb); + ret = musb_platform_recover(musb); if (ret) { musb_enable_interrupts(musb); return; diff --git a/drivers/usb/musb/musb_core.h b/drivers/usb/musb/musb_core.h index 5e65958f7915..1e03c7ec82e4 100644 --- a/drivers/usb/musb/musb_core.h +++ b/drivers/usb/musb/musb_core.h @@ -160,7 +160,8 @@ struct musb_io; * @init: turns on clocks, sets up platform-specific registers, etc * @exit: undoes @init * @set_mode: forcefully changes operating mode - * @try_ilde: tries to idle the IP + * @try_idle: tries to idle the IP + * @recover: platform-specific babble recovery * @vbus_status: returns vbus status if possible * @set_vbus: forces vbus status * @adjust_channel_params: pre check for standard dma channel_program func @@ -196,7 +197,7 @@ struct musb_platform_ops { void (*write_fifo)(struct musb_hw_ep *hw_ep, u16 len, const u8 *buf); int (*set_mode)(struct musb *musb, u8 mode); void (*try_idle)(struct musb *musb, unsigned long timeout); - int (*reset)(struct musb *musb); + int (*recover)(struct musb *musb); int (*vbus_status)(struct musb *musb); void (*set_vbus)(struct musb *musb, int on); @@ -558,12 +559,12 @@ static inline void musb_platform_try_idle(struct musb *musb, musb->ops->try_idle(musb, timeout); } -static inline int musb_platform_reset(struct musb *musb) +static inline int musb_platform_recover(struct musb *musb) { - if (!musb->ops->reset) - return -EINVAL; + if (!musb->ops->recover) + return 0; - return musb->ops->reset(musb); + return musb->ops->recover(musb); } static inline int musb_platform_get_vbus_status(struct musb *musb) diff --git a/drivers/usb/musb/musb_dsps.c b/drivers/usb/musb/musb_dsps.c index a159de1225f3..30eb6ac29b81 100644 --- a/drivers/usb/musb/musb_dsps.c +++ b/drivers/usb/musb/musb_dsps.c @@ -600,7 +600,7 @@ static bool dsps_sw_babble_control(struct musb *musb) return session_restart; } -static int dsps_musb_reset(struct musb *musb) +static int dsps_musb_recover(struct musb *musb) { struct device *dev = musb->controller; struct dsps_glue *glue = dev_get_drvdata(dev->parent); @@ -624,7 +624,7 @@ static struct musb_platform_ops dsps_ops = { .try_idle = dsps_musb_try_idle, .set_mode = dsps_musb_set_mode, - .reset = dsps_musb_reset, + .recover = dsps_musb_recover, }; static u64 musb_dmamask = DMA_BIT_MASK(32); -- cgit v1.2.3 From 83b8f5b8c07c8cbad8c14c7b8767e7219a6c1813 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Thu, 26 Feb 2015 14:27:12 -0600 Subject: usb: musb: core: drop recover_work that's not needed anymore. Everything that we call is irq-safe, so we might as well not have a delayed work for babble recovery. Tested-by: Bin Liu Signed-off-by: Felipe Balbi --- drivers/usb/musb/musb_core.c | 17 +++++++++-------- drivers/usb/musb/musb_core.h | 1 - 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/drivers/usb/musb/musb_core.c b/drivers/usb/musb/musb_core.c index bf9746287d7c..b410af6b0510 100644 --- a/drivers/usb/musb/musb_core.c +++ b/drivers/usb/musb/musb_core.c @@ -509,6 +509,7 @@ void musb_hnp_stop(struct musb *musb) static void musb_disable_interrupts(struct musb *musb); static void musb_generic_disable(struct musb *musb); +static void musb_recover_from_babble(struct musb *musb); /* * Interrupt Service Routine to record USB "global" interrupts. @@ -885,8 +886,7 @@ b_host: if (is_host_active(musb)) { musb_disable_interrupts(musb); - schedule_delayed_work(&musb->recover_work, - usecs_to_jiffies(10)); + musb_recover_from_babble(musb); } } } else { @@ -1831,13 +1831,17 @@ static void musb_irq_work(struct work_struct *data) } } -/* Recover from babble interrupt conditions */ -static void musb_recover_work(struct work_struct *data) +static void musb_recover_from_babble(struct musb *musb) { - struct musb *musb = container_of(data, struct musb, recover_work.work); int ret; u8 devctl; + /* + * wait at least 320 cycles of 60MHz clock. That's 5.3us, we will give + * it some slack and wait for 10us. + */ + udelay(10); + ret = musb_platform_recover(musb); if (ret) { musb_enable_interrupts(musb); @@ -2098,7 +2102,6 @@ musb_init_controller(struct device *dev, int nIrq, void __iomem *ctrl) /* Init IRQ workqueue before request_irq */ INIT_WORK(&musb->irq_work, musb_irq_work); - INIT_DELAYED_WORK(&musb->recover_work, musb_recover_work); INIT_DELAYED_WORK(&musb->deassert_reset_work, musb_deassert_reset); INIT_DELAYED_WORK(&musb->finish_resume_work, musb_host_finish_resume); @@ -2194,7 +2197,6 @@ fail4: fail3: cancel_work_sync(&musb->irq_work); - cancel_delayed_work_sync(&musb->recover_work); cancel_delayed_work_sync(&musb->finish_resume_work); cancel_delayed_work_sync(&musb->deassert_reset_work); if (musb->dma_controller) @@ -2260,7 +2262,6 @@ static int musb_remove(struct platform_device *pdev) dma_controller_destroy(musb->dma_controller); cancel_work_sync(&musb->irq_work); - cancel_delayed_work_sync(&musb->recover_work); cancel_delayed_work_sync(&musb->finish_resume_work); cancel_delayed_work_sync(&musb->deassert_reset_work); musb_free(musb); diff --git a/drivers/usb/musb/musb_core.h b/drivers/usb/musb/musb_core.h index 1e03c7ec82e4..3877249a8b2d 100644 --- a/drivers/usb/musb/musb_core.h +++ b/drivers/usb/musb/musb_core.h @@ -301,7 +301,6 @@ struct musb { irqreturn_t (*isr)(int, void *); struct work_struct irq_work; - struct delayed_work recover_work; struct delayed_work deassert_reset_work; struct delayed_work finish_resume_work; u16 hwvers; -- cgit v1.2.3 From 06753fe115c517b715616ef7ef4f56b1b46ecc69 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Thu, 26 Feb 2015 14:33:41 -0600 Subject: usb: musb: core: remove unnecessary forward declaration no functional changes, cleanup only. Tested-by: Bin Liu Signed-off-by: Felipe Balbi --- drivers/usb/musb/musb_core.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/usb/musb/musb_core.c b/drivers/usb/musb/musb_core.c index b410af6b0510..ecf2219ebc78 100644 --- a/drivers/usb/musb/musb_core.c +++ b/drivers/usb/musb/musb_core.c @@ -508,7 +508,6 @@ void musb_hnp_stop(struct musb *musb) } static void musb_disable_interrupts(struct musb *musb); -static void musb_generic_disable(struct musb *musb); static void musb_recover_from_babble(struct musb *musb); /* -- cgit v1.2.3 From 0244336f812583299291e18b69a75be5674e819f Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Thu, 26 Feb 2015 14:42:19 -0600 Subject: usb: musb: core: disable irqs inside babble recovery There's no point is splitting those anymore. We're now also able to drop another forward declaration. Tested-by: Bin Liu Signed-off-by: Felipe Balbi --- drivers/usb/musb/musb_core.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/drivers/usb/musb/musb_core.c b/drivers/usb/musb/musb_core.c index ecf2219ebc78..3916e73abf7d 100644 --- a/drivers/usb/musb/musb_core.c +++ b/drivers/usb/musb/musb_core.c @@ -507,7 +507,6 @@ void musb_hnp_stop(struct musb *musb) musb->port1_status &= ~(USB_PORT_STAT_C_CONNECTION << 16); } -static void musb_disable_interrupts(struct musb *musb); static void musb_recover_from_babble(struct musb *musb); /* @@ -883,10 +882,8 @@ b_host: if (power & MUSB_POWER_HSMODE) { dev_err(musb->controller, "Babble\n"); - if (is_host_active(musb)) { - musb_disable_interrupts(musb); + if (is_host_active(musb)) musb_recover_from_babble(musb); - } } } else { dev_dbg(musb->controller, "BUS RESET as %s\n", @@ -1835,6 +1832,8 @@ static void musb_recover_from_babble(struct musb *musb) int ret; u8 devctl; + musb_disable_interrupts(musb); + /* * wait at least 320 cycles of 60MHz clock. That's 5.3us, we will give * it some slack and wait for 10us. -- cgit v1.2.3 From 34754dec8ab83799a0a37f2a7ada8ce1e53d174b Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Thu, 26 Feb 2015 14:43:57 -0600 Subject: usb: musb: core: always try to recover from babble we can also have babble conditions with LS/FS and we also want to recover in that case. Because of that we will drop the check of HSMODE and always try to run babble recovery. Suggested-by: Bin Liu Signed-off-by: Felipe Balbi --- drivers/usb/musb/musb_core.c | 22 ++++++---------------- 1 file changed, 6 insertions(+), 16 deletions(-) diff --git a/drivers/usb/musb/musb_core.c b/drivers/usb/musb/musb_core.c index 3916e73abf7d..a48b5a9c6c47 100644 --- a/drivers/usb/musb/musb_core.c +++ b/drivers/usb/musb/musb_core.c @@ -863,28 +863,18 @@ b_host: if (int_usb & MUSB_INTR_RESET) { handled = IRQ_HANDLED; if (devctl & MUSB_DEVCTL_HM) { - u8 power = musb_readl(musb->mregs, MUSB_POWER); - /* - * Looks like non-HS BABBLE can be ignored, but - * HS BABBLE is an error condition. - * - * For HS the solution is to avoid babble in the first - * place and fix what caused BABBLE. - * - * When HS BABBLE happens what we can depends on which + * When BABBLE happens what we can depends on which * platform MUSB is running, because some platforms * implemented proprietary means for 'recovering' from * Babble conditions. One such platform is AM335x. In - * most cases, however, the only thing we can do is drop - * the session. + * most cases, however, the only thing we can do is + * drop the session. */ - if (power & MUSB_POWER_HSMODE) { - dev_err(musb->controller, "Babble\n"); + dev_err(musb->controller, "Babble\n"); - if (is_host_active(musb)) - musb_recover_from_babble(musb); - } + if (is_host_active(musb)) + musb_recover_from_babble(musb); } else { dev_dbg(musb->controller, "BUS RESET as %s\n", usb_otg_state_string(musb->xceiv->otg->state)); -- cgit v1.2.3 From a285f40d80d440853ac908017d6d949ae2a7f88e Mon Sep 17 00:00:00 2001 From: Mian Yousaf Kaukab Date: Mon, 2 Feb 2015 10:55:23 +0100 Subject: usb: gadget: net2280: use ep_autoconfig compatible names in advance mode Each struct usb_ep added for net2280 can be used in either direction. Whereas, each struct usb_ep for usb3380 has fixed direction. Use ep_autoconf compatible names so that endpoint with correct direction can be selected. Name sequence is due to the logic in usb_reinit_338x() in ne[] and ep_reg_addr[]. Signed-off-by: Mian Yousaf Kaukab Signed-off-by: Felipe Balbi --- drivers/usb/gadget/udc/net2280.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/drivers/usb/gadget/udc/net2280.c b/drivers/usb/gadget/udc/net2280.c index d2c0bf65e345..b7024dcce83a 100644 --- a/drivers/usb/gadget/udc/net2280.c +++ b/drivers/usb/gadget/udc/net2280.c @@ -80,6 +80,13 @@ static const char *const ep_name[] = { "ep-e", "ep-f", "ep-g", "ep-h", }; +/* Endpoint names for usb3380 advance mode */ +static const char *const ep_name_adv[] = { + ep0name, + "ep1in", "ep2out", "ep3in", "ep4out", + "ep1out", "ep2in", "ep3out", "ep4in", +}; + /* mode 0 == ep-{a,b,c,d} 1K fifo each * mode 1 == ep-{a,b} 2K fifo each, ep-{c,d} unavailable * mode 2 == ep-a 2K fifo, ep-{b,c} 1K each, ep-d unavailable @@ -1977,7 +1984,7 @@ static void usb_reinit_338x(struct net2280 *dev) for (i = 0; i < dev->n_ep; i++) { struct net2280_ep *ep = &dev->ep[i]; - ep->ep.name = ep_name[i]; + ep->ep.name = dev->enhanced_mode ? ep_name_adv[i] : ep_name[i]; ep->dev = dev; ep->num = i; -- cgit v1.2.3 From fb2a85dd933c2793f5aabeb84af8b3bc00c0c968 Mon Sep 17 00:00:00 2001 From: Mian Yousaf Kaukab Date: Mon, 2 Feb 2015 10:55:24 +0100 Subject: usb: gadget: net2280: remove fiforegs as it is unused Remove fiforegs from struct net2280 and net2280_ep as it is unused. By the way, ep->fiforegs = &dev->fiforegs[i] assignment is incorrect. It should be ep->fiforegs = &dev->fiforegs[ne[i]], but it doesn't matter now. Signed-off-by: Mian Yousaf Kaukab Signed-off-by: Felipe Balbi --- drivers/usb/gadget/udc/net2280.c | 4 ---- drivers/usb/gadget/udc/net2280.h | 2 -- 2 files changed, 6 deletions(-) diff --git a/drivers/usb/gadget/udc/net2280.c b/drivers/usb/gadget/udc/net2280.c index b7024dcce83a..d51430f26ecf 100644 --- a/drivers/usb/gadget/udc/net2280.c +++ b/drivers/usb/gadget/udc/net2280.c @@ -1996,11 +1996,9 @@ static void usb_reinit_338x(struct net2280 *dev) ep->regs = (struct net2280_ep_regs __iomem *) (((void __iomem *)&dev->epregs[ne[i]]) + ep_reg_addr[i]); - ep->fiforegs = &dev->fiforegs[i]; } else { ep->cfg = &dev->epregs[i]; ep->regs = &dev->epregs[i]; - ep->fiforegs = &dev->fiforegs[i]; } ep->fifo_size = (i != 0) ? 2048 : 512; @@ -3380,8 +3378,6 @@ static int net2280_probe(struct pci_dev *pdev, const struct pci_device_id *id) u32 usbstat; dev->usb_ext = (struct usb338x_usb_ext_regs __iomem *) (base + 0x00b4); - dev->fiforegs = (struct usb338x_fifo_regs __iomem *) - (base + 0x0500); dev->llregs = (struct usb338x_ll_regs __iomem *) (base + 0x0700); dev->ll_lfps_regs = (struct usb338x_ll_lfps_regs __iomem *) diff --git a/drivers/usb/gadget/udc/net2280.h b/drivers/usb/gadget/udc/net2280.h index ac8d5a20a378..4dff60d34f73 100644 --- a/drivers/usb/gadget/udc/net2280.h +++ b/drivers/usb/gadget/udc/net2280.h @@ -96,7 +96,6 @@ struct net2280_ep { struct net2280_ep_regs __iomem *regs; struct net2280_dma_regs __iomem *dma; struct net2280_dma *dummy; - struct usb338x_fifo_regs __iomem *fiforegs; dma_addr_t td_dma; /* of dummy */ struct net2280 *dev; unsigned long irqs; @@ -181,7 +180,6 @@ struct net2280 { struct net2280_dma_regs __iomem *dma; struct net2280_dep_regs __iomem *dep; struct net2280_ep_regs __iomem *epregs; - struct usb338x_fifo_regs __iomem *fiforegs; struct usb338x_ll_regs __iomem *llregs; struct usb338x_ll_lfps_regs __iomem *ll_lfps_regs; struct usb338x_ll_tsn_regs __iomem *ll_tsn_regs; -- cgit v1.2.3 From 9ceafcc2b3ad48ad2ef42608f1505ecad515d144 Mon Sep 17 00:00:00 2001 From: Mian Yousaf Kaukab Date: Mon, 2 Feb 2015 10:55:25 +0100 Subject: usb: gadget: net2280: print error in ep_ops error paths Hopefully, these prints will help localize the problems faster. [ balbi@ti.com: removed 2 unnecessary OOM error messages ] Signed-off-by: Mian Yousaf Kaukab Signed-off-by: Felipe Balbi --- drivers/usb/gadget/udc/net2280.c | 166 ++++++++++++++++++++++++++++----------- 1 file changed, 119 insertions(+), 47 deletions(-) diff --git a/drivers/usb/gadget/udc/net2280.c b/drivers/usb/gadget/udc/net2280.c index d51430f26ecf..5a8f6b6f7c8d 100644 --- a/drivers/usb/gadget/udc/net2280.c +++ b/drivers/usb/gadget/udc/net2280.c @@ -145,31 +145,44 @@ net2280_enable(struct usb_ep *_ep, const struct usb_endpoint_descriptor *desc) u32 max, tmp; unsigned long flags; static const u32 ep_key[9] = { 1, 0, 1, 0, 1, 1, 0, 1, 0 }; + int ret = 0; ep = container_of(_ep, struct net2280_ep, ep); if (!_ep || !desc || ep->desc || _ep->name == ep0name || - desc->bDescriptorType != USB_DT_ENDPOINT) + desc->bDescriptorType != USB_DT_ENDPOINT) { + pr_err("%s: failed at line=%d\n", __func__, __LINE__); return -EINVAL; + } dev = ep->dev; - if (!dev->driver || dev->gadget.speed == USB_SPEED_UNKNOWN) - return -ESHUTDOWN; + if (!dev->driver || dev->gadget.speed == USB_SPEED_UNKNOWN) { + ret = -ESHUTDOWN; + goto print_err; + } /* erratum 0119 workaround ties up an endpoint number */ - if ((desc->bEndpointAddress & 0x0f) == EP_DONTUSE) - return -EDOM; + if ((desc->bEndpointAddress & 0x0f) == EP_DONTUSE) { + ret = -EDOM; + goto print_err; + } if (dev->quirks & PLX_SUPERSPEED) { - if ((desc->bEndpointAddress & 0x0f) >= 0x0c) - return -EDOM; + if ((desc->bEndpointAddress & 0x0f) >= 0x0c) { + ret = -EDOM; + goto print_err; + } ep->is_in = !!usb_endpoint_dir_in(desc); - if (dev->enhanced_mode && ep->is_in && ep_key[ep->num]) - return -EINVAL; + if (dev->enhanced_mode && ep->is_in && ep_key[ep->num]) { + ret = -EINVAL; + goto print_err; + } } /* sanity check ep-e/ep-f since their fifos are small */ max = usb_endpoint_maxp(desc) & 0x1fff; - if (ep->num > 4 && max > 64 && (dev->quirks & PLX_LEGACY)) - return -ERANGE; + if (ep->num > 4 && max > 64 && (dev->quirks & PLX_LEGACY)) { + ret = -ERANGE; + goto print_err; + } spin_lock_irqsave(&dev->lock, flags); _ep->maxpacket = max & 0x7ff; @@ -199,7 +212,8 @@ net2280_enable(struct usb_ep *_ep, const struct usb_endpoint_descriptor *desc) (dev->gadget.speed == USB_SPEED_HIGH && max != 512) || (dev->gadget.speed == USB_SPEED_FULL && max > 64)) { spin_unlock_irqrestore(&dev->lock, flags); - return -ERANGE; + ret = -ERANGE; + goto print_err; } } ep->is_iso = (tmp == USB_ENDPOINT_XFER_ISOC); @@ -278,7 +292,11 @@ net2280_enable(struct usb_ep *_ep, const struct usb_endpoint_descriptor *desc) /* pci writes may still be posted */ spin_unlock_irqrestore(&dev->lock, flags); - return 0; + return ret; + +print_err: + dev_err(&ep->dev->pdev->dev, "%s: error=%d\n", __func__, ret); + return ret; } static int handshake(u32 __iomem *ptr, u32 mask, u32 done, int usec) @@ -433,9 +451,10 @@ static int net2280_disable(struct usb_ep *_ep) unsigned long flags; ep = container_of(_ep, struct net2280_ep, ep); - if (!_ep || !ep->desc || _ep->name == ep0name) + if (!_ep || !ep->desc || _ep->name == ep0name) { + pr_err("%s: Invalid ep=%p or ep->desc\n", __func__, _ep); return -EINVAL; - + } spin_lock_irqsave(&ep->dev->lock, flags); nuke(ep); @@ -465,8 +484,10 @@ static struct usb_request struct net2280_ep *ep; struct net2280_request *req; - if (!_ep) + if (!_ep) { + pr_err("%s: Invalid ep\n", __func__); return NULL; + } ep = container_of(_ep, struct net2280_ep, ep); req = kzalloc(sizeof(*req), gfp_flags); @@ -498,8 +519,11 @@ static void net2280_free_request(struct usb_ep *_ep, struct usb_request *_req) struct net2280_request *req; ep = container_of(_ep, struct net2280_ep, ep); - if (!_ep || !_req) + if (!_ep || !_req) { + dev_err(&ep->dev->pdev->dev, "%s: Inavlid ep=%p or req=%p\n", + __func__, _ep, _req); return; + } req = container_of(_req, struct net2280_request, req); WARN_ON(!list_empty(&req->queue)); @@ -903,35 +927,44 @@ net2280_queue(struct usb_ep *_ep, struct usb_request *_req, gfp_t gfp_flags) struct net2280_ep *ep; struct net2280 *dev; unsigned long flags; + int ret = 0; /* we always require a cpu-view buffer, so that we can * always use pio (as fallback or whatever). */ - req = container_of(_req, struct net2280_request, req); - if (!_req || !_req->complete || !_req->buf || - !list_empty(&req->queue)) - return -EINVAL; - if (_req->length > (~0 & DMA_BYTE_COUNT_MASK)) - return -EDOM; ep = container_of(_ep, struct net2280_ep, ep); - if (!_ep || (!ep->desc && ep->num != 0)) + if (!_ep || (!ep->desc && ep->num != 0)) { + pr_err("%s: Invalid ep=%p or ep->desc\n", __func__, _ep); return -EINVAL; + } + req = container_of(_req, struct net2280_request, req); + if (!_req || !_req->complete || !_req->buf || + !list_empty(&req->queue)) { + ret = -EINVAL; + goto print_err; + } + if (_req->length > (~0 & DMA_BYTE_COUNT_MASK)) { + ret = -EDOM; + goto print_err; + } dev = ep->dev; - if (!dev->driver || dev->gadget.speed == USB_SPEED_UNKNOWN) - return -ESHUTDOWN; + if (!dev->driver || dev->gadget.speed == USB_SPEED_UNKNOWN) { + ret = -ESHUTDOWN; + goto print_err; + } /* FIXME implement PIO fallback for ZLPs with DMA */ - if (ep->dma && _req->length == 0) - return -EOPNOTSUPP; + if (ep->dma && _req->length == 0) { + ret = -EOPNOTSUPP; + goto print_err; + } /* set up dma mapping in case the caller didn't */ if (ep->dma) { - int ret; - ret = usb_gadget_map_request(&dev->gadget, _req, ep->is_in); if (ret) - return ret; + goto print_err; } ep_vdbg(dev, "%s queue req %p, len %d buf %p\n", @@ -1020,7 +1053,11 @@ done: spin_unlock_irqrestore(&dev->lock, flags); /* pci writes may still be posted */ - return 0; + return ret; + +print_err: + dev_err(&ep->dev->pdev->dev, "%s: error=%d\n", __func__, ret); + return ret; } static inline void @@ -1141,8 +1178,11 @@ static int net2280_dequeue(struct usb_ep *_ep, struct usb_request *_req) int stopped; ep = container_of(_ep, struct net2280_ep, ep); - if (!_ep || (!ep->desc && ep->num != 0) || !_req) + if (!_ep || (!ep->desc && ep->num != 0) || !_req) { + pr_err("%s: Invalid ep=%p or ep->desc or req=%p\n", + __func__, _ep, _req); return -EINVAL; + } spin_lock_irqsave(&ep->dev->lock, flags); stopped = ep->stopped; @@ -1164,6 +1204,8 @@ static int net2280_dequeue(struct usb_ep *_ep, struct usb_request *_req) } if (&req->req != _req) { spin_unlock_irqrestore(&ep->dev->lock, flags); + dev_err(&ep->dev->pdev->dev, "%s: Request mismatch\n", + __func__); return -EINVAL; } @@ -1221,20 +1263,28 @@ net2280_set_halt_and_wedge(struct usb_ep *_ep, int value, int wedged) int retval = 0; ep = container_of(_ep, struct net2280_ep, ep); - if (!_ep || (!ep->desc && ep->num != 0)) + if (!_ep || (!ep->desc && ep->num != 0)) { + pr_err("%s: Invalid ep=%p or ep->desc\n", __func__, _ep); return -EINVAL; - if (!ep->dev->driver || ep->dev->gadget.speed == USB_SPEED_UNKNOWN) - return -ESHUTDOWN; + } + if (!ep->dev->driver || ep->dev->gadget.speed == USB_SPEED_UNKNOWN) { + retval = -ESHUTDOWN; + goto print_err; + } if (ep->desc /* not ep0 */ && (ep->desc->bmAttributes & 0x03) - == USB_ENDPOINT_XFER_ISOC) - return -EINVAL; + == USB_ENDPOINT_XFER_ISOC) { + retval = -EINVAL; + goto print_err; + } spin_lock_irqsave(&ep->dev->lock, flags); - if (!list_empty(&ep->queue)) + if (!list_empty(&ep->queue)) { retval = -EAGAIN; - else if (ep->is_in && value && net2280_fifo_status(_ep) != 0) + goto print_unlock; + } else if (ep->is_in && value && net2280_fifo_status(_ep) != 0) { retval = -EAGAIN; - else { + goto print_unlock; + } else { ep_vdbg(ep->dev, "%s %s %s\n", _ep->name, value ? "set" : "clear", wedged ? "wedge" : "halt"); @@ -1258,6 +1308,12 @@ net2280_set_halt_and_wedge(struct usb_ep *_ep, int value, int wedged) spin_unlock_irqrestore(&ep->dev->lock, flags); return retval; + +print_unlock: + spin_unlock_irqrestore(&ep->dev->lock, flags); +print_err: + dev_err(&ep->dev->pdev->dev, "%s: error=%d\n", __func__, retval); + return retval; } static int net2280_set_halt(struct usb_ep *_ep, int value) @@ -1267,8 +1323,10 @@ static int net2280_set_halt(struct usb_ep *_ep, int value) static int net2280_set_wedge(struct usb_ep *_ep) { - if (!_ep || _ep->name == ep0name) + if (!_ep || _ep->name == ep0name) { + pr_err("%s: Invalid ep=%p or ep0\n", __func__, _ep); return -EINVAL; + } return net2280_set_halt_and_wedge(_ep, 1, 1); } @@ -1278,14 +1336,22 @@ static int net2280_fifo_status(struct usb_ep *_ep) u32 avail; ep = container_of(_ep, struct net2280_ep, ep); - if (!_ep || (!ep->desc && ep->num != 0)) + if (!_ep || (!ep->desc && ep->num != 0)) { + pr_err("%s: Invalid ep=%p or ep->desc\n", __func__, _ep); return -ENODEV; - if (!ep->dev->driver || ep->dev->gadget.speed == USB_SPEED_UNKNOWN) + } + if (!ep->dev->driver || ep->dev->gadget.speed == USB_SPEED_UNKNOWN) { + dev_err(&ep->dev->pdev->dev, + "%s: Invalid driver=%p or speed=%d\n", + __func__, ep->dev->driver, ep->dev->gadget.speed); return -ESHUTDOWN; + } avail = readl(&ep->regs->ep_avail) & (BIT(12) - 1); - if (avail > ep->fifo_size) + if (avail > ep->fifo_size) { + dev_err(&ep->dev->pdev->dev, "%s: Fifo overflow\n", __func__); return -EOVERFLOW; + } if (ep->is_in) avail = ep->fifo_size - avail; return avail; @@ -1296,10 +1362,16 @@ static void net2280_fifo_flush(struct usb_ep *_ep) struct net2280_ep *ep; ep = container_of(_ep, struct net2280_ep, ep); - if (!_ep || (!ep->desc && ep->num != 0)) + if (!_ep || (!ep->desc && ep->num != 0)) { + pr_err("%s: Invalid ep=%p or ep->desc\n", __func__, _ep); return; - if (!ep->dev->driver || ep->dev->gadget.speed == USB_SPEED_UNKNOWN) + } + if (!ep->dev->driver || ep->dev->gadget.speed == USB_SPEED_UNKNOWN) { + dev_err(&ep->dev->pdev->dev, + "%s: Invalid driver=%p or speed=%d\n", + __func__, ep->dev->driver, ep->dev->gadget.speed); return; + } writel(BIT(FIFO_FLUSH), &ep->regs->ep_stat); (void) readl(&ep->regs->ep_rsp); -- cgit v1.2.3 From 12366ef1942128d9bd8baa77e7eb4010b18fd676 Mon Sep 17 00:00:00 2001 From: Mian Yousaf Kaukab Date: Mon, 2 Feb 2015 10:55:26 +0100 Subject: usb: gadget: net2280: don't connect from udc_start net2280_start can be called with pullup disabled. Don't set softconnect flag in it. Let net2280_pullup handle the connection part. Signed-off-by: Mian Yousaf Kaukab Signed-off-by: Felipe Balbi --- drivers/usb/gadget/udc/net2280.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/usb/gadget/udc/net2280.c b/drivers/usb/gadget/udc/net2280.c index 5a8f6b6f7c8d..5041e218a302 100644 --- a/drivers/usb/gadget/udc/net2280.c +++ b/drivers/usb/gadget/udc/net2280.c @@ -2263,7 +2263,6 @@ static int net2280_start(struct usb_gadget *_gadget, dev->ep[i].irqs = 0; /* hook up the driver ... */ - dev->softconnect = 1; driver->driver.bus = NULL; dev->driver = driver; -- cgit v1.2.3 From ccf5fb698155ee289b9257b0f1d6be3c7900ba0a Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Fri, 27 Feb 2015 09:44:51 -0600 Subject: usb: gadget: net2280: silence sparse warning Silence the following warning: drivers/usb/gadget/udc/net2280.c:3176:33: warning: context imbalance in 'handle_stat1_irqs' - unexpected unlock Signed-off-by: Felipe Balbi --- drivers/usb/gadget/udc/net2280.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/usb/gadget/udc/net2280.c b/drivers/usb/gadget/udc/net2280.c index 5041e218a302..9871b90195ad 100644 --- a/drivers/usb/gadget/udc/net2280.c +++ b/drivers/usb/gadget/udc/net2280.c @@ -3128,6 +3128,8 @@ next_endpoints: BIT(PCI_RETRY_ABORT_INTERRUPT)) static void handle_stat1_irqs(struct net2280 *dev, u32 stat) +__releases(dev->lock) +__acquires(dev->lock) { struct net2280_ep *ep; u32 tmp, num, mask, scratch; -- cgit v1.2.3 From 1b61625f8b5d87caf9633d7dbfbaf1ea8270036d Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Fri, 27 Feb 2015 13:19:39 -0600 Subject: usb: musb: cppi41: decrease indentation level no functional changes, clean up only. Signed-off-by: Felipe Balbi --- drivers/usb/musb/musb_cppi41.c | 88 +++++++++++++++++++++--------------------- 1 file changed, 45 insertions(+), 43 deletions(-) diff --git a/drivers/usb/musb/musb_cppi41.c b/drivers/usb/musb/musb_cppi41.c index be84562d021b..73ac9835485d 100644 --- a/drivers/usb/musb/musb_cppi41.c +++ b/drivers/usb/musb/musb_cppi41.c @@ -225,10 +225,12 @@ static void cppi41_dma_callback(void *private_data) struct dma_channel *channel = private_data; struct cppi41_dma_channel *cppi41_channel = channel->private_data; struct musb_hw_ep *hw_ep = cppi41_channel->hw_ep; + struct cppi41_dma_controller *controller; struct musb *musb = hw_ep->musb; unsigned long flags; struct dma_tx_state txstate; u32 transferred; + int is_hs = 0; bool empty; spin_lock_irqsave(&musb->lock, flags); @@ -251,58 +253,58 @@ static void cppi41_dma_callback(void *private_data) empty = musb_is_tx_fifo_empty(hw_ep); if (empty) { cppi41_trans_done(cppi41_channel); - } else { - struct cppi41_dma_controller *controller; - int is_hs = 0; - /* - * On AM335x it has been observed that the TX interrupt fires - * too early that means the TXFIFO is not yet empty but the DMA - * engine says that it is done with the transfer. We don't - * receive a FIFO empty interrupt so the only thing we can do is - * to poll for the bit. On HS it usually takes 2us, on FS around - * 110us - 150us depending on the transfer size. - * We spin on HS (no longer than than 25us and setup a timer on - * FS to check for the bit and complete the transfer. - */ - controller = cppi41_channel->controller; + goto out; + } - if (is_host_active(musb)) { - if (musb->port1_status & USB_PORT_STAT_HIGH_SPEED) - is_hs = 1; - } else { - if (musb->g.speed == USB_SPEED_HIGH) - is_hs = 1; - } - if (is_hs) { - unsigned wait = 25; - - do { - empty = musb_is_tx_fifo_empty(hw_ep); - if (empty) - break; - wait--; - if (!wait) - break; - udelay(1); - } while (1); + /* + * On AM335x it has been observed that the TX interrupt fires + * too early that means the TXFIFO is not yet empty but the DMA + * engine says that it is done with the transfer. We don't + * receive a FIFO empty interrupt so the only thing we can do is + * to poll for the bit. On HS it usually takes 2us, on FS around + * 110us - 150us depending on the transfer size. + * We spin on HS (no longer than than 25us and setup a timer on + * FS to check for the bit and complete the transfer. + */ + controller = cppi41_channel->controller; + + if (is_host_active(musb)) { + if (musb->port1_status & USB_PORT_STAT_HIGH_SPEED) + is_hs = 1; + } else { + if (musb->g.speed == USB_SPEED_HIGH) + is_hs = 1; + } + if (is_hs) { + unsigned wait = 25; + do { empty = musb_is_tx_fifo_empty(hw_ep); - if (empty) { - cppi41_trans_done(cppi41_channel); - goto out; - } + if (empty) + break; + wait--; + if (!wait) + break; + udelay(1); + } while (1); + + empty = musb_is_tx_fifo_empty(hw_ep); + if (empty) { + cppi41_trans_done(cppi41_channel); + goto out; } - list_add_tail(&cppi41_channel->tx_check, - &controller->early_tx_list); - if (!hrtimer_is_queued(&controller->early_tx)) { - unsigned long usecs = cppi41_channel->total_len / 10; + } + list_add_tail(&cppi41_channel->tx_check, + &controller->early_tx_list); + if (!hrtimer_is_queued(&controller->early_tx)) { + unsigned long usecs = cppi41_channel->total_len / 10; - hrtimer_start_range_ns(&controller->early_tx, + hrtimer_start_range_ns(&controller->early_tx, ktime_set(0, usecs * NSEC_PER_USEC), 20 * NSEC_PER_USEC, HRTIMER_MODE_REL); - } } + out: spin_unlock_irqrestore(&musb->lock, flags); } -- cgit v1.2.3 From af63429cf046210d7313805b579dd779d10ad1c0 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Fri, 27 Feb 2015 13:21:14 -0600 Subject: usb: musb: cppi41: exit early when tx fifo is empty as soon as we find out tx fifo is empty, there's no need to break out of the loop just to have another branch to complete the transfer. We can just complete transfer and exit early. Signed-off-by: Felipe Balbi --- drivers/usb/musb/musb_cppi41.c | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/drivers/usb/musb/musb_cppi41.c b/drivers/usb/musb/musb_cppi41.c index 73ac9835485d..4407f30d0b86 100644 --- a/drivers/usb/musb/musb_cppi41.c +++ b/drivers/usb/musb/musb_cppi41.c @@ -280,19 +280,15 @@ static void cppi41_dma_callback(void *private_data) do { empty = musb_is_tx_fifo_empty(hw_ep); - if (empty) - break; + if (empty) { + cppi41_trans_done(cppi41_channel); + goto out; + } wait--; if (!wait) break; udelay(1); } while (1); - - empty = musb_is_tx_fifo_empty(hw_ep); - if (empty) { - cppi41_trans_done(cppi41_channel); - goto out; - } } list_add_tail(&cppi41_channel->tx_check, &controller->early_tx_list); -- cgit v1.2.3 From 043f5b75dd2b1fbd45d5f367d50e5ae5b4afa955 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Fri, 27 Feb 2015 13:22:27 -0600 Subject: usb: musb: cppi41: do not call udelay() according to comment in code, HS completion will happen pretty fast, instead of using udelay(), let's just busy loop and drop a cpu_relax() where udelay() was. Signed-off-by: Felipe Balbi --- drivers/usb/musb/musb_cppi41.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/usb/musb/musb_cppi41.c b/drivers/usb/musb/musb_cppi41.c index 4407f30d0b86..9dc45a4a9fa8 100644 --- a/drivers/usb/musb/musb_cppi41.c +++ b/drivers/usb/musb/musb_cppi41.c @@ -287,7 +287,7 @@ static void cppi41_dma_callback(void *private_data) wait--; if (!wait) break; - udelay(1); + cpu_relax(); } while (1); } list_add_tail(&cppi41_channel->tx_check, -- cgit v1.2.3 From 9e204d885a6d0ae3696284bacd86e2b94dd936c8 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Fri, 27 Feb 2015 19:02:41 -0600 Subject: usb: musb: dsps: use msecs_to_jiffies instead when polling, we were using n * HZ (where n is an integer in seconds), however HZ isn't always correct if we're using cpufreq. A better way is to use msecs_to_jiffies(n) (where n is now an integer in miliseconds). while at that, also rename poll_seconds to poll_timeout and change its type to unsigned int. Signed-off-by: Felipe Balbi --- drivers/usb/musb/musb_dsps.c | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/drivers/usb/musb/musb_dsps.c b/drivers/usb/musb/musb_dsps.c index 30eb6ac29b81..9271450ebacd 100644 --- a/drivers/usb/musb/musb_dsps.c +++ b/drivers/usb/musb/musb_dsps.c @@ -119,7 +119,7 @@ struct dsps_musb_wrapper { unsigned iddig:5; unsigned iddig_mux:5; /* miscellaneous stuff */ - u8 poll_seconds; + unsigned poll_timeout; }; /* @@ -285,7 +285,8 @@ static void otg_timer(unsigned long _musb) } if (!(devctl & MUSB_DEVCTL_SESSION) && !skip_session) dsps_writeb(mregs, MUSB_DEVCTL, MUSB_DEVCTL_SESSION); - mod_timer(&glue->timer, jiffies + wrp->poll_seconds * HZ); + mod_timer(&glue->timer, jiffies + + msecs_to_jiffies(wrp->poll_timeout)); break; case OTG_STATE_A_WAIT_VFALL: musb->xceiv->otg->state = OTG_STATE_A_WAIT_VRISE; @@ -352,8 +353,8 @@ static irqreturn_t dsps_interrupt(int irq, void *hci) */ musb->int_usb &= ~MUSB_INTR_VBUSERROR; musb->xceiv->otg->state = OTG_STATE_A_WAIT_VFALL; - mod_timer(&glue->timer, - jiffies + wrp->poll_seconds * HZ); + mod_timer(&glue->timer, jiffies + + msecs_to_jiffies(wrp->poll_timeout)); WARNING("VBUS error workaround (delay coming)\n"); } else if (drvvbus) { MUSB_HST_MODE(musb); @@ -382,7 +383,8 @@ static irqreturn_t dsps_interrupt(int irq, void *hci) /* Poll for ID change in OTG port mode */ if (musb->xceiv->otg->state == OTG_STATE_B_IDLE && musb->port_mode == MUSB_PORT_MODE_DUAL_ROLE) - mod_timer(&glue->timer, jiffies + wrp->poll_seconds * HZ); + mod_timer(&glue->timer, jiffies + + msecs_to_jiffies(wrp->poll_timeout)); out: spin_unlock_irqrestore(&musb->lock, flags); @@ -832,7 +834,7 @@ static const struct dsps_musb_wrapper am33xx_driver_data = { .rxep_shift = 16, .rxep_mask = 0xfffe, .rxep_bitmap = (0xfffe << 16), - .poll_seconds = 2, + .poll_timeout = 2000, /* ms */ }; static const struct of_device_id musb_dsps_of_match[] = { @@ -888,7 +890,8 @@ static int dsps_resume(struct device *dev) dsps_writel(mbase, wrp->rx_mode, glue->context.rx_mode); if (musb->xceiv->otg->state == OTG_STATE_B_IDLE && musb->port_mode == MUSB_PORT_MODE_DUAL_ROLE) - mod_timer(&glue->timer, jiffies + wrp->poll_seconds * HZ); + mod_timer(&glue->timer, jiffies + + msecs_to_jiffies(wrp->poll_timeout)); return 0; } -- cgit v1.2.3 From ad78c918602cb7cce0fab5d5813213853a6f351d Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Fri, 27 Feb 2015 19:07:49 -0600 Subject: usb: musb: dsps: just start polling already there's no need to fake an IRQ, just check if VBUS is valid already. Signed-off-by: Felipe Balbi --- drivers/usb/musb/musb_dsps.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/drivers/usb/musb/musb_dsps.c b/drivers/usb/musb/musb_dsps.c index 9271450ebacd..baa757ba1353 100644 --- a/drivers/usb/musb/musb_dsps.c +++ b/drivers/usb/musb/musb_dsps.c @@ -225,9 +225,8 @@ static void dsps_musb_enable(struct musb *musb) dsps_writel(reg_base, wrp->epintr_set, epmask); dsps_writel(reg_base, wrp->coreintr_set, coremask); - /* Force the DRVVBUS IRQ so we can start polling for ID change. */ - dsps_writel(reg_base, wrp->coreintr_set, - (1 << wrp->drvvbus) << wrp->usb_shift); + /* start polling for ID change. */ + mod_timer(&glue->timer, jiffies + msecs_to_jiffies(wrp->poll_timeout)); dsps_musb_try_idle(musb, 0); } -- cgit v1.2.3 From eac68e8f979b82d257eea0a4bbcda7b169d330bf Mon Sep 17 00:00:00 2001 From: Robert Baldyga Date: Mon, 9 Mar 2015 15:06:12 +0100 Subject: usb: dwc3: make LPM configurable in DT This patch removes "Enable USB3 LPM Capability" option from Kconfig and adds snps,usb3_lpm_capable devicetree property instead of it. USB3 LPM (Link Power Management) capability is hardware property, and it's platform dependent, so if our hardware supports this feature, we want rather to configure it in devicetree than having it as Kconfig option. Signed-off-by: Robert Baldyga Signed-off-by: Felipe Balbi --- Documentation/devicetree/bindings/usb/dwc3.txt | 1 + drivers/usb/dwc3/Kconfig | 7 ------- drivers/usb/dwc3/core.c | 3 +++ drivers/usb/dwc3/core.h | 2 ++ drivers/usb/dwc3/host.c | 4 +--- drivers/usb/dwc3/platform_data.h | 1 + 6 files changed, 8 insertions(+), 10 deletions(-) diff --git a/Documentation/devicetree/bindings/usb/dwc3.txt b/Documentation/devicetree/bindings/usb/dwc3.txt index cd7f0454e13a..5cc364309edb 100644 --- a/Documentation/devicetree/bindings/usb/dwc3.txt +++ b/Documentation/devicetree/bindings/usb/dwc3.txt @@ -14,6 +14,7 @@ Optional properties: - phys: from the *Generic PHY* bindings - phy-names: from the *Generic PHY* bindings - tx-fifo-resize: determines if the FIFO *has* to be reallocated. + - snps,usb3_lpm_capable: determines if platform is USB3 LPM capable - snps,disable_scramble_quirk: true when SW should disable data scrambling. Only really useful for FPGA builds. - snps,has-lpm-erratum: true when DWC3 was configured with LPM Erratum enabled diff --git a/drivers/usb/dwc3/Kconfig b/drivers/usb/dwc3/Kconfig index edbf9c85af7e..827c4f80379f 100644 --- a/drivers/usb/dwc3/Kconfig +++ b/drivers/usb/dwc3/Kconfig @@ -104,11 +104,4 @@ config USB_DWC3_DEBUG help Say Y here to enable debugging messages on DWC3 Driver. -config DWC3_HOST_USB3_LPM_ENABLE - bool "Enable USB3 LPM Capability" - depends on USB_DWC3_HOST=y || USB_DWC3_DUAL_ROLE=y - default n - help - Select this when you want to enable USB3 LPM with dwc3 xhci host. - endif diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c index cd59e919e27e..2bbab3d86fff 100644 --- a/drivers/usb/dwc3/core.c +++ b/drivers/usb/dwc3/core.c @@ -804,6 +804,8 @@ static int dwc3_probe(struct platform_device *pdev) "snps,is-utmi-l1-suspend"); of_property_read_u8(node, "snps,hird-threshold", &hird_threshold); + dwc->usb3_lpm_capable = of_property_read_bool(node, + "snps,usb3_lpm_capable"); dwc->needs_fifo_resize = of_property_read_bool(node, "tx-fifo-resize"); @@ -844,6 +846,7 @@ static int dwc3_probe(struct platform_device *pdev) hird_threshold = pdata->hird_threshold; dwc->needs_fifo_resize = pdata->tx_fifo_resize; + dwc->usb3_lpm_capable = pdata->usb3_lpm_capable; dwc->dr_mode = pdata->dr_mode; dwc->disable_scramble_quirk = pdata->disable_scramble_quirk; diff --git a/drivers/usb/dwc3/core.h b/drivers/usb/dwc3/core.h index d201910b892f..fdab715a0631 100644 --- a/drivers/usb/dwc3/core.h +++ b/drivers/usb/dwc3/core.h @@ -689,6 +689,7 @@ struct dwc3_scratchpad_array { * @setup_packet_pending: true when there's a Setup Packet in FIFO. Workaround * @start_config_issued: true when StartConfig command has been issued * @three_stage_setup: set if we perform a three phase setup + * @usb3_lpm_capable: set if hadrware supports Link Power Management * @disable_scramble_quirk: set if we enable the disable scramble quirk * @u2exit_lfps_quirk: set if we enable u2exit lfps quirk * @u2ss_inp3_quirk: set if we enable P3 OK for U2/SS Inactive quirk @@ -812,6 +813,7 @@ struct dwc3 { unsigned setup_packet_pending:1; unsigned start_config_issued:1; unsigned three_stage_setup:1; + unsigned usb3_lpm_capable:1; unsigned disable_scramble_quirk:1; unsigned u2exit_lfps_quirk:1; diff --git a/drivers/usb/dwc3/host.c b/drivers/usb/dwc3/host.c index 12bfd3c5405e..c679f63783ae 100644 --- a/drivers/usb/dwc3/host.c +++ b/drivers/usb/dwc3/host.c @@ -49,9 +49,7 @@ int dwc3_host_init(struct dwc3 *dwc) memset(&pdata, 0, sizeof(pdata)); -#ifdef CONFIG_DWC3_HOST_USB3_LPM_ENABLE - pdata.usb3_lpm_capable = 1; -#endif + pdata.usb3_lpm_capable = dwc->usb3_lpm_capable; ret = platform_device_add_data(xhci, &pdata, sizeof(pdata)); if (ret) { diff --git a/drivers/usb/dwc3/platform_data.h b/drivers/usb/dwc3/platform_data.h index a3a3b6d5668c..a2bd464be828 100644 --- a/drivers/usb/dwc3/platform_data.h +++ b/drivers/usb/dwc3/platform_data.h @@ -24,6 +24,7 @@ struct dwc3_platform_data { enum usb_device_speed maximum_speed; enum usb_dr_mode dr_mode; bool tx_fifo_resize; + bool usb3_lpm_capable; unsigned is_utmi_l1_suspend:1; u8 hird_threshold; -- cgit v1.2.3 From 232c0102e84b7fce634c8902a5fa30ca2b3342ac Mon Sep 17 00:00:00 2001 From: Andrzej Pietrasiewicz Date: Tue, 3 Mar 2015 10:52:04 +0100 Subject: usb: gadget: composite: don't try standard handling for non-standard requests If a non-standard request is processed and its parameters just happen to match those of some standard request, the logic of composite_setup() can be fooled, so don't even try any switch cases, just go to the proper place where unknown requests are handled. Signed-off-by: Andrzej Pietrasiewicz Signed-off-by: Felipe Balbi --- drivers/usb/gadget/composite.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/drivers/usb/gadget/composite.c b/drivers/usb/gadget/composite.c index 13adfd1a3f54..9fb92310fb2b 100644 --- a/drivers/usb/gadget/composite.c +++ b/drivers/usb/gadget/composite.c @@ -1472,6 +1472,13 @@ composite_setup(struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl) req->length = 0; gadget->ep0->driver_data = cdev; + /* + * Don't let non-standard requests match any of the cases below + * by accident. + */ + if ((ctrl->bRequestType & USB_TYPE_MASK) != USB_TYPE_STANDARD) + goto unknown; + switch (ctrl->bRequest) { /* we handle all standard USB descriptors */ -- cgit v1.2.3 From eb132ccbdec5df46e29c9814adf76075ce83576b Mon Sep 17 00:00:00 2001 From: Andrzej Pietrasiewicz Date: Tue, 3 Mar 2015 10:52:05 +0100 Subject: usb: gadget: printer: enqueue printer's response for setup request Function-specific setup requests should be handled in such a way, that apart from filling in the data buffer, the requests are also actually enqueued: if function-specific setup is called from composte_setup(), the "usb_ep_queue()" block of code in composite_setup() is skipped. The printer function lacks this part and it results in e.g. get device id requests failing: the host expects some response, the device prepares it but does not equeue it for sending to the host, so the host finally asserts timeout. This patch adds enqueueing the prepared responses. Cc: # v3.4+ Fixes: 2e87edf49227: "usb: gadget: make g_printer use composite" Signed-off-by: Andrzej Pietrasiewicz Signed-off-by: Felipe Balbi --- drivers/usb/gadget/legacy/printer.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/drivers/usb/gadget/legacy/printer.c b/drivers/usb/gadget/legacy/printer.c index 90545980542f..6385c198c134 100644 --- a/drivers/usb/gadget/legacy/printer.c +++ b/drivers/usb/gadget/legacy/printer.c @@ -1031,6 +1031,15 @@ unknown: break; } /* host either stalls (value < 0) or reports success */ + if (value >= 0) { + req->length = value; + req->zero = value < wLength; + value = usb_ep_queue(cdev->gadget->ep0, req, GFP_ATOMIC); + if (value < 0) { + ERROR(dev, "%s:%d Error!\n", __func__, __LINE__); + req->status = 0; + } + } return value; } -- cgit v1.2.3 From 050f571264154b2f5b4c3c4c1581ab365064ff28 Mon Sep 17 00:00:00 2001 From: Andrzej Pietrasiewicz Date: Tue, 3 Mar 2015 10:52:06 +0100 Subject: usb: gadget: printer: remove unused and empty printer_unbind The unbind() method is optional is usb_composite_driver. In this particular driver the method does nothing so it can be removed. Signed-off-by: Andrzej Pietrasiewicz Signed-off-by: Felipe Balbi --- drivers/usb/gadget/legacy/printer.c | 6 ------ 1 file changed, 6 deletions(-) diff --git a/drivers/usb/gadget/legacy/printer.c b/drivers/usb/gadget/legacy/printer.c index 6385c198c134..21ea317d2a43 100644 --- a/drivers/usb/gadget/legacy/printer.c +++ b/drivers/usb/gadget/legacy/printer.c @@ -1288,11 +1288,6 @@ fail: return status; } -static int printer_unbind(struct usb_composite_dev *cdev) -{ - return 0; -} - static int __init printer_bind(struct usb_composite_dev *cdev) { int ret; @@ -1317,7 +1312,6 @@ static __refdata struct usb_composite_driver printer_driver = { .strings = dev_strings, .max_speed = USB_SPEED_SUPER, .bind = printer_bind, - .unbind = printer_unbind, }; static int __init -- cgit v1.2.3 From c69b8186945c10d245586e9f9703486e9574170c Mon Sep 17 00:00:00 2001 From: Andrzej Pietrasiewicz Date: Tue, 3 Mar 2015 10:52:07 +0100 Subject: usb: gadget: printer: eliminate random pointer dereference struct printer_dev contains 3 list heads: tx_reqs, rx_reqs and rx_buffers. There is just one instance of this structure in the driver and it is file static, and as such initialized with all zeros. If device_create() or cdev_add() fails then "goto fail" branch is taken, which results in printer_cfg_unbind() call. The latter checks if tx_reqs, rx_reqs and rx_buffers lists are empty. The check for emptiness is in fact a check whether the "next" member of struct list_head points to the head of the list. But the heads of the lists in question have not been initialized yet and, as mentioned above, contain all zeros, so list_empty() returns false and respective "while" loop body starts executing. Here, container_of() just subtracts the offset of a struct usb_request member from an address of this same member, which results in a value somewhere near 0 or 0xfff...ff. And the argument to list_del() dereferences such a pointer which causes a disaster. This patch moves respective INIT_LIST_HEAD() invocations to a point before "goto fail" branch can be taken. Signed-off-by: Andrzej Pietrasiewicz Signed-off-by: Felipe Balbi --- drivers/usb/gadget/legacy/printer.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/usb/gadget/legacy/printer.c b/drivers/usb/gadget/legacy/printer.c index 21ea317d2a43..12247d3fe768 100644 --- a/drivers/usb/gadget/legacy/printer.c +++ b/drivers/usb/gadget/legacy/printer.c @@ -1190,6 +1190,9 @@ static int __init printer_bind_config(struct usb_configuration *c) dev->function.unbind = printer_func_unbind; dev->function.set_alt = printer_func_set_alt; dev->function.disable = printer_func_disable; + INIT_LIST_HEAD(&dev->tx_reqs); + INIT_LIST_HEAD(&dev->rx_reqs); + INIT_LIST_HEAD(&dev->rx_buffers); status = usb_add_function(c, &dev->function); if (status) @@ -1233,11 +1236,8 @@ static int __init printer_bind_config(struct usb_configuration *c) spin_lock_init(&dev->lock); mutex_init(&dev->lock_printer_io); - INIT_LIST_HEAD(&dev->tx_reqs); INIT_LIST_HEAD(&dev->tx_reqs_active); - INIT_LIST_HEAD(&dev->rx_reqs); INIT_LIST_HEAD(&dev->rx_reqs_active); - INIT_LIST_HEAD(&dev->rx_buffers); init_waitqueue_head(&dev->rx_wait); init_waitqueue_head(&dev->tx_wait); init_waitqueue_head(&dev->tx_flush_wait); -- cgit v1.2.3 From f5bda0034fba942adf5555246e248ddb66c76052 Mon Sep 17 00:00:00 2001 From: Andrzej Pietrasiewicz Date: Tue, 3 Mar 2015 10:52:08 +0100 Subject: usb: gadget: printer: revert usb_add_function() effect in error recovery Whenever the "goto fail" branch is taken, the effect of usb_add_function() should be reverted. Signed-off-by: Andrzej Pietrasiewicz Signed-off-by: Felipe Balbi --- drivers/usb/gadget/legacy/printer.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/usb/gadget/legacy/printer.c b/drivers/usb/gadget/legacy/printer.c index 12247d3fe768..eb02a6b8da08 100644 --- a/drivers/usb/gadget/legacy/printer.c +++ b/drivers/usb/gadget/legacy/printer.c @@ -1285,6 +1285,7 @@ static int __init printer_bind_config(struct usb_configuration *c) fail: printer_cfg_unbind(c); + usb_remove_function(c, &dev->function); return status; } -- cgit v1.2.3 From 44b316525986252bb95d356419fc9e75f0532112 Mon Sep 17 00:00:00 2001 From: Andrzej Pietrasiewicz Date: Tue, 3 Mar 2015 10:52:09 +0100 Subject: usb: gadget: printer: add missing error handling If cdev_add() in printer_bind_config() fails, care is taken to reverse the effects of initializations completed until the fail happens. But if printer_req_alloc() fails, it is just one of the two lists that is cleaned up while the effects of cdev_add() and device_create() are not reverted. This patch changes error handling so that at least as much cleanup is done as when a failure happens before printer_req_alloc() invocations. Signed-off-by: Andrzej Pietrasiewicz Signed-off-by: Felipe Balbi --- drivers/usb/gadget/legacy/printer.c | 23 +++++------------------ 1 file changed, 5 insertions(+), 18 deletions(-) diff --git a/drivers/usb/gadget/legacy/printer.c b/drivers/usb/gadget/legacy/printer.c index eb02a6b8da08..bbcd6aa9abd1 100644 --- a/drivers/usb/gadget/legacy/printer.c +++ b/drivers/usb/gadget/legacy/printer.c @@ -1249,31 +1249,18 @@ static int __init printer_bind_config(struct usb_configuration *c) dev->current_rx_bytes = 0; dev->current_rx_buf = NULL; + status = -ENOMEM; for (i = 0; i < QLEN; i++) { req = printer_req_alloc(dev->in_ep, USB_BUFSIZE, GFP_KERNEL); - if (!req) { - while (!list_empty(&dev->tx_reqs)) { - req = container_of(dev->tx_reqs.next, - struct usb_request, list); - list_del(&req->list); - printer_req_free(dev->in_ep, req); - } - return -ENOMEM; - } + if (!req) + goto fail; list_add(&req->list, &dev->tx_reqs); } for (i = 0; i < QLEN; i++) { req = printer_req_alloc(dev->out_ep, USB_BUFSIZE, GFP_KERNEL); - if (!req) { - while (!list_empty(&dev->rx_reqs)) { - req = container_of(dev->rx_reqs.next, - struct usb_request, list); - list_del(&req->list); - printer_req_free(dev->out_ep, req); - } - return -ENOMEM; - } + if (!req) + goto fail; list_add(&req->list, &dev->rx_reqs); } -- cgit v1.2.3 From 44eccced2b9aafd1eced9fb4821f26b6dff26a25 Mon Sep 17 00:00:00 2001 From: Andrzej Pietrasiewicz Date: Tue, 3 Mar 2015 10:52:10 +0100 Subject: usb: gadget: printer: eliminate pdev member of struct printer_dev The pdev member of struct printer_dev is not used outside printer_bind_config(), so it can just as well be a local variable there. Signed-off-by: Andrzej Pietrasiewicz Signed-off-by: Felipe Balbi --- drivers/usb/gadget/legacy/printer.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/usb/gadget/legacy/printer.c b/drivers/usb/gadget/legacy/printer.c index bbcd6aa9abd1..a9c3e5782462 100644 --- a/drivers/usb/gadget/legacy/printer.c +++ b/drivers/usb/gadget/legacy/printer.c @@ -83,7 +83,6 @@ struct printer_dev { u8 printer_status; u8 reset_printer; struct cdev printer_cdev; - struct device *pdev; u8 printer_cdev_open; wait_queue_head_t wait; struct usb_function function; @@ -1175,6 +1174,7 @@ static int __init printer_bind_config(struct usb_configuration *c) { struct usb_gadget *gadget = c->cdev->gadget; struct printer_dev *dev; + struct device *pdev; int status = -ENOMEM; size_t len; u32 i; @@ -1199,11 +1199,11 @@ static int __init printer_bind_config(struct usb_configuration *c) return status; /* Setup the sysfs files for the printer gadget. */ - dev->pdev = device_create(usb_gadget_class, NULL, g_printer_devno, + pdev = device_create(usb_gadget_class, NULL, g_printer_devno, NULL, "g_printer"); - if (IS_ERR(dev->pdev)) { + if (IS_ERR(pdev)) { ERROR(dev, "Failed to create device: g_printer\n"); - status = PTR_ERR(dev->pdev); + status = PTR_ERR(pdev); goto fail; } -- cgit v1.2.3 From 406be2ccbadb5652f5894078d0e025d90683b3e9 Mon Sep 17 00:00:00 2001 From: Andrzej Pietrasiewicz Date: Tue, 3 Mar 2015 10:52:11 +0100 Subject: usb: gadget: printer: follow the naming convention for usb_add_config callback Legacy gadgets, before converting them to the new function framework, used to use the name _do_config() for usb_add_config()'s callback. This patch changes the name so that it is easier to follow the convention. Signed-off-by: Andrzej Pietrasiewicz Signed-off-by: Felipe Balbi --- drivers/usb/gadget/legacy/printer.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/usb/gadget/legacy/printer.c b/drivers/usb/gadget/legacy/printer.c index a9c3e5782462..c86583317431 100644 --- a/drivers/usb/gadget/legacy/printer.c +++ b/drivers/usb/gadget/legacy/printer.c @@ -1170,7 +1170,7 @@ static struct usb_configuration printer_cfg_driver = { .bmAttributes = USB_CONFIG_ATT_ONE | USB_CONFIG_ATT_SELFPOWER, }; -static int __init printer_bind_config(struct usb_configuration *c) +static int __init printer_do_config(struct usb_configuration *c) { struct usb_gadget *gadget = c->cdev->gadget; struct printer_dev *dev; @@ -1287,7 +1287,7 @@ static int __init printer_bind(struct usb_composite_dev *cdev) device_desc.iProduct = strings[USB_GADGET_PRODUCT_IDX].id; device_desc.iSerialNumber = strings[USB_GADGET_SERIAL_IDX].id; - ret = usb_add_config(cdev, &printer_cfg_driver, printer_bind_config); + ret = usb_add_config(cdev, &printer_cfg_driver, printer_do_config); if (ret) return ret; usb_composite_overwrite_options(cdev, &coverwrite); -- cgit v1.2.3 From ae2dd0de57a3f6b12e30e5552033a492d6d206f7 Mon Sep 17 00:00:00 2001 From: Andrzej Pietrasiewicz Date: Tue, 3 Mar 2015 10:52:12 +0100 Subject: usb: gadget: printer: standardize printer_do_config Follow the convention of distributing source code between _do_config() and _bind_config(). Signed-off-by: Andrzej Pietrasiewicz Signed-off-by: Felipe Balbi --- drivers/usb/gadget/legacy/printer.c | 39 +++++++++++++++++++++++-------------- 1 file changed, 24 insertions(+), 15 deletions(-) diff --git a/drivers/usb/gadget/legacy/printer.c b/drivers/usb/gadget/legacy/printer.c index c86583317431..494cd8a5aca4 100644 --- a/drivers/usb/gadget/legacy/printer.c +++ b/drivers/usb/gadget/legacy/printer.c @@ -1170,7 +1170,8 @@ static struct usb_configuration printer_cfg_driver = { .bmAttributes = USB_CONFIG_ATT_ONE | USB_CONFIG_ATT_SELFPOWER, }; -static int __init printer_do_config(struct usb_configuration *c) +static int f_printer_bind_config(struct usb_configuration *c, char *pnp_str, + unsigned q_len) { struct usb_gadget *gadget = c->cdev->gadget; struct printer_dev *dev; @@ -1180,8 +1181,6 @@ static int __init printer_do_config(struct usb_configuration *c) u32 i; struct usb_request *req; - usb_ep_autoconfig_reset(gadget); - dev = &usb_printer_gadget; dev->function.name = shortname; @@ -1219,21 +1218,13 @@ static int __init printer_do_config(struct usb_configuration *c) goto fail; } - if (iPNPstring) - strlcpy(&pnp_string[2], iPNPstring, (sizeof pnp_string)-2); + if (pnp_str) + strlcpy(&pnp_string[2], pnp_str, sizeof(pnp_string) - 2); len = strlen(pnp_string); pnp_string[0] = (len >> 8) & 0xFF; pnp_string[1] = len & 0xFF; - usb_gadget_set_selfpowered(gadget); - - if (gadget_is_otg(gadget)) { - otg_descriptor.bmAttributes |= USB_OTG_HNP; - printer_cfg_driver.descriptors = otg_desc; - printer_cfg_driver.bmAttributes |= USB_CONFIG_ATT_WAKEUP; - } - spin_lock_init(&dev->lock); mutex_init(&dev->lock_printer_io); INIT_LIST_HEAD(&dev->tx_reqs_active); @@ -1250,14 +1241,14 @@ static int __init printer_do_config(struct usb_configuration *c) dev->current_rx_buf = NULL; status = -ENOMEM; - for (i = 0; i < QLEN; i++) { + for (i = 0; i < q_len; i++) { req = printer_req_alloc(dev->in_ep, USB_BUFSIZE, GFP_KERNEL); if (!req) goto fail; list_add(&req->list, &dev->tx_reqs); } - for (i = 0; i < QLEN; i++) { + for (i = 0; i < q_len; i++) { req = printer_req_alloc(dev->out_ep, USB_BUFSIZE, GFP_KERNEL); if (!req) goto fail; @@ -1276,6 +1267,24 @@ fail: return status; } +static int __init printer_do_config(struct usb_configuration *c) +{ + struct usb_gadget *gadget = c->cdev->gadget; + + usb_ep_autoconfig_reset(gadget); + + usb_gadget_set_selfpowered(gadget); + + if (gadget_is_otg(gadget)) { + otg_descriptor.bmAttributes |= USB_OTG_HNP; + printer_cfg_driver.descriptors = otg_desc; + printer_cfg_driver.bmAttributes |= USB_CONFIG_ATT_WAKEUP; + } + + return f_printer_bind_config(c, iPNPstring, QLEN); + +} + static int __init printer_bind(struct usb_composite_dev *cdev) { int ret; -- cgit v1.2.3 From 4504b5a0b22e26a7213d9e08706303a790f5a400 Mon Sep 17 00:00:00 2001 From: Andrzej Pietrasiewicz Date: Tue, 3 Mar 2015 10:52:13 +0100 Subject: usb: gadget: printer: move function-related bind code to function's bind In order to factor out a reusable f_printer.c, the code related to the function should be placed in functions related to the function. Signed-off-by: Andrzej Pietrasiewicz Signed-off-by: Felipe Balbi --- drivers/usb/gadget/legacy/printer.c | 114 +++++++++++++++++++++--------------- 1 file changed, 66 insertions(+), 48 deletions(-) diff --git a/drivers/usb/gadget/legacy/printer.c b/drivers/usb/gadget/legacy/printer.c index 494cd8a5aca4..c8570441a303 100644 --- a/drivers/usb/gadget/legacy/printer.c +++ b/drivers/usb/gadget/legacy/printer.c @@ -85,6 +85,7 @@ struct printer_dev { struct cdev printer_cdev; u8 printer_cdev_open; wait_queue_head_t wait; + unsigned q_len; struct usb_function function; }; @@ -1045,18 +1046,25 @@ unknown: static int __init printer_func_bind(struct usb_configuration *c, struct usb_function *f) { + struct usb_gadget *gadget = c->cdev->gadget; struct printer_dev *dev = container_of(f, struct printer_dev, function); + struct device *pdev; struct usb_composite_dev *cdev = c->cdev; struct usb_ep *in_ep; struct usb_ep *out_ep = NULL; + struct usb_request *req; int id; int ret; + u32 i; id = usb_interface_id(c, f); if (id < 0) return id; intf_desc.bInterfaceNumber = id; + /* finish hookup to lower layer ... */ + dev->gadget = gadget; + /* all we really need is bulk IN/OUT */ in_ep = usb_ep_autoconfig(cdev->gadget, &fs_ep_in_desc); if (!in_ep) { @@ -1085,7 +1093,64 @@ autoconf_fail: dev->in_ep = in_ep; dev->out_ep = out_ep; + + ret = -ENOMEM; + for (i = 0; i < dev->q_len; i++) { + req = printer_req_alloc(dev->in_ep, USB_BUFSIZE, GFP_KERNEL); + if (!req) + goto fail_tx_reqs; + list_add(&req->list, &dev->tx_reqs); + } + + for (i = 0; i < dev->q_len; i++) { + req = printer_req_alloc(dev->out_ep, USB_BUFSIZE, GFP_KERNEL); + if (!req) + goto fail_rx_reqs; + list_add(&req->list, &dev->rx_reqs); + } + + /* Setup the sysfs files for the printer gadget. */ + pdev = device_create(usb_gadget_class, NULL, g_printer_devno, + NULL, "g_printer"); + if (IS_ERR(pdev)) { + ERROR(dev, "Failed to create device: g_printer\n"); + ret = PTR_ERR(pdev); + goto fail_rx_reqs; + } + + /* + * Register a character device as an interface to a user mode + * program that handles the printer specific functionality. + */ + cdev_init(&dev->printer_cdev, &printer_io_operations); + dev->printer_cdev.owner = THIS_MODULE; + ret = cdev_add(&dev->printer_cdev, g_printer_devno, 1); + if (ret) { + ERROR(dev, "Failed to open char device\n"); + goto fail_cdev_add; + } + return 0; + +fail_cdev_add: + device_destroy(usb_gadget_class, g_printer_devno); + +fail_rx_reqs: + while (!list_empty(&dev->rx_reqs)) { + req = container_of(dev->rx_reqs.next, struct usb_request, list); + list_del(&req->list); + printer_req_free(dev->out_ep, req); + } + +fail_tx_reqs: + while (!list_empty(&dev->tx_reqs)) { + req = container_of(dev->tx_reqs.next, struct usb_request, list); + list_del(&req->list); + printer_req_free(dev->in_ep, req); + } + + return ret; + } static void printer_func_unbind(struct usb_configuration *c, @@ -1173,13 +1238,9 @@ static struct usb_configuration printer_cfg_driver = { static int f_printer_bind_config(struct usb_configuration *c, char *pnp_str, unsigned q_len) { - struct usb_gadget *gadget = c->cdev->gadget; struct printer_dev *dev; - struct device *pdev; int status = -ENOMEM; size_t len; - u32 i; - struct usb_request *req; dev = &usb_printer_gadget; @@ -1193,31 +1254,11 @@ static int f_printer_bind_config(struct usb_configuration *c, char *pnp_str, INIT_LIST_HEAD(&dev->rx_reqs); INIT_LIST_HEAD(&dev->rx_buffers); + dev->q_len = q_len; status = usb_add_function(c, &dev->function); if (status) return status; - /* Setup the sysfs files for the printer gadget. */ - pdev = device_create(usb_gadget_class, NULL, g_printer_devno, - NULL, "g_printer"); - if (IS_ERR(pdev)) { - ERROR(dev, "Failed to create device: g_printer\n"); - status = PTR_ERR(pdev); - goto fail; - } - - /* - * Register a character device as an interface to a user mode - * program that handles the printer specific functionality. - */ - cdev_init(&dev->printer_cdev, &printer_io_operations); - dev->printer_cdev.owner = THIS_MODULE; - status = cdev_add(&dev->printer_cdev, g_printer_devno, 1); - if (status) { - ERROR(dev, "Failed to open char device\n"); - goto fail; - } - if (pnp_str) strlcpy(&pnp_string[2], pnp_str, sizeof(pnp_string) - 2); @@ -1240,31 +1281,8 @@ static int f_printer_bind_config(struct usb_configuration *c, char *pnp_str, dev->current_rx_bytes = 0; dev->current_rx_buf = NULL; - status = -ENOMEM; - for (i = 0; i < q_len; i++) { - req = printer_req_alloc(dev->in_ep, USB_BUFSIZE, GFP_KERNEL); - if (!req) - goto fail; - list_add(&req->list, &dev->tx_reqs); - } - - for (i = 0; i < q_len; i++) { - req = printer_req_alloc(dev->out_ep, USB_BUFSIZE, GFP_KERNEL); - if (!req) - goto fail; - list_add(&req->list, &dev->rx_reqs); - } - - /* finish hookup to lower layer ... */ - dev->gadget = gadget; - INFO(dev, "%s, version: " DRIVER_VERSION "\n", driver_desc); return 0; - -fail: - printer_cfg_unbind(c); - usb_remove_function(c, &dev->function); - return status; } static int __init printer_do_config(struct usb_configuration *c) -- cgit v1.2.3 From cee5cbff8d80ec2d10fe8070f229e95cc42443bf Mon Sep 17 00:00:00 2001 From: Andrzej Pietrasiewicz Date: Tue, 3 Mar 2015 10:52:14 +0100 Subject: usb: gadget: printer: call usb_add_function() last Conversion to the new function interface requires splitting a _bind_config() function into two parts: allocation of container_of struct usb_function and invocation of usb_add_function(). This patch moves the latter to the end of the f_printer_bind_config() in order to enable conversion to the new interface. Signed-off-by: Andrzej Pietrasiewicz Signed-off-by: Felipe Balbi --- drivers/usb/gadget/legacy/printer.c | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/drivers/usb/gadget/legacy/printer.c b/drivers/usb/gadget/legacy/printer.c index c8570441a303..5dbb93a91512 100644 --- a/drivers/usb/gadget/legacy/printer.c +++ b/drivers/usb/gadget/legacy/printer.c @@ -1254,11 +1254,6 @@ static int f_printer_bind_config(struct usb_configuration *c, char *pnp_str, INIT_LIST_HEAD(&dev->rx_reqs); INIT_LIST_HEAD(&dev->rx_buffers); - dev->q_len = q_len; - status = usb_add_function(c, &dev->function); - if (status) - return status; - if (pnp_str) strlcpy(&pnp_string[2], pnp_str, sizeof(pnp_string) - 2); @@ -1280,7 +1275,11 @@ static int f_printer_bind_config(struct usb_configuration *c, char *pnp_str, dev->current_rx_req = NULL; dev->current_rx_bytes = 0; dev->current_rx_buf = NULL; + dev->q_len = q_len; + status = usb_add_function(c, &dev->function); + if (status) + return status; INFO(dev, "%s, version: " DRIVER_VERSION "\n", driver_desc); return 0; } -- cgit v1.2.3 From 991cd26249e775c07347ab4d62adfbc3284e7704 Mon Sep 17 00:00:00 2001 From: Andrzej Pietrasiewicz Date: Tue, 3 Mar 2015 10:52:15 +0100 Subject: usb: gadget: printer: move function-related unbind code to function's unbind In order to factor out a reusable f_printer.c, the code related to the function should be placed in functions related to the function. printer_cfg_unbind() becomes empty, so it is removed. Signed-off-by: Andrzej Pietrasiewicz Signed-off-by: Felipe Balbi --- drivers/usb/gadget/legacy/printer.c | 58 ++++++++++++++++--------------------- 1 file changed, 25 insertions(+), 33 deletions(-) diff --git a/drivers/usb/gadget/legacy/printer.c b/drivers/usb/gadget/legacy/printer.c index 5dbb93a91512..84e6cdd72137 100644 --- a/drivers/usb/gadget/legacy/printer.c +++ b/drivers/usb/gadget/legacy/printer.c @@ -1155,44 +1155,12 @@ fail_tx_reqs: static void printer_func_unbind(struct usb_configuration *c, struct usb_function *f) -{ - usb_free_all_descriptors(f); -} - -static int printer_func_set_alt(struct usb_function *f, - unsigned intf, unsigned alt) -{ - struct printer_dev *dev = container_of(f, struct printer_dev, function); - int ret = -ENOTSUPP; - - if (!alt) - ret = set_interface(dev, intf); - - return ret; -} - -static void printer_func_disable(struct usb_function *f) -{ - struct printer_dev *dev = container_of(f, struct printer_dev, function); - unsigned long flags; - - DBG(dev, "%s\n", __func__); - - spin_lock_irqsave(&dev->lock, flags); - printer_reset_interface(dev); - spin_unlock_irqrestore(&dev->lock, flags); -} - -static void printer_cfg_unbind(struct usb_configuration *c) { struct printer_dev *dev; struct usb_request *req; dev = &usb_printer_gadget; - DBG(dev, "%s\n", __func__); - - /* Remove sysfs files */ device_destroy(usb_gadget_class, g_printer_devno); /* Remove Character Device */ @@ -1226,11 +1194,35 @@ static void printer_cfg_unbind(struct usb_configuration *c) list_del(&req->list); printer_req_free(dev->out_ep, req); } + usb_free_all_descriptors(f); +} + +static int printer_func_set_alt(struct usb_function *f, + unsigned intf, unsigned alt) +{ + struct printer_dev *dev = container_of(f, struct printer_dev, function); + int ret = -ENOTSUPP; + + if (!alt) + ret = set_interface(dev, intf); + + return ret; +} + +static void printer_func_disable(struct usb_function *f) +{ + struct printer_dev *dev = container_of(f, struct printer_dev, function); + unsigned long flags; + + DBG(dev, "%s\n", __func__); + + spin_lock_irqsave(&dev->lock, flags); + printer_reset_interface(dev); + spin_unlock_irqrestore(&dev->lock, flags); } static struct usb_configuration printer_cfg_driver = { .label = "printer", - .unbind = printer_cfg_unbind, .bConfigurationValue = 1, .bmAttributes = USB_CONFIG_ATT_ONE | USB_CONFIG_ATT_SELFPOWER, }; -- cgit v1.2.3 From 085617a1eb865c2987c05652bf82d35f500ac4b4 Mon Sep 17 00:00:00 2001 From: Andrzej Pietrasiewicz Date: Tue, 3 Mar 2015 10:52:16 +0100 Subject: usb: gadget: printer: define pnp string buffer length Avoid using magic numbers. Signed-off-by: Andrzej Pietrasiewicz Signed-off-by: Felipe Balbi --- drivers/usb/gadget/legacy/printer.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/drivers/usb/gadget/legacy/printer.c b/drivers/usb/gadget/legacy/printer.c index 84e6cdd72137..db5e2f0681c7 100644 --- a/drivers/usb/gadget/legacy/printer.c +++ b/drivers/usb/gadget/legacy/printer.c @@ -276,9 +276,11 @@ static inline struct usb_endpoint_descriptor *ep_desc(struct usb_gadget *gadget, /* descriptors that are built on-demand */ +#define PNP_STRING_LEN 1024 + static char product_desc [40] = DRIVER_DESC; static char serial_num [40] = "1"; -static char pnp_string [1024] = +static char pnp_string[PNP_STRING_LEN] = "XXMFG:linux;MDL:g_printer;CLS:PRINTER;SN:1;"; /* static strings, in UTF-8 */ @@ -1247,7 +1249,7 @@ static int f_printer_bind_config(struct usb_configuration *c, char *pnp_str, INIT_LIST_HEAD(&dev->rx_buffers); if (pnp_str) - strlcpy(&pnp_string[2], pnp_str, sizeof(pnp_string) - 2); + strlcpy(&pnp_string[2], pnp_str, PNP_STRING_LEN - 2); len = strlen(pnp_string); pnp_string[0] = (len >> 8) & 0xFF; -- cgit v1.2.3 From 5a84e6f608598dd691c0024eab50fffb96aca43b Mon Sep 17 00:00:00 2001 From: Andrzej Pietrasiewicz Date: Tue, 3 Mar 2015 10:52:17 +0100 Subject: usb: gadget: printer: don't access file global pnp_string in function's code In order to factor out a reusable f_printer, the function's code should not use file global variables related to legacy printer gadget's implementation. Signed-off-by: Andrzej Pietrasiewicz Signed-off-by: Felipe Balbi --- drivers/usb/gadget/legacy/printer.c | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/drivers/usb/gadget/legacy/printer.c b/drivers/usb/gadget/legacy/printer.c index db5e2f0681c7..42c46da6f59f 100644 --- a/drivers/usb/gadget/legacy/printer.c +++ b/drivers/usb/gadget/legacy/printer.c @@ -86,6 +86,7 @@ struct printer_dev { u8 printer_cdev_open; wait_queue_head_t wait; unsigned q_len; + char *pnp_string; /* We don't own memory! */ struct usb_function function; }; @@ -994,10 +995,10 @@ static int printer_func_setup(struct usb_function *f, if ((wIndex>>8) != dev->interface) break; - value = (pnp_string[0]<<8)|pnp_string[1]; - memcpy(req->buf, pnp_string, value); + value = (dev->pnp_string[0] << 8) | dev->pnp_string[1]; + memcpy(req->buf, dev->pnp_string, value); DBG(dev, "1284 PNP String: %x %s\n", value, - &pnp_string[2]); + &dev->pnp_string[2]); break; case 1: /* Get Port Status */ @@ -1230,13 +1231,14 @@ static struct usb_configuration printer_cfg_driver = { }; static int f_printer_bind_config(struct usb_configuration *c, char *pnp_str, - unsigned q_len) + char *pnp_string, unsigned q_len) { struct printer_dev *dev; int status = -ENOMEM; size_t len; dev = &usb_printer_gadget; + dev->pnp_string = pnp_string; dev->function.name = shortname; dev->function.bind = printer_func_bind; @@ -1249,7 +1251,7 @@ static int f_printer_bind_config(struct usb_configuration *c, char *pnp_str, INIT_LIST_HEAD(&dev->rx_buffers); if (pnp_str) - strlcpy(&pnp_string[2], pnp_str, PNP_STRING_LEN - 2); + strlcpy(&dev->pnp_string[2], pnp_str, PNP_STRING_LEN - 2); len = strlen(pnp_string); pnp_string[0] = (len >> 8) & 0xFF; @@ -1292,7 +1294,7 @@ static int __init printer_do_config(struct usb_configuration *c) printer_cfg_driver.bmAttributes |= USB_CONFIG_ATT_WAKEUP; } - return f_printer_bind_config(c, iPNPstring, QLEN); + return f_printer_bind_config(c, iPNPstring, pnp_string, QLEN); } -- cgit v1.2.3 From d82cd82edb98d727c6a0804a6e271e3081559404 Mon Sep 17 00:00:00 2001 From: Andrzej Pietrasiewicz Date: Tue, 3 Mar 2015 10:52:18 +0100 Subject: usb: gadget: printer: add setup and cleanup functions Factor out gprinter_setup() and gprinter_cleanup() so that it is easy to change the place they are called from. Signed-off-by: Andrzej Pietrasiewicz Signed-off-by: Felipe Balbi --- drivers/usb/gadget/legacy/printer.c | 46 +++++++++++++++++++++++++------------ 1 file changed, 31 insertions(+), 15 deletions(-) diff --git a/drivers/usb/gadget/legacy/printer.c b/drivers/usb/gadget/legacy/printer.c index 42c46da6f59f..83cea9a5c75e 100644 --- a/drivers/usb/gadget/legacy/printer.c +++ b/drivers/usb/gadget/legacy/printer.c @@ -1298,6 +1298,34 @@ static int __init printer_do_config(struct usb_configuration *c) } +static int gprinter_setup(void) +{ + int status; + + usb_gadget_class = class_create(THIS_MODULE, "usb_printer_gadget"); + if (IS_ERR(usb_gadget_class)) { + status = PTR_ERR(usb_gadget_class); + pr_err("unable to create usb_gadget class %d\n", status); + return status; + } + + status = alloc_chrdev_region(&g_printer_devno, 0, 1, + "USB printer gadget"); + if (status) { + pr_err("alloc_chrdev_region %d\n", status); + class_destroy(usb_gadget_class); + } + + return status; +} + +/* must be called with struct printer_dev's lock_printer_io held */ +static void gprinter_cleanup(void) +{ + unregister_chrdev_region(g_printer_devno, 1); + class_destroy(usb_gadget_class); +} + static int __init printer_bind(struct usb_composite_dev *cdev) { int ret; @@ -1329,20 +1357,9 @@ init(void) { int status; - usb_gadget_class = class_create(THIS_MODULE, "usb_printer_gadget"); - if (IS_ERR(usb_gadget_class)) { - status = PTR_ERR(usb_gadget_class); - pr_err("unable to create usb_gadget class %d\n", status); - return status; - } - - status = alloc_chrdev_region(&g_printer_devno, 0, 1, - "USB printer gadget"); - if (status) { - pr_err("alloc_chrdev_region %d\n", status); - class_destroy(usb_gadget_class); + status = gprinter_setup(); + if (status) return status; - } status = usb_composite_probe(&printer_driver); if (status) { @@ -1360,8 +1377,7 @@ cleanup(void) { mutex_lock(&usb_printer_gadget.lock_printer_io); usb_composite_unregister(&printer_driver); - unregister_chrdev_region(g_printer_devno, 1); - class_destroy(usb_gadget_class); + gprinter_cleanup(); mutex_unlock(&usb_printer_gadget.lock_printer_io); } module_exit(cleanup); -- cgit v1.2.3 From a844715d2fc44adc2da17f90b34cc0d0c1e81596 Mon Sep 17 00:00:00 2001 From: Andrzej Pietrasiewicz Date: Tue, 3 Mar 2015 10:52:19 +0100 Subject: usb: gadget: printer: call gprinter_setup() from gadget's bind Call gprinter_setup() from gadget's bind instead of module's init. Call gprinter_cleaup() corerspondingly. This detaches printer function's logic from legacy printer gadget's implementation. Signed-off-by: Andrzej Pietrasiewicz Signed-off-by: Felipe Balbi --- drivers/usb/gadget/legacy/printer.c | 35 ++++++++++++++++++----------------- 1 file changed, 18 insertions(+), 17 deletions(-) diff --git a/drivers/usb/gadget/legacy/printer.c b/drivers/usb/gadget/legacy/printer.c index 83cea9a5c75e..b7889b1f7afa 100644 --- a/drivers/usb/gadget/legacy/printer.c +++ b/drivers/usb/gadget/legacy/printer.c @@ -1330,45 +1330,47 @@ static int __init printer_bind(struct usb_composite_dev *cdev) { int ret; + ret = gprinter_setup(); + if (ret) + return ret; + ret = usb_string_ids_tab(cdev, strings); - if (ret < 0) + if (ret < 0) { + gprinter_cleanup(); return ret; + } device_desc.iManufacturer = strings[USB_GADGET_MANUFACTURER_IDX].id; device_desc.iProduct = strings[USB_GADGET_PRODUCT_IDX].id; device_desc.iSerialNumber = strings[USB_GADGET_SERIAL_IDX].id; ret = usb_add_config(cdev, &printer_cfg_driver, printer_do_config); - if (ret) + if (ret) { + gprinter_cleanup(); return ret; + } usb_composite_overwrite_options(cdev, &coverwrite); return ret; } +static int __exit printer_unbind(struct usb_composite_dev *cdev) +{ + gprinter_cleanup(); + return 0; +} + static __refdata struct usb_composite_driver printer_driver = { .name = shortname, .dev = &device_desc, .strings = dev_strings, .max_speed = USB_SPEED_SUPER, .bind = printer_bind, + .unbind = printer_unbind, }; static int __init init(void) { - int status; - - status = gprinter_setup(); - if (status) - return status; - - status = usb_composite_probe(&printer_driver); - if (status) { - class_destroy(usb_gadget_class); - unregister_chrdev_region(g_printer_devno, 1); - pr_err("usb_gadget_probe_driver %x\n", status); - } - - return status; + return usb_composite_probe(&printer_driver); } module_init(init); @@ -1377,7 +1379,6 @@ cleanup(void) { mutex_lock(&usb_printer_gadget.lock_printer_io); usb_composite_unregister(&printer_driver); - gprinter_cleanup(); mutex_unlock(&usb_printer_gadget.lock_printer_io); } module_exit(cleanup); -- cgit v1.2.3 From dec81cf1dcaac5b91de7cd32c96aadcb94840c7f Mon Sep 17 00:00:00 2001 From: Andrzej Pietrasiewicz Date: Tue, 3 Mar 2015 10:52:20 +0100 Subject: usb: gadget: printer: eliminate file global printer_mutex The mutex is a legacy after semi-automatic Big Kernel Lock removal. printer_open() does its own locking, so no need to duplicate it. Signed-off-by: Andrzej Pietrasiewicz Signed-off-by: Felipe Balbi --- drivers/usb/gadget/legacy/printer.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/drivers/usb/gadget/legacy/printer.c b/drivers/usb/gadget/legacy/printer.c index b7889b1f7afa..3206ebcdd7a6 100644 --- a/drivers/usb/gadget/legacy/printer.c +++ b/drivers/usb/gadget/legacy/printer.c @@ -48,7 +48,6 @@ USB_GADGET_COMPOSITE_OPTIONS(); #define DRIVER_DESC "Printer Gadget" #define DRIVER_VERSION "2007 OCT 06" -static DEFINE_MUTEX(printer_mutex); static const char shortname [] = "printer"; static const char driver_desc [] = DRIVER_DESC; @@ -420,7 +419,6 @@ printer_open(struct inode *inode, struct file *fd) unsigned long flags; int ret = -EBUSY; - mutex_lock(&printer_mutex); dev = container_of(inode->i_cdev, struct printer_dev, printer_cdev); spin_lock_irqsave(&dev->lock, flags); @@ -436,7 +434,6 @@ printer_open(struct inode *inode, struct file *fd) spin_unlock_irqrestore(&dev->lock, flags); DBG(dev, "printer_open returned %x\n", ret); - mutex_unlock(&printer_mutex); return ret; } -- cgit v1.2.3 From 8fe20f661f3cfbb6778368eb3c73f8a6438ac640 Mon Sep 17 00:00:00 2001 From: Andrzej Pietrasiewicz Date: Tue, 3 Mar 2015 10:52:21 +0100 Subject: usb: gadget: printer: don't access file global usb_printer_gadget in function's code The printer_dev can be recovered from printer_func_unbind() function's parameters. Signed-off-by: Andrzej Pietrasiewicz Signed-off-by: Felipe Balbi --- drivers/usb/gadget/legacy/printer.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/usb/gadget/legacy/printer.c b/drivers/usb/gadget/legacy/printer.c index 3206ebcdd7a6..806475c19934 100644 --- a/drivers/usb/gadget/legacy/printer.c +++ b/drivers/usb/gadget/legacy/printer.c @@ -1159,7 +1159,7 @@ static void printer_func_unbind(struct usb_configuration *c, struct printer_dev *dev; struct usb_request *req; - dev = &usb_printer_gadget; + dev = container_of(f, struct printer_dev, function); device_destroy(usb_gadget_class, g_printer_devno); -- cgit v1.2.3 From 143d53e10ecfeee7245341aa9c50515b5680ffd4 Mon Sep 17 00:00:00 2001 From: Andrzej Pietrasiewicz Date: Tue, 3 Mar 2015 10:52:22 +0100 Subject: usb: gadget: printer: add container_of helper for printer_dev 5 uses of container_of() in the same context justify wrapping it in a static inline function. Signed-off-by: Andrzej Pietrasiewicz Signed-off-by: Felipe Balbi --- drivers/usb/gadget/legacy/printer.c | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/drivers/usb/gadget/legacy/printer.c b/drivers/usb/gadget/legacy/printer.c index 806475c19934..955847fe8092 100644 --- a/drivers/usb/gadget/legacy/printer.c +++ b/drivers/usb/gadget/legacy/printer.c @@ -89,6 +89,11 @@ struct printer_dev { struct usb_function function; }; +static inline struct printer_dev *func_to_printer(struct usb_function *f) +{ + return container_of(f, struct printer_dev, function); +} + static struct printer_dev usb_printer_gadget; /*-------------------------------------------------------------------------*/ @@ -973,7 +978,7 @@ static void printer_soft_reset(struct printer_dev *dev) static int printer_func_setup(struct usb_function *f, const struct usb_ctrlrequest *ctrl) { - struct printer_dev *dev = container_of(f, struct printer_dev, function); + struct printer_dev *dev = func_to_printer(f); struct usb_composite_dev *cdev = f->config->cdev; struct usb_request *req = cdev->req; int value = -EOPNOTSUPP; @@ -1047,7 +1052,7 @@ static int __init printer_func_bind(struct usb_configuration *c, struct usb_function *f) { struct usb_gadget *gadget = c->cdev->gadget; - struct printer_dev *dev = container_of(f, struct printer_dev, function); + struct printer_dev *dev = func_to_printer(f); struct device *pdev; struct usb_composite_dev *cdev = c->cdev; struct usb_ep *in_ep; @@ -1159,7 +1164,7 @@ static void printer_func_unbind(struct usb_configuration *c, struct printer_dev *dev; struct usb_request *req; - dev = container_of(f, struct printer_dev, function); + dev = func_to_printer(f); device_destroy(usb_gadget_class, g_printer_devno); @@ -1200,7 +1205,7 @@ static void printer_func_unbind(struct usb_configuration *c, static int printer_func_set_alt(struct usb_function *f, unsigned intf, unsigned alt) { - struct printer_dev *dev = container_of(f, struct printer_dev, function); + struct printer_dev *dev = func_to_printer(f); int ret = -ENOTSUPP; if (!alt) @@ -1211,7 +1216,7 @@ static int printer_func_set_alt(struct usb_function *f, static void printer_func_disable(struct usb_function *f) { - struct printer_dev *dev = container_of(f, struct printer_dev, function); + struct printer_dev *dev = func_to_printer(f); unsigned long flags; DBG(dev, "%s\n", __func__); -- cgit v1.2.3 From f563d230903210acc2336af58e422216b68ded76 Mon Sep 17 00:00:00 2001 From: Andrzej Pietrasiewicz Date: Tue, 3 Mar 2015 10:52:23 +0100 Subject: usb: gadget: composite: add req_match method to usb_function Non-standard requests can encode the actual interface number in a non-standard way. For example composite_setup() assumes that it is w_index && 0xFF, but the printer function encodes the interface number in a context-dependet way (either w_index or w_index >> 8). This can lead to such requests being directed to wrong functions. This patch adds req_match() method to usb_function. Its purpose is to verify that a given request can be handled by a given function. If any function within a configuration provides the method and it returns true, then it is assumed that the right function is found. If a function uses req_match(), it should try as hard as possible to determine if the request is meant for it. If no functions in a configuration provide req_match or none of them returns true, then fall back to the usual approach. Signed-off-by: Andrzej Pietrasiewicz Signed-off-by: Felipe Balbi --- drivers/usb/gadget/composite.c | 6 +++++- include/linux/usb/composite.h | 3 +++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/drivers/usb/gadget/composite.c b/drivers/usb/gadget/composite.c index 9fb92310fb2b..4d25e11b1f72 100644 --- a/drivers/usb/gadget/composite.c +++ b/drivers/usb/gadget/composite.c @@ -1758,6 +1758,10 @@ unknown: * take such requests too, if that's ever needed: to work * in config 0, etc. */ + list_for_each_entry(f, &cdev->config->functions, list) + if (f->req_match && f->req_match(f, ctrl)) + goto try_fun_setup; + f = NULL; switch (ctrl->bRequestType & USB_RECIP_MASK) { case USB_RECIP_INTERFACE: if (!cdev->config || intf >= MAX_CONFIG_INTERFACES) @@ -1775,7 +1779,7 @@ unknown: f = NULL; break; } - +try_fun_setup: if (f && f->setup) value = f->setup(f, ctrl); else { diff --git a/include/linux/usb/composite.h b/include/linux/usb/composite.h index 3d87defcc527..2511469a9904 100644 --- a/include/linux/usb/composite.h +++ b/include/linux/usb/composite.h @@ -148,6 +148,7 @@ struct usb_os_desc_table { * @disable: (REQUIRED) Indicates the function should be disabled. Reasons * include host resetting or reconfiguring the gadget, and disconnection. * @setup: Used for interface-specific control requests. + * @req_match: Tests if a given class request can be handled by this function. * @suspend: Notifies functions when the host stops sending USB traffic. * @resume: Notifies functions when the host restarts USB traffic. * @get_status: Returns function status as a reply to @@ -213,6 +214,8 @@ struct usb_function { void (*disable)(struct usb_function *); int (*setup)(struct usb_function *, const struct usb_ctrlrequest *); + bool (*req_match)(struct usb_function *, + const struct usb_ctrlrequest *); void (*suspend)(struct usb_function *); void (*resume)(struct usb_function *); -- cgit v1.2.3 From d7239f4c6daeb7a987a0e6f37a3ea24b37f7c208 Mon Sep 17 00:00:00 2001 From: Andrzej Pietrasiewicz Date: Tue, 3 Mar 2015 10:52:24 +0100 Subject: usb: gadget: printer: name class specific requests Avoid using magic numbers. Signed-off-by: Andrzej Pietrasiewicz Signed-off-by: Felipe Balbi --- drivers/usb/gadget/legacy/printer.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/drivers/usb/gadget/legacy/printer.c b/drivers/usb/gadget/legacy/printer.c index 955847fe8092..78f515413e3b 100644 --- a/drivers/usb/gadget/legacy/printer.c +++ b/drivers/usb/gadget/legacy/printer.c @@ -47,6 +47,9 @@ USB_GADGET_COMPOSITE_OPTIONS(); #define DRIVER_DESC "Printer Gadget" #define DRIVER_VERSION "2007 OCT 06" +#define GET_DEVICE_ID 0 +#define GET_PORT_STATUS 1 +#define SOFT_RESET 2 static const char shortname [] = "printer"; static const char driver_desc [] = DRIVER_DESC; @@ -992,7 +995,7 @@ static int printer_func_setup(struct usb_function *f, switch (ctrl->bRequestType&USB_TYPE_MASK) { case USB_TYPE_CLASS: switch (ctrl->bRequest) { - case 0: /* Get the IEEE-1284 PNP String */ + case GET_DEVICE_ID: /* Get the IEEE-1284 PNP String */ /* Only one printer interface is supported. */ if ((wIndex>>8) != dev->interface) break; @@ -1003,7 +1006,7 @@ static int printer_func_setup(struct usb_function *f, &dev->pnp_string[2]); break; - case 1: /* Get Port Status */ + case GET_PORT_STATUS: /* Get Port Status */ /* Only one printer interface is supported. */ if (wIndex != dev->interface) break; @@ -1012,7 +1015,7 @@ static int printer_func_setup(struct usb_function *f, value = min(wLength, (u16) 1); break; - case 2: /* Soft Reset */ + case SOFT_RESET: /* Soft Reset */ /* Only one printer interface is supported. */ if (wIndex != dev->interface) break; -- cgit v1.2.3 From 636bc0ed271d996e18365345e9c00fc712edc610 Mon Sep 17 00:00:00 2001 From: Andrzej Pietrasiewicz Date: Tue, 3 Mar 2015 10:52:25 +0100 Subject: usb: gadget: printer: add req_match for printer function Verify that a given usb_ctrlrequest is meant for printer function. The following parts of the request are tested: - bmRequestType:Data transfer direction - bmRequestType:Type - bmRequestType:Recipient - bRequest - wValue for bRequest 1 and 2 - wLength Additionally, the request is considered meant for this function iff the decoded interface number matches dev->interface. Signed-off-by: Andrzej Pietrasiewicz Signed-off-by: Felipe Balbi --- drivers/usb/gadget/legacy/printer.c | 36 ++++++++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/drivers/usb/gadget/legacy/printer.c b/drivers/usb/gadget/legacy/printer.c index 78f515413e3b..c059af1aa454 100644 --- a/drivers/usb/gadget/legacy/printer.c +++ b/drivers/usb/gadget/legacy/printer.c @@ -974,6 +974,41 @@ static void printer_soft_reset(struct printer_dev *dev) /*-------------------------------------------------------------------------*/ +static bool gprinter_req_match(struct usb_function *f, + const struct usb_ctrlrequest *ctrl) +{ + struct printer_dev *dev = func_to_printer(f); + u16 w_index = le16_to_cpu(ctrl->wIndex); + u16 w_value = le16_to_cpu(ctrl->wValue); + u16 w_length = le16_to_cpu(ctrl->wLength); + + if ((ctrl->bRequestType & USB_RECIP_MASK) != USB_RECIP_INTERFACE || + (ctrl->bRequestType & USB_TYPE_MASK) != USB_TYPE_CLASS) + return false; + + switch (ctrl->bRequest) { + case GET_DEVICE_ID: + w_index >>= 8; + if (w_length <= PNP_STRING_LEN && + (USB_DIR_IN & ctrl->bRequestType)) + break; + return false; + case GET_PORT_STATUS: + if (!w_value && w_length == 1 && + (USB_DIR_IN & ctrl->bRequestType)) + break; + return false; + case SOFT_RESET: + if (!w_value && !w_length && + (USB_DIR_OUT & ctrl->bRequestType)) + break; + /* fall through */ + default: + return false; + } + return w_index == dev->interface; +} + /* * The setup() callback implements all the ep0 functionality that's not * handled lower down. @@ -1251,6 +1286,7 @@ static int f_printer_bind_config(struct usb_configuration *c, char *pnp_str, dev->function.unbind = printer_func_unbind; dev->function.set_alt = printer_func_set_alt; dev->function.disable = printer_func_disable; + dev->function.req_match = gprinter_req_match; INIT_LIST_HEAD(&dev->tx_reqs); INIT_LIST_HEAD(&dev->rx_reqs); INIT_LIST_HEAD(&dev->rx_buffers); -- cgit v1.2.3 From 6dd8c2e69521ec9d7b23a741294702f844353f1a Mon Sep 17 00:00:00 2001 From: Andrzej Pietrasiewicz Date: Tue, 3 Mar 2015 10:52:26 +0100 Subject: usb: gadget: printer: allocate printer_dev instances dynamically With all the obstacles removed it is possible to allow more than one instance of the printer function. Since the function requires allocating character device region, a maximum number of allowed instances is defined. Such an approach is used in f_acm and in f_hid. With multiple instances it does not make sense to depend on a lock_printer_io member of a dynamically allocated (and destroyed) struct printer_dev to clean up after all instances of the printer function. Signed-off-by: Andrzej Pietrasiewicz Signed-off-by: Felipe Balbi --- drivers/usb/gadget/legacy/printer.c | 62 ++++++++++++++++++++++++------------- 1 file changed, 40 insertions(+), 22 deletions(-) diff --git a/drivers/usb/gadget/legacy/printer.c b/drivers/usb/gadget/legacy/printer.c index c059af1aa454..d1f85f81975f 100644 --- a/drivers/usb/gadget/legacy/printer.c +++ b/drivers/usb/gadget/legacy/printer.c @@ -51,11 +51,12 @@ USB_GADGET_COMPOSITE_OPTIONS(); #define GET_PORT_STATUS 1 #define SOFT_RESET 2 +#define PRINTER_MINORS 4 + static const char shortname [] = "printer"; static const char driver_desc [] = DRIVER_DESC; -static dev_t g_printer_devno; - +static int major, minors; static struct class *usb_gadget_class; /*-------------------------------------------------------------------------*/ @@ -84,6 +85,7 @@ struct printer_dev { u8 *current_rx_buf; u8 printer_status; u8 reset_printer; + int minor; struct cdev printer_cdev; u8 printer_cdev_open; wait_queue_head_t wait; @@ -97,8 +99,6 @@ static inline struct printer_dev *func_to_printer(struct usb_function *f) return container_of(f, struct printer_dev, function); } -static struct printer_dev usb_printer_gadget; - /*-------------------------------------------------------------------------*/ /* DO NOT REUSE THESE IDs with a protocol-incompatible driver!! Ever!! @@ -1096,6 +1096,7 @@ static int __init printer_func_bind(struct usb_configuration *c, struct usb_ep *in_ep; struct usb_ep *out_ep = NULL; struct usb_request *req; + dev_t devt; int id; int ret; u32 i; @@ -1153,8 +1154,9 @@ autoconf_fail: } /* Setup the sysfs files for the printer gadget. */ - pdev = device_create(usb_gadget_class, NULL, g_printer_devno, - NULL, "g_printer"); + devt = MKDEV(major, dev->minor); + pdev = device_create(usb_gadget_class, NULL, devt, + NULL, "g_printer%d", dev->minor); if (IS_ERR(pdev)) { ERROR(dev, "Failed to create device: g_printer\n"); ret = PTR_ERR(pdev); @@ -1167,7 +1169,7 @@ autoconf_fail: */ cdev_init(&dev->printer_cdev, &printer_io_operations); dev->printer_cdev.owner = THIS_MODULE; - ret = cdev_add(&dev->printer_cdev, g_printer_devno, 1); + ret = cdev_add(&dev->printer_cdev, devt, 1); if (ret) { ERROR(dev, "Failed to open char device\n"); goto fail_cdev_add; @@ -1176,7 +1178,7 @@ autoconf_fail: return 0; fail_cdev_add: - device_destroy(usb_gadget_class, g_printer_devno); + device_destroy(usb_gadget_class, devt); fail_rx_reqs: while (!list_empty(&dev->rx_reqs)) { @@ -1204,7 +1206,7 @@ static void printer_func_unbind(struct usb_configuration *c, dev = func_to_printer(f); - device_destroy(usb_gadget_class, g_printer_devno); + device_destroy(usb_gadget_class, MKDEV(major, dev->minor)); /* Remove Character Device */ cdev_del(&dev->printer_cdev); @@ -1238,6 +1240,7 @@ static void printer_func_unbind(struct usb_configuration *c, printer_req_free(dev->out_ep, req); } usb_free_all_descriptors(f); + kfree(dev); } static int printer_func_set_alt(struct usb_function *f, @@ -1271,14 +1274,21 @@ static struct usb_configuration printer_cfg_driver = { }; static int f_printer_bind_config(struct usb_configuration *c, char *pnp_str, - char *pnp_string, unsigned q_len) + char *pnp_string, unsigned q_len, int minor) { struct printer_dev *dev; int status = -ENOMEM; size_t len; - dev = &usb_printer_gadget; + if (minor >= minors) + return -ENOENT; + + dev = kzalloc(sizeof(*dev), GFP_KERNEL); + if (!dev) + return -ENOMEM; + dev->pnp_string = pnp_string; + dev->minor = minor; dev->function.name = shortname; dev->function.bind = printer_func_bind; @@ -1315,8 +1325,10 @@ static int f_printer_bind_config(struct usb_configuration *c, char *pnp_str, dev->q_len = q_len; status = usb_add_function(c, &dev->function); - if (status) + if (status) { + kfree(dev); return status; + } INFO(dev, "%s, version: " DRIVER_VERSION "\n", driver_desc); return 0; } @@ -1335,43 +1347,51 @@ static int __init printer_do_config(struct usb_configuration *c) printer_cfg_driver.bmAttributes |= USB_CONFIG_ATT_WAKEUP; } - return f_printer_bind_config(c, iPNPstring, pnp_string, QLEN); - + return f_printer_bind_config(c, iPNPstring, pnp_string, QLEN, 0); } -static int gprinter_setup(void) +static int gprinter_setup(int count) { int status; + dev_t devt; usb_gadget_class = class_create(THIS_MODULE, "usb_printer_gadget"); if (IS_ERR(usb_gadget_class)) { status = PTR_ERR(usb_gadget_class); + usb_gadget_class = NULL; pr_err("unable to create usb_gadget class %d\n", status); return status; } - status = alloc_chrdev_region(&g_printer_devno, 0, 1, - "USB printer gadget"); + status = alloc_chrdev_region(&devt, 0, count, "USB printer gadget"); if (status) { pr_err("alloc_chrdev_region %d\n", status); class_destroy(usb_gadget_class); + usb_gadget_class = NULL; + return status; } + major = MAJOR(devt); + minors = count; + return status; } -/* must be called with struct printer_dev's lock_printer_io held */ static void gprinter_cleanup(void) { - unregister_chrdev_region(g_printer_devno, 1); + if (major) { + unregister_chrdev_region(MKDEV(major, 0), minors); + major = minors = 0; + } class_destroy(usb_gadget_class); + usb_gadget_class = NULL; } static int __init printer_bind(struct usb_composite_dev *cdev) { int ret; - ret = gprinter_setup(); + ret = gprinter_setup(PRINTER_MINORS); if (ret) return ret; @@ -1418,9 +1438,7 @@ module_init(init); static void __exit cleanup(void) { - mutex_lock(&usb_printer_gadget.lock_printer_io); usb_composite_unregister(&printer_driver); - mutex_unlock(&usb_printer_gadget.lock_printer_io); } module_exit(cleanup); -- cgit v1.2.3 From b185f01a9ab7af586133be2555298e960237359b Mon Sep 17 00:00:00 2001 From: Andrzej Pietrasiewicz Date: Tue, 3 Mar 2015 10:52:27 +0100 Subject: usb: gadget: printer: factor out f_printer The legacy printer gadget now contains both a reusable printer function and legacy gadget proper implementations interwoven, but logically separate. This patch factors out a reusable f_printer. Signed-off-by: Andrzej Pietrasiewicz Signed-off-by: Felipe Balbi --- drivers/usb/gadget/function/f_printer.c | 1279 +++++++++++++++++++++++++++++++ drivers/usb/gadget/legacy/printer.c | 1255 +----------------------------- 2 files changed, 1285 insertions(+), 1249 deletions(-) create mode 100644 drivers/usb/gadget/function/f_printer.c diff --git a/drivers/usb/gadget/function/f_printer.c b/drivers/usb/gadget/function/f_printer.c new file mode 100644 index 000000000000..0847972b9686 --- /dev/null +++ b/drivers/usb/gadget/function/f_printer.c @@ -0,0 +1,1279 @@ +/* + * f_printer.c - USB printer function driver + * + * Copied from drivers/usb/gadget/legacy/printer.c, + * which was: + * + * printer.c -- Printer gadget driver + * + * Copyright (C) 2003-2005 David Brownell + * Copyright (C) 2006 Craig W. Nadler + * + * 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 +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#define PNP_STRING_LEN 1024 +#define PRINTER_MINORS 4 +#define GET_DEVICE_ID 0 +#define GET_PORT_STATUS 1 +#define SOFT_RESET 2 + +static int major, minors; +static struct class *usb_gadget_class; + +/*-------------------------------------------------------------------------*/ + +struct printer_dev { + spinlock_t lock; /* lock this structure */ + /* lock buffer lists during read/write calls */ + struct mutex lock_printer_io; + struct usb_gadget *gadget; + s8 interface; + struct usb_ep *in_ep, *out_ep; + + struct list_head rx_reqs; /* List of free RX structs */ + struct list_head rx_reqs_active; /* List of Active RX xfers */ + struct list_head rx_buffers; /* List of completed xfers */ + /* wait until there is data to be read. */ + wait_queue_head_t rx_wait; + struct list_head tx_reqs; /* List of free TX structs */ + struct list_head tx_reqs_active; /* List of Active TX xfers */ + /* Wait until there are write buffers available to use. */ + wait_queue_head_t tx_wait; + /* Wait until all write buffers have been sent. */ + wait_queue_head_t tx_flush_wait; + struct usb_request *current_rx_req; + size_t current_rx_bytes; + u8 *current_rx_buf; + u8 printer_status; + u8 reset_printer; + int minor; + struct cdev printer_cdev; + u8 printer_cdev_open; + wait_queue_head_t wait; + unsigned q_len; + char *pnp_string; /* We don't own memory! */ + struct usb_function function; +}; + +static inline struct printer_dev *func_to_printer(struct usb_function *f) +{ + return container_of(f, struct printer_dev, function); +} + +/*-------------------------------------------------------------------------*/ + +/* + * DESCRIPTORS ... most are static, but strings and (full) configuration + * descriptors are built on demand. + */ + +/* holds our biggest descriptor */ +#define USB_DESC_BUFSIZE 256 +#define USB_BUFSIZE 8192 + +static struct usb_interface_descriptor intf_desc = { + .bLength = sizeof(intf_desc), + .bDescriptorType = USB_DT_INTERFACE, + .bNumEndpoints = 2, + .bInterfaceClass = USB_CLASS_PRINTER, + .bInterfaceSubClass = 1, /* Printer Sub-Class */ + .bInterfaceProtocol = 2, /* Bi-Directional */ + .iInterface = 0 +}; + +static struct usb_endpoint_descriptor fs_ep_in_desc = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + .bEndpointAddress = USB_DIR_IN, + .bmAttributes = USB_ENDPOINT_XFER_BULK +}; + +static struct usb_endpoint_descriptor fs_ep_out_desc = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + .bEndpointAddress = USB_DIR_OUT, + .bmAttributes = USB_ENDPOINT_XFER_BULK +}; + +static struct usb_descriptor_header *fs_printer_function[] = { + (struct usb_descriptor_header *) &intf_desc, + (struct usb_descriptor_header *) &fs_ep_in_desc, + (struct usb_descriptor_header *) &fs_ep_out_desc, + NULL +}; + +/* + * usb 2.0 devices need to expose both high speed and full speed + * descriptors, unless they only run at full speed. + */ + +static struct usb_endpoint_descriptor hs_ep_in_desc = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + .bmAttributes = USB_ENDPOINT_XFER_BULK, + .wMaxPacketSize = cpu_to_le16(512) +}; + +static struct usb_endpoint_descriptor hs_ep_out_desc = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + .bmAttributes = USB_ENDPOINT_XFER_BULK, + .wMaxPacketSize = cpu_to_le16(512) +}; + +static struct usb_qualifier_descriptor dev_qualifier = { + .bLength = sizeof(dev_qualifier), + .bDescriptorType = USB_DT_DEVICE_QUALIFIER, + .bcdUSB = cpu_to_le16(0x0200), + .bDeviceClass = USB_CLASS_PRINTER, + .bNumConfigurations = 1 +}; + +static struct usb_descriptor_header *hs_printer_function[] = { + (struct usb_descriptor_header *) &intf_desc, + (struct usb_descriptor_header *) &hs_ep_in_desc, + (struct usb_descriptor_header *) &hs_ep_out_desc, + NULL +}; + +/* + * Added endpoint descriptors for 3.0 devices + */ + +static struct usb_endpoint_descriptor ss_ep_in_desc = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + .bmAttributes = USB_ENDPOINT_XFER_BULK, + .wMaxPacketSize = cpu_to_le16(1024), +}; + +static struct usb_ss_ep_comp_descriptor ss_ep_in_comp_desc = { + .bLength = sizeof(ss_ep_in_comp_desc), + .bDescriptorType = USB_DT_SS_ENDPOINT_COMP, +}; + +static struct usb_endpoint_descriptor ss_ep_out_desc = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + .bmAttributes = USB_ENDPOINT_XFER_BULK, + .wMaxPacketSize = cpu_to_le16(1024), +}; + +static struct usb_ss_ep_comp_descriptor ss_ep_out_comp_desc = { + .bLength = sizeof(ss_ep_out_comp_desc), + .bDescriptorType = USB_DT_SS_ENDPOINT_COMP, +}; + +static struct usb_descriptor_header *ss_printer_function[] = { + (struct usb_descriptor_header *) &intf_desc, + (struct usb_descriptor_header *) &ss_ep_in_desc, + (struct usb_descriptor_header *) &ss_ep_in_comp_desc, + (struct usb_descriptor_header *) &ss_ep_out_desc, + (struct usb_descriptor_header *) &ss_ep_out_comp_desc, + NULL +}; + +/* maxpacket and other transfer characteristics vary by speed. */ +static inline struct usb_endpoint_descriptor *ep_desc(struct usb_gadget *gadget, + struct usb_endpoint_descriptor *fs, + struct usb_endpoint_descriptor *hs, + struct usb_endpoint_descriptor *ss) +{ + switch (gadget->speed) { + case USB_SPEED_SUPER: + return ss; + case USB_SPEED_HIGH: + return hs; + default: + return fs; + } +} + +/*-------------------------------------------------------------------------*/ + +static struct usb_request * +printer_req_alloc(struct usb_ep *ep, unsigned len, gfp_t gfp_flags) +{ + struct usb_request *req; + + req = usb_ep_alloc_request(ep, gfp_flags); + + if (req != NULL) { + req->length = len; + req->buf = kmalloc(len, gfp_flags); + if (req->buf == NULL) { + usb_ep_free_request(ep, req); + return NULL; + } + } + + return req; +} + +static void +printer_req_free(struct usb_ep *ep, struct usb_request *req) +{ + if (ep != NULL && req != NULL) { + kfree(req->buf); + usb_ep_free_request(ep, req); + } +} + +/*-------------------------------------------------------------------------*/ + +static void rx_complete(struct usb_ep *ep, struct usb_request *req) +{ + struct printer_dev *dev = ep->driver_data; + int status = req->status; + unsigned long flags; + + spin_lock_irqsave(&dev->lock, flags); + + list_del_init(&req->list); /* Remode from Active List */ + + switch (status) { + + /* normal completion */ + case 0: + if (req->actual > 0) { + list_add_tail(&req->list, &dev->rx_buffers); + DBG(dev, "G_Printer : rx length %d\n", req->actual); + } else { + list_add(&req->list, &dev->rx_reqs); + } + break; + + /* software-driven interface shutdown */ + case -ECONNRESET: /* unlink */ + case -ESHUTDOWN: /* disconnect etc */ + VDBG(dev, "rx shutdown, code %d\n", status); + list_add(&req->list, &dev->rx_reqs); + break; + + /* for hardware automagic (such as pxa) */ + case -ECONNABORTED: /* endpoint reset */ + DBG(dev, "rx %s reset\n", ep->name); + list_add(&req->list, &dev->rx_reqs); + break; + + /* data overrun */ + case -EOVERFLOW: + /* FALLTHROUGH */ + + default: + DBG(dev, "rx status %d\n", status); + list_add(&req->list, &dev->rx_reqs); + break; + } + + wake_up_interruptible(&dev->rx_wait); + spin_unlock_irqrestore(&dev->lock, flags); +} + +static void tx_complete(struct usb_ep *ep, struct usb_request *req) +{ + struct printer_dev *dev = ep->driver_data; + + switch (req->status) { + default: + VDBG(dev, "tx err %d\n", req->status); + /* FALLTHROUGH */ + case -ECONNRESET: /* unlink */ + case -ESHUTDOWN: /* disconnect etc */ + break; + case 0: + break; + } + + spin_lock(&dev->lock); + /* Take the request struct off the active list and put it on the + * free list. + */ + list_del_init(&req->list); + list_add(&req->list, &dev->tx_reqs); + wake_up_interruptible(&dev->tx_wait); + if (likely(list_empty(&dev->tx_reqs_active))) + wake_up_interruptible(&dev->tx_flush_wait); + + spin_unlock(&dev->lock); +} + +/*-------------------------------------------------------------------------*/ + +static int +printer_open(struct inode *inode, struct file *fd) +{ + struct printer_dev *dev; + unsigned long flags; + int ret = -EBUSY; + + dev = container_of(inode->i_cdev, struct printer_dev, printer_cdev); + + spin_lock_irqsave(&dev->lock, flags); + + if (!dev->printer_cdev_open) { + dev->printer_cdev_open = 1; + fd->private_data = dev; + ret = 0; + /* Change the printer status to show that it's on-line. */ + dev->printer_status |= PRINTER_SELECTED; + } + + spin_unlock_irqrestore(&dev->lock, flags); + + DBG(dev, "printer_open returned %x\n", ret); + return ret; +} + +static int +printer_close(struct inode *inode, struct file *fd) +{ + struct printer_dev *dev = fd->private_data; + unsigned long flags; + + spin_lock_irqsave(&dev->lock, flags); + dev->printer_cdev_open = 0; + fd->private_data = NULL; + /* Change printer status to show that the printer is off-line. */ + dev->printer_status &= ~PRINTER_SELECTED; + spin_unlock_irqrestore(&dev->lock, flags); + + DBG(dev, "printer_close\n"); + + return 0; +} + +/* This function must be called with interrupts turned off. */ +static void +setup_rx_reqs(struct printer_dev *dev) +{ + struct usb_request *req; + + while (likely(!list_empty(&dev->rx_reqs))) { + int error; + + req = container_of(dev->rx_reqs.next, + struct usb_request, list); + list_del_init(&req->list); + + /* The USB Host sends us whatever amount of data it wants to + * so we always set the length field to the full USB_BUFSIZE. + * If the amount of data is more than the read() caller asked + * for it will be stored in the request buffer until it is + * asked for by read(). + */ + req->length = USB_BUFSIZE; + req->complete = rx_complete; + + /* here, we unlock, and only unlock, to avoid deadlock. */ + spin_unlock(&dev->lock); + error = usb_ep_queue(dev->out_ep, req, GFP_ATOMIC); + spin_lock(&dev->lock); + if (error) { + DBG(dev, "rx submit --> %d\n", error); + list_add(&req->list, &dev->rx_reqs); + break; + } + /* if the req is empty, then add it into dev->rx_reqs_active. */ + else if (list_empty(&req->list)) + list_add(&req->list, &dev->rx_reqs_active); + } +} + +static ssize_t +printer_read(struct file *fd, char __user *buf, size_t len, loff_t *ptr) +{ + struct printer_dev *dev = fd->private_data; + unsigned long flags; + size_t size; + size_t bytes_copied; + struct usb_request *req; + /* This is a pointer to the current USB rx request. */ + struct usb_request *current_rx_req; + /* This is the number of bytes in the current rx buffer. */ + size_t current_rx_bytes; + /* This is a pointer to the current rx buffer. */ + u8 *current_rx_buf; + + if (len == 0) + return -EINVAL; + + DBG(dev, "printer_read trying to read %d bytes\n", (int)len); + + mutex_lock(&dev->lock_printer_io); + spin_lock_irqsave(&dev->lock, flags); + + /* We will use this flag later to check if a printer reset happened + * after we turn interrupts back on. + */ + dev->reset_printer = 0; + + setup_rx_reqs(dev); + + bytes_copied = 0; + current_rx_req = dev->current_rx_req; + current_rx_bytes = dev->current_rx_bytes; + current_rx_buf = dev->current_rx_buf; + dev->current_rx_req = NULL; + dev->current_rx_bytes = 0; + dev->current_rx_buf = NULL; + + /* Check if there is any data in the read buffers. Please note that + * current_rx_bytes is the number of bytes in the current rx buffer. + * If it is zero then check if there are any other rx_buffers that + * are on the completed list. We are only out of data if all rx + * buffers are empty. + */ + if ((current_rx_bytes == 0) && + (likely(list_empty(&dev->rx_buffers)))) { + /* Turn interrupts back on before sleeping. */ + spin_unlock_irqrestore(&dev->lock, flags); + + /* + * If no data is available check if this is a NON-Blocking + * call or not. + */ + if (fd->f_flags & (O_NONBLOCK|O_NDELAY)) { + mutex_unlock(&dev->lock_printer_io); + return -EAGAIN; + } + + /* Sleep until data is available */ + wait_event_interruptible(dev->rx_wait, + (likely(!list_empty(&dev->rx_buffers)))); + spin_lock_irqsave(&dev->lock, flags); + } + + /* We have data to return then copy it to the caller's buffer.*/ + while ((current_rx_bytes || likely(!list_empty(&dev->rx_buffers))) + && len) { + if (current_rx_bytes == 0) { + req = container_of(dev->rx_buffers.next, + struct usb_request, list); + list_del_init(&req->list); + + if (req->actual && req->buf) { + current_rx_req = req; + current_rx_bytes = req->actual; + current_rx_buf = req->buf; + } else { + list_add(&req->list, &dev->rx_reqs); + continue; + } + } + + /* Don't leave irqs off while doing memory copies */ + spin_unlock_irqrestore(&dev->lock, flags); + + if (len > current_rx_bytes) + size = current_rx_bytes; + else + size = len; + + size -= copy_to_user(buf, current_rx_buf, size); + bytes_copied += size; + len -= size; + buf += size; + + spin_lock_irqsave(&dev->lock, flags); + + /* We've disconnected or reset so return. */ + if (dev->reset_printer) { + list_add(¤t_rx_req->list, &dev->rx_reqs); + spin_unlock_irqrestore(&dev->lock, flags); + mutex_unlock(&dev->lock_printer_io); + return -EAGAIN; + } + + /* If we not returning all the data left in this RX request + * buffer then adjust the amount of data left in the buffer. + * Othewise if we are done with this RX request buffer then + * requeue it to get any incoming data from the USB host. + */ + if (size < current_rx_bytes) { + current_rx_bytes -= size; + current_rx_buf += size; + } else { + list_add(¤t_rx_req->list, &dev->rx_reqs); + current_rx_bytes = 0; + current_rx_buf = NULL; + current_rx_req = NULL; + } + } + + dev->current_rx_req = current_rx_req; + dev->current_rx_bytes = current_rx_bytes; + dev->current_rx_buf = current_rx_buf; + + spin_unlock_irqrestore(&dev->lock, flags); + mutex_unlock(&dev->lock_printer_io); + + DBG(dev, "printer_read returned %d bytes\n", (int)bytes_copied); + + if (bytes_copied) + return bytes_copied; + else + return -EAGAIN; +} + +static ssize_t +printer_write(struct file *fd, const char __user *buf, size_t len, loff_t *ptr) +{ + struct printer_dev *dev = fd->private_data; + unsigned long flags; + size_t size; /* Amount of data in a TX request. */ + size_t bytes_copied = 0; + struct usb_request *req; + + DBG(dev, "printer_write trying to send %d bytes\n", (int)len); + + if (len == 0) + return -EINVAL; + + mutex_lock(&dev->lock_printer_io); + spin_lock_irqsave(&dev->lock, flags); + + /* Check if a printer reset happens while we have interrupts on */ + dev->reset_printer = 0; + + /* Check if there is any available write buffers */ + if (likely(list_empty(&dev->tx_reqs))) { + /* Turn interrupts back on before sleeping. */ + spin_unlock_irqrestore(&dev->lock, flags); + + /* + * If write buffers are available check if this is + * a NON-Blocking call or not. + */ + if (fd->f_flags & (O_NONBLOCK|O_NDELAY)) { + mutex_unlock(&dev->lock_printer_io); + return -EAGAIN; + } + + /* Sleep until a write buffer is available */ + wait_event_interruptible(dev->tx_wait, + (likely(!list_empty(&dev->tx_reqs)))); + spin_lock_irqsave(&dev->lock, flags); + } + + while (likely(!list_empty(&dev->tx_reqs)) && len) { + + if (len > USB_BUFSIZE) + size = USB_BUFSIZE; + else + size = len; + + req = container_of(dev->tx_reqs.next, struct usb_request, + list); + list_del_init(&req->list); + + req->complete = tx_complete; + req->length = size; + + /* Check if we need to send a zero length packet. */ + if (len > size) + /* They will be more TX requests so no yet. */ + req->zero = 0; + else + /* If the data amount is not a multiple of the + * maxpacket size then send a zero length packet. + */ + req->zero = ((len % dev->in_ep->maxpacket) == 0); + + /* Don't leave irqs off while doing memory copies */ + spin_unlock_irqrestore(&dev->lock, flags); + + if (copy_from_user(req->buf, buf, size)) { + list_add(&req->list, &dev->tx_reqs); + mutex_unlock(&dev->lock_printer_io); + return bytes_copied; + } + + bytes_copied += size; + len -= size; + buf += size; + + spin_lock_irqsave(&dev->lock, flags); + + /* We've disconnected or reset so free the req and buffer */ + if (dev->reset_printer) { + list_add(&req->list, &dev->tx_reqs); + spin_unlock_irqrestore(&dev->lock, flags); + mutex_unlock(&dev->lock_printer_io); + return -EAGAIN; + } + + if (usb_ep_queue(dev->in_ep, req, GFP_ATOMIC)) { + list_add(&req->list, &dev->tx_reqs); + spin_unlock_irqrestore(&dev->lock, flags); + mutex_unlock(&dev->lock_printer_io); + return -EAGAIN; + } + + list_add(&req->list, &dev->tx_reqs_active); + + } + + spin_unlock_irqrestore(&dev->lock, flags); + mutex_unlock(&dev->lock_printer_io); + + DBG(dev, "printer_write sent %d bytes\n", (int)bytes_copied); + + if (bytes_copied) + return bytes_copied; + else + return -EAGAIN; +} + +static int +printer_fsync(struct file *fd, loff_t start, loff_t end, int datasync) +{ + struct printer_dev *dev = fd->private_data; + struct inode *inode = file_inode(fd); + unsigned long flags; + int tx_list_empty; + + mutex_lock(&inode->i_mutex); + spin_lock_irqsave(&dev->lock, flags); + tx_list_empty = (likely(list_empty(&dev->tx_reqs))); + spin_unlock_irqrestore(&dev->lock, flags); + + if (!tx_list_empty) { + /* Sleep until all data has been sent */ + wait_event_interruptible(dev->tx_flush_wait, + (likely(list_empty(&dev->tx_reqs_active)))); + } + mutex_unlock(&inode->i_mutex); + + return 0; +} + +static unsigned int +printer_poll(struct file *fd, poll_table *wait) +{ + struct printer_dev *dev = fd->private_data; + unsigned long flags; + int status = 0; + + mutex_lock(&dev->lock_printer_io); + spin_lock_irqsave(&dev->lock, flags); + setup_rx_reqs(dev); + spin_unlock_irqrestore(&dev->lock, flags); + mutex_unlock(&dev->lock_printer_io); + + poll_wait(fd, &dev->rx_wait, wait); + poll_wait(fd, &dev->tx_wait, wait); + + spin_lock_irqsave(&dev->lock, flags); + if (likely(!list_empty(&dev->tx_reqs))) + status |= POLLOUT | POLLWRNORM; + + if (likely(dev->current_rx_bytes) || + likely(!list_empty(&dev->rx_buffers))) + status |= POLLIN | POLLRDNORM; + + spin_unlock_irqrestore(&dev->lock, flags); + + return status; +} + +static long +printer_ioctl(struct file *fd, unsigned int code, unsigned long arg) +{ + struct printer_dev *dev = fd->private_data; + unsigned long flags; + int status = 0; + + DBG(dev, "printer_ioctl: cmd=0x%4.4x, arg=%lu\n", code, arg); + + /* handle ioctls */ + + spin_lock_irqsave(&dev->lock, flags); + + switch (code) { + case GADGET_GET_PRINTER_STATUS: + status = (int)dev->printer_status; + break; + case GADGET_SET_PRINTER_STATUS: + dev->printer_status = (u8)arg; + break; + default: + /* could not handle ioctl */ + DBG(dev, "printer_ioctl: ERROR cmd=0x%4.4xis not supported\n", + code); + status = -ENOTTY; + } + + spin_unlock_irqrestore(&dev->lock, flags); + + return status; +} + +/* used after endpoint configuration */ +static const struct file_operations printer_io_operations = { + .owner = THIS_MODULE, + .open = printer_open, + .read = printer_read, + .write = printer_write, + .fsync = printer_fsync, + .poll = printer_poll, + .unlocked_ioctl = printer_ioctl, + .release = printer_close, + .llseek = noop_llseek, +}; + +/*-------------------------------------------------------------------------*/ + +static int +set_printer_interface(struct printer_dev *dev) +{ + int result = 0; + + dev->in_ep->desc = ep_desc(dev->gadget, &fs_ep_in_desc, &hs_ep_in_desc, + &ss_ep_in_desc); + dev->in_ep->driver_data = dev; + + dev->out_ep->desc = ep_desc(dev->gadget, &fs_ep_out_desc, + &hs_ep_out_desc, &ss_ep_out_desc); + dev->out_ep->driver_data = dev; + + result = usb_ep_enable(dev->in_ep); + if (result != 0) { + DBG(dev, "enable %s --> %d\n", dev->in_ep->name, result); + goto done; + } + + result = usb_ep_enable(dev->out_ep); + if (result != 0) { + DBG(dev, "enable %s --> %d\n", dev->in_ep->name, result); + goto done; + } + +done: + /* on error, disable any endpoints */ + if (result != 0) { + (void) usb_ep_disable(dev->in_ep); + (void) usb_ep_disable(dev->out_ep); + dev->in_ep->desc = NULL; + dev->out_ep->desc = NULL; + } + + /* caller is responsible for cleanup on error */ + return result; +} + +static void printer_reset_interface(struct printer_dev *dev) +{ + if (dev->interface < 0) + return; + + DBG(dev, "%s\n", __func__); + + if (dev->in_ep->desc) + usb_ep_disable(dev->in_ep); + + if (dev->out_ep->desc) + usb_ep_disable(dev->out_ep); + + dev->in_ep->desc = NULL; + dev->out_ep->desc = NULL; + dev->interface = -1; +} + +/* Change our operational Interface. */ +static int set_interface(struct printer_dev *dev, unsigned number) +{ + int result = 0; + + /* Free the current interface */ + printer_reset_interface(dev); + + result = set_printer_interface(dev); + if (result) + printer_reset_interface(dev); + else + dev->interface = number; + + if (!result) + INFO(dev, "Using interface %x\n", number); + + return result; +} + +static void printer_soft_reset(struct printer_dev *dev) +{ + struct usb_request *req; + + INFO(dev, "Received Printer Reset Request\n"); + + if (usb_ep_disable(dev->in_ep)) + DBG(dev, "Failed to disable USB in_ep\n"); + if (usb_ep_disable(dev->out_ep)) + DBG(dev, "Failed to disable USB out_ep\n"); + + if (dev->current_rx_req != NULL) { + list_add(&dev->current_rx_req->list, &dev->rx_reqs); + dev->current_rx_req = NULL; + } + dev->current_rx_bytes = 0; + dev->current_rx_buf = NULL; + dev->reset_printer = 1; + + while (likely(!(list_empty(&dev->rx_buffers)))) { + req = container_of(dev->rx_buffers.next, struct usb_request, + list); + list_del_init(&req->list); + list_add(&req->list, &dev->rx_reqs); + } + + while (likely(!(list_empty(&dev->rx_reqs_active)))) { + req = container_of(dev->rx_buffers.next, struct usb_request, + list); + list_del_init(&req->list); + list_add(&req->list, &dev->rx_reqs); + } + + while (likely(!(list_empty(&dev->tx_reqs_active)))) { + req = container_of(dev->tx_reqs_active.next, + struct usb_request, list); + list_del_init(&req->list); + list_add(&req->list, &dev->tx_reqs); + } + + if (usb_ep_enable(dev->in_ep)) + DBG(dev, "Failed to enable USB in_ep\n"); + if (usb_ep_enable(dev->out_ep)) + DBG(dev, "Failed to enable USB out_ep\n"); + + wake_up_interruptible(&dev->rx_wait); + wake_up_interruptible(&dev->tx_wait); + wake_up_interruptible(&dev->tx_flush_wait); +} + +/*-------------------------------------------------------------------------*/ + +static bool gprinter_req_match(struct usb_function *f, + const struct usb_ctrlrequest *ctrl) +{ + struct printer_dev *dev = func_to_printer(f); + u16 w_index = le16_to_cpu(ctrl->wIndex); + u16 w_value = le16_to_cpu(ctrl->wValue); + u16 w_length = le16_to_cpu(ctrl->wLength); + + if ((ctrl->bRequestType & USB_RECIP_MASK) != USB_RECIP_INTERFACE || + (ctrl->bRequestType & USB_TYPE_MASK) != USB_TYPE_CLASS) + return false; + + switch (ctrl->bRequest) { + case GET_DEVICE_ID: + w_index >>= 8; + if (w_length <= PNP_STRING_LEN && + (USB_DIR_IN & ctrl->bRequestType)) + break; + return false; + case GET_PORT_STATUS: + if (!w_value && w_length == 1 && + (USB_DIR_IN & ctrl->bRequestType)) + break; + return false; + case SOFT_RESET: + if (!w_value && !w_length && + (USB_DIR_OUT & ctrl->bRequestType)) + break; + /* fall through */ + default: + return false; + } + return w_index == dev->interface; +} + +/* + * The setup() callback implements all the ep0 functionality that's not + * handled lower down. + */ +static int printer_func_setup(struct usb_function *f, + const struct usb_ctrlrequest *ctrl) +{ + struct printer_dev *dev = func_to_printer(f); + struct usb_composite_dev *cdev = f->config->cdev; + struct usb_request *req = cdev->req; + int value = -EOPNOTSUPP; + u16 wIndex = le16_to_cpu(ctrl->wIndex); + u16 wValue = le16_to_cpu(ctrl->wValue); + u16 wLength = le16_to_cpu(ctrl->wLength); + + DBG(dev, "ctrl req%02x.%02x v%04x i%04x l%d\n", + ctrl->bRequestType, ctrl->bRequest, wValue, wIndex, wLength); + + switch (ctrl->bRequestType&USB_TYPE_MASK) { + case USB_TYPE_CLASS: + switch (ctrl->bRequest) { + case GET_DEVICE_ID: /* Get the IEEE-1284 PNP String */ + /* Only one printer interface is supported. */ + if ((wIndex>>8) != dev->interface) + break; + + value = (dev->pnp_string[0] << 8) | dev->pnp_string[1]; + memcpy(req->buf, dev->pnp_string, value); + DBG(dev, "1284 PNP String: %x %s\n", value, + &dev->pnp_string[2]); + break; + + case GET_PORT_STATUS: /* Get Port Status */ + /* Only one printer interface is supported. */ + if (wIndex != dev->interface) + break; + + *(u8 *)req->buf = dev->printer_status; + value = min_t(u16, wLength, 1); + break; + + case SOFT_RESET: /* Soft Reset */ + /* Only one printer interface is supported. */ + if (wIndex != dev->interface) + break; + + printer_soft_reset(dev); + + value = 0; + break; + + default: + goto unknown; + } + break; + + default: +unknown: + VDBG(dev, + "unknown ctrl req%02x.%02x v%04x i%04x l%d\n", + ctrl->bRequestType, ctrl->bRequest, + wValue, wIndex, wLength); + break; + } + /* host either stalls (value < 0) or reports success */ + if (value >= 0) { + req->length = value; + req->zero = value < wLength; + value = usb_ep_queue(cdev->gadget->ep0, req, GFP_ATOMIC); + if (value < 0) { + ERROR(dev, "%s:%d Error!\n", __func__, __LINE__); + req->status = 0; + } + } + return value; +} + +static int __init printer_func_bind(struct usb_configuration *c, + struct usb_function *f) +{ + struct usb_gadget *gadget = c->cdev->gadget; + struct printer_dev *dev = func_to_printer(f); + struct device *pdev; + struct usb_composite_dev *cdev = c->cdev; + struct usb_ep *in_ep; + struct usb_ep *out_ep = NULL; + struct usb_request *req; + dev_t devt; + int id; + int ret; + u32 i; + + id = usb_interface_id(c, f); + if (id < 0) + return id; + intf_desc.bInterfaceNumber = id; + + /* finish hookup to lower layer ... */ + dev->gadget = gadget; + + /* all we really need is bulk IN/OUT */ + in_ep = usb_ep_autoconfig(cdev->gadget, &fs_ep_in_desc); + if (!in_ep) { +autoconf_fail: + dev_err(&cdev->gadget->dev, "can't autoconfigure on %s\n", + cdev->gadget->name); + return -ENODEV; + } + in_ep->driver_data = in_ep; /* claim */ + + out_ep = usb_ep_autoconfig(cdev->gadget, &fs_ep_out_desc); + if (!out_ep) + goto autoconf_fail; + out_ep->driver_data = out_ep; /* claim */ + + /* assumes that all endpoints are dual-speed */ + hs_ep_in_desc.bEndpointAddress = fs_ep_in_desc.bEndpointAddress; + hs_ep_out_desc.bEndpointAddress = fs_ep_out_desc.bEndpointAddress; + ss_ep_in_desc.bEndpointAddress = fs_ep_in_desc.bEndpointAddress; + ss_ep_out_desc.bEndpointAddress = fs_ep_out_desc.bEndpointAddress; + + ret = usb_assign_descriptors(f, fs_printer_function, + hs_printer_function, ss_printer_function); + if (ret) + return ret; + + dev->in_ep = in_ep; + dev->out_ep = out_ep; + + ret = -ENOMEM; + for (i = 0; i < dev->q_len; i++) { + req = printer_req_alloc(dev->in_ep, USB_BUFSIZE, GFP_KERNEL); + if (!req) + goto fail_tx_reqs; + list_add(&req->list, &dev->tx_reqs); + } + + for (i = 0; i < dev->q_len; i++) { + req = printer_req_alloc(dev->out_ep, USB_BUFSIZE, GFP_KERNEL); + if (!req) + goto fail_rx_reqs; + list_add(&req->list, &dev->rx_reqs); + } + + /* Setup the sysfs files for the printer gadget. */ + devt = MKDEV(major, dev->minor); + pdev = device_create(usb_gadget_class, NULL, devt, + NULL, "g_printer%d", dev->minor); + if (IS_ERR(pdev)) { + ERROR(dev, "Failed to create device: g_printer\n"); + ret = PTR_ERR(pdev); + goto fail_rx_reqs; + } + + /* + * Register a character device as an interface to a user mode + * program that handles the printer specific functionality. + */ + cdev_init(&dev->printer_cdev, &printer_io_operations); + dev->printer_cdev.owner = THIS_MODULE; + ret = cdev_add(&dev->printer_cdev, devt, 1); + if (ret) { + ERROR(dev, "Failed to open char device\n"); + goto fail_cdev_add; + } + + return 0; + +fail_cdev_add: + device_destroy(usb_gadget_class, devt); + +fail_rx_reqs: + while (!list_empty(&dev->rx_reqs)) { + req = container_of(dev->rx_reqs.next, struct usb_request, list); + list_del(&req->list); + printer_req_free(dev->out_ep, req); + } + +fail_tx_reqs: + while (!list_empty(&dev->tx_reqs)) { + req = container_of(dev->tx_reqs.next, struct usb_request, list); + list_del(&req->list); + printer_req_free(dev->in_ep, req); + } + + return ret; + +} + +static void printer_func_unbind(struct usb_configuration *c, + struct usb_function *f) +{ + struct printer_dev *dev; + struct usb_request *req; + + dev = func_to_printer(f); + + device_destroy(usb_gadget_class, MKDEV(major, dev->minor)); + + /* Remove Character Device */ + cdev_del(&dev->printer_cdev); + + /* we must already have been disconnected ... no i/o may be active */ + WARN_ON(!list_empty(&dev->tx_reqs_active)); + WARN_ON(!list_empty(&dev->rx_reqs_active)); + + /* Free all memory for this driver. */ + while (!list_empty(&dev->tx_reqs)) { + req = container_of(dev->tx_reqs.next, struct usb_request, + list); + list_del(&req->list); + printer_req_free(dev->in_ep, req); + } + + if (dev->current_rx_req != NULL) + printer_req_free(dev->out_ep, dev->current_rx_req); + + while (!list_empty(&dev->rx_reqs)) { + req = container_of(dev->rx_reqs.next, + struct usb_request, list); + list_del(&req->list); + printer_req_free(dev->out_ep, req); + } + + while (!list_empty(&dev->rx_buffers)) { + req = container_of(dev->rx_buffers.next, + struct usb_request, list); + list_del(&req->list); + printer_req_free(dev->out_ep, req); + } + usb_free_all_descriptors(f); + kfree(dev); +} + +static int printer_func_set_alt(struct usb_function *f, + unsigned intf, unsigned alt) +{ + struct printer_dev *dev = func_to_printer(f); + int ret = -ENOTSUPP; + + if (!alt) + ret = set_interface(dev, intf); + + return ret; +} + +static void printer_func_disable(struct usb_function *f) +{ + struct printer_dev *dev = func_to_printer(f); + unsigned long flags; + + DBG(dev, "%s\n", __func__); + + spin_lock_irqsave(&dev->lock, flags); + printer_reset_interface(dev); + spin_unlock_irqrestore(&dev->lock, flags); +} + +static int f_printer_bind_config(struct usb_configuration *c, char *pnp_str, + char *pnp_string, unsigned q_len, int minor) +{ + struct printer_dev *dev; + int status = -ENOMEM; + size_t len; + + if (minor >= minors) + return -ENOENT; + + dev = kzalloc(sizeof(*dev), GFP_KERNEL); + if (!dev) + return -ENOMEM; + + dev->pnp_string = pnp_string; + dev->minor = minor; + + dev->function.name = shortname; + dev->function.bind = printer_func_bind; + dev->function.setup = printer_func_setup; + dev->function.unbind = printer_func_unbind; + dev->function.set_alt = printer_func_set_alt; + dev->function.disable = printer_func_disable; + dev->function.req_match = gprinter_req_match; + INIT_LIST_HEAD(&dev->tx_reqs); + INIT_LIST_HEAD(&dev->rx_reqs); + INIT_LIST_HEAD(&dev->rx_buffers); + + if (pnp_str) + strlcpy(&dev->pnp_string[2], pnp_str, PNP_STRING_LEN - 2); + + len = strlen(pnp_string); + pnp_string[0] = (len >> 8) & 0xFF; + pnp_string[1] = len & 0xFF; + + spin_lock_init(&dev->lock); + mutex_init(&dev->lock_printer_io); + INIT_LIST_HEAD(&dev->tx_reqs_active); + INIT_LIST_HEAD(&dev->rx_reqs_active); + init_waitqueue_head(&dev->rx_wait); + init_waitqueue_head(&dev->tx_wait); + init_waitqueue_head(&dev->tx_flush_wait); + + dev->interface = -1; + dev->printer_cdev_open = 0; + dev->printer_status = PRINTER_NOT_ERROR; + dev->current_rx_req = NULL; + dev->current_rx_bytes = 0; + dev->current_rx_buf = NULL; + dev->q_len = q_len; + + status = usb_add_function(c, &dev->function); + if (status) { + kfree(dev); + return status; + } + + INFO(dev, "%s, version: " DRIVER_VERSION "\n", driver_desc); + return 0; +} + +static int gprinter_setup(int count) +{ + int status; + dev_t devt; + + usb_gadget_class = class_create(THIS_MODULE, "usb_printer_gadget"); + if (IS_ERR(usb_gadget_class)) { + status = PTR_ERR(usb_gadget_class); + usb_gadget_class = NULL; + pr_err("unable to create usb_gadget class %d\n", status); + return status; + } + + status = alloc_chrdev_region(&devt, 0, count, "USB printer gadget"); + if (status) { + pr_err("alloc_chrdev_region %d\n", status); + class_destroy(usb_gadget_class); + usb_gadget_class = NULL; + return status; + } + + major = MAJOR(devt); + minors = count; + + return status; +} + +static void gprinter_cleanup(void) +{ + if (major) { + unregister_chrdev_region(MKDEV(major, 0), minors); + major = minors = 0; + } + class_destroy(usb_gadget_class); + usb_gadget_class = NULL; +} diff --git a/drivers/usb/gadget/legacy/printer.c b/drivers/usb/gadget/legacy/printer.c index d1f85f81975f..4d926d08df02 100644 --- a/drivers/usb/gadget/legacy/printer.c +++ b/drivers/usb/gadget/legacy/printer.c @@ -12,29 +12,7 @@ #include #include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - #include -#include -#include -#include -#include #include #include @@ -46,58 +24,16 @@ USB_GADGET_COMPOSITE_OPTIONS(); #define DRIVER_DESC "Printer Gadget" -#define DRIVER_VERSION "2007 OCT 06" -#define GET_DEVICE_ID 0 -#define GET_PORT_STATUS 1 -#define SOFT_RESET 2 - -#define PRINTER_MINORS 4 +#define DRIVER_VERSION "2015 FEB 17" static const char shortname [] = "printer"; static const char driver_desc [] = DRIVER_DESC; -static int major, minors; -static struct class *usb_gadget_class; - -/*-------------------------------------------------------------------------*/ - -struct printer_dev { - spinlock_t lock; /* lock this structure */ - /* lock buffer lists during read/write calls */ - struct mutex lock_printer_io; - struct usb_gadget *gadget; - s8 interface; - struct usb_ep *in_ep, *out_ep; - - struct list_head rx_reqs; /* List of free RX structs */ - struct list_head rx_reqs_active; /* List of Active RX xfers */ - struct list_head rx_buffers; /* List of completed xfers */ - /* wait until there is data to be read. */ - wait_queue_head_t rx_wait; - struct list_head tx_reqs; /* List of free TX structs */ - struct list_head tx_reqs_active; /* List of Active TX xfers */ - /* Wait until there are write buffers available to use. */ - wait_queue_head_t tx_wait; - /* Wait until all write buffers have been sent. */ - wait_queue_head_t tx_flush_wait; - struct usb_request *current_rx_req; - size_t current_rx_bytes; - u8 *current_rx_buf; - u8 printer_status; - u8 reset_printer; - int minor; - struct cdev printer_cdev; - u8 printer_cdev_open; - wait_queue_head_t wait; - unsigned q_len; - char *pnp_string; /* We don't own memory! */ - struct usb_function function; -}; - -static inline struct printer_dev *func_to_printer(struct usb_function *f) -{ - return container_of(f, struct printer_dev, function); -} +/* + * This will be changed when f_printer is converted + * to the new function interface. + */ +#include "f_printer.c" /*-------------------------------------------------------------------------*/ @@ -135,10 +71,6 @@ module_param(qlen, uint, S_IRUGO|S_IWUSR); * descriptors are built on demand. */ -/* holds our biggest descriptor */ -#define USB_DESC_BUFSIZE 256 -#define USB_BUFSIZE 8192 - static struct usb_device_descriptor device_desc = { .bLength = sizeof device_desc, .bDescriptorType = USB_DT_DEVICE, @@ -151,108 +83,6 @@ static struct usb_device_descriptor device_desc = { .bNumConfigurations = 1 }; -static struct usb_interface_descriptor intf_desc = { - .bLength = sizeof intf_desc, - .bDescriptorType = USB_DT_INTERFACE, - .bNumEndpoints = 2, - .bInterfaceClass = USB_CLASS_PRINTER, - .bInterfaceSubClass = 1, /* Printer Sub-Class */ - .bInterfaceProtocol = 2, /* Bi-Directional */ - .iInterface = 0 -}; - -static struct usb_endpoint_descriptor fs_ep_in_desc = { - .bLength = USB_DT_ENDPOINT_SIZE, - .bDescriptorType = USB_DT_ENDPOINT, - .bEndpointAddress = USB_DIR_IN, - .bmAttributes = USB_ENDPOINT_XFER_BULK -}; - -static struct usb_endpoint_descriptor fs_ep_out_desc = { - .bLength = USB_DT_ENDPOINT_SIZE, - .bDescriptorType = USB_DT_ENDPOINT, - .bEndpointAddress = USB_DIR_OUT, - .bmAttributes = USB_ENDPOINT_XFER_BULK -}; - -static struct usb_descriptor_header *fs_printer_function[] = { - (struct usb_descriptor_header *) &intf_desc, - (struct usb_descriptor_header *) &fs_ep_in_desc, - (struct usb_descriptor_header *) &fs_ep_out_desc, - NULL -}; - -/* - * usb 2.0 devices need to expose both high speed and full speed - * descriptors, unless they only run at full speed. - */ - -static struct usb_endpoint_descriptor hs_ep_in_desc = { - .bLength = USB_DT_ENDPOINT_SIZE, - .bDescriptorType = USB_DT_ENDPOINT, - .bmAttributes = USB_ENDPOINT_XFER_BULK, - .wMaxPacketSize = cpu_to_le16(512) -}; - -static struct usb_endpoint_descriptor hs_ep_out_desc = { - .bLength = USB_DT_ENDPOINT_SIZE, - .bDescriptorType = USB_DT_ENDPOINT, - .bmAttributes = USB_ENDPOINT_XFER_BULK, - .wMaxPacketSize = cpu_to_le16(512) -}; - -static struct usb_qualifier_descriptor dev_qualifier = { - .bLength = sizeof dev_qualifier, - .bDescriptorType = USB_DT_DEVICE_QUALIFIER, - .bcdUSB = cpu_to_le16(0x0200), - .bDeviceClass = USB_CLASS_PRINTER, - .bNumConfigurations = 1 -}; - -static struct usb_descriptor_header *hs_printer_function[] = { - (struct usb_descriptor_header *) &intf_desc, - (struct usb_descriptor_header *) &hs_ep_in_desc, - (struct usb_descriptor_header *) &hs_ep_out_desc, - NULL -}; - -/* - * Added endpoint descriptors for 3.0 devices - */ - -static struct usb_endpoint_descriptor ss_ep_in_desc = { - .bLength = USB_DT_ENDPOINT_SIZE, - .bDescriptorType = USB_DT_ENDPOINT, - .bmAttributes = USB_ENDPOINT_XFER_BULK, - .wMaxPacketSize = cpu_to_le16(1024), -}; - -static struct usb_ss_ep_comp_descriptor ss_ep_in_comp_desc = { - .bLength = sizeof(ss_ep_in_comp_desc), - .bDescriptorType = USB_DT_SS_ENDPOINT_COMP, -}; - -static struct usb_endpoint_descriptor ss_ep_out_desc = { - .bLength = USB_DT_ENDPOINT_SIZE, - .bDescriptorType = USB_DT_ENDPOINT, - .bmAttributes = USB_ENDPOINT_XFER_BULK, - .wMaxPacketSize = cpu_to_le16(1024), -}; - -static struct usb_ss_ep_comp_descriptor ss_ep_out_comp_desc = { - .bLength = sizeof(ss_ep_out_comp_desc), - .bDescriptorType = USB_DT_SS_ENDPOINT_COMP, -}; - -static struct usb_descriptor_header *ss_printer_function[] = { - (struct usb_descriptor_header *) &intf_desc, - (struct usb_descriptor_header *) &ss_ep_in_desc, - (struct usb_descriptor_header *) &ss_ep_in_comp_desc, - (struct usb_descriptor_header *) &ss_ep_out_desc, - (struct usb_descriptor_header *) &ss_ep_out_comp_desc, - NULL -}; - static struct usb_otg_descriptor otg_descriptor = { .bLength = sizeof otg_descriptor, .bDescriptorType = USB_DT_OTG, @@ -264,28 +94,10 @@ static const struct usb_descriptor_header *otg_desc[] = { NULL, }; -/* maxpacket and other transfer characteristics vary by speed. */ -static inline struct usb_endpoint_descriptor *ep_desc(struct usb_gadget *gadget, - struct usb_endpoint_descriptor *fs, - struct usb_endpoint_descriptor *hs, - struct usb_endpoint_descriptor *ss) -{ - switch (gadget->speed) { - case USB_SPEED_SUPER: - return ss; - case USB_SPEED_HIGH: - return hs; - default: - return fs; - } -} - /*-------------------------------------------------------------------------*/ /* descriptors that are built on-demand */ -#define PNP_STRING_LEN 1024 - static char product_desc [40] = DRIVER_DESC; static char serial_num [40] = "1"; static char pnp_string[PNP_STRING_LEN] = @@ -309,1030 +121,12 @@ static struct usb_gadget_strings *dev_strings[] = { NULL, }; -/*-------------------------------------------------------------------------*/ - -static struct usb_request * -printer_req_alloc(struct usb_ep *ep, unsigned len, gfp_t gfp_flags) -{ - struct usb_request *req; - - req = usb_ep_alloc_request(ep, gfp_flags); - - if (req != NULL) { - req->length = len; - req->buf = kmalloc(len, gfp_flags); - if (req->buf == NULL) { - usb_ep_free_request(ep, req); - return NULL; - } - } - - return req; -} - -static void -printer_req_free(struct usb_ep *ep, struct usb_request *req) -{ - if (ep != NULL && req != NULL) { - kfree(req->buf); - usb_ep_free_request(ep, req); - } -} - -/*-------------------------------------------------------------------------*/ - -static void rx_complete(struct usb_ep *ep, struct usb_request *req) -{ - struct printer_dev *dev = ep->driver_data; - int status = req->status; - unsigned long flags; - - spin_lock_irqsave(&dev->lock, flags); - - list_del_init(&req->list); /* Remode from Active List */ - - switch (status) { - - /* normal completion */ - case 0: - if (req->actual > 0) { - list_add_tail(&req->list, &dev->rx_buffers); - DBG(dev, "G_Printer : rx length %d\n", req->actual); - } else { - list_add(&req->list, &dev->rx_reqs); - } - break; - - /* software-driven interface shutdown */ - case -ECONNRESET: /* unlink */ - case -ESHUTDOWN: /* disconnect etc */ - VDBG(dev, "rx shutdown, code %d\n", status); - list_add(&req->list, &dev->rx_reqs); - break; - - /* for hardware automagic (such as pxa) */ - case -ECONNABORTED: /* endpoint reset */ - DBG(dev, "rx %s reset\n", ep->name); - list_add(&req->list, &dev->rx_reqs); - break; - - /* data overrun */ - case -EOVERFLOW: - /* FALLTHROUGH */ - - default: - DBG(dev, "rx status %d\n", status); - list_add(&req->list, &dev->rx_reqs); - break; - } - - wake_up_interruptible(&dev->rx_wait); - spin_unlock_irqrestore(&dev->lock, flags); -} - -static void tx_complete(struct usb_ep *ep, struct usb_request *req) -{ - struct printer_dev *dev = ep->driver_data; - - switch (req->status) { - default: - VDBG(dev, "tx err %d\n", req->status); - /* FALLTHROUGH */ - case -ECONNRESET: /* unlink */ - case -ESHUTDOWN: /* disconnect etc */ - break; - case 0: - break; - } - - spin_lock(&dev->lock); - /* Take the request struct off the active list and put it on the - * free list. - */ - list_del_init(&req->list); - list_add(&req->list, &dev->tx_reqs); - wake_up_interruptible(&dev->tx_wait); - if (likely(list_empty(&dev->tx_reqs_active))) - wake_up_interruptible(&dev->tx_flush_wait); - - spin_unlock(&dev->lock); -} - -/*-------------------------------------------------------------------------*/ - -static int -printer_open(struct inode *inode, struct file *fd) -{ - struct printer_dev *dev; - unsigned long flags; - int ret = -EBUSY; - - dev = container_of(inode->i_cdev, struct printer_dev, printer_cdev); - - spin_lock_irqsave(&dev->lock, flags); - - if (!dev->printer_cdev_open) { - dev->printer_cdev_open = 1; - fd->private_data = dev; - ret = 0; - /* Change the printer status to show that it's on-line. */ - dev->printer_status |= PRINTER_SELECTED; - } - - spin_unlock_irqrestore(&dev->lock, flags); - - DBG(dev, "printer_open returned %x\n", ret); - return ret; -} - -static int -printer_close(struct inode *inode, struct file *fd) -{ - struct printer_dev *dev = fd->private_data; - unsigned long flags; - - spin_lock_irqsave(&dev->lock, flags); - dev->printer_cdev_open = 0; - fd->private_data = NULL; - /* Change printer status to show that the printer is off-line. */ - dev->printer_status &= ~PRINTER_SELECTED; - spin_unlock_irqrestore(&dev->lock, flags); - - DBG(dev, "printer_close\n"); - - return 0; -} - -/* This function must be called with interrupts turned off. */ -static void -setup_rx_reqs(struct printer_dev *dev) -{ - struct usb_request *req; - - while (likely(!list_empty(&dev->rx_reqs))) { - int error; - - req = container_of(dev->rx_reqs.next, - struct usb_request, list); - list_del_init(&req->list); - - /* The USB Host sends us whatever amount of data it wants to - * so we always set the length field to the full USB_BUFSIZE. - * If the amount of data is more than the read() caller asked - * for it will be stored in the request buffer until it is - * asked for by read(). - */ - req->length = USB_BUFSIZE; - req->complete = rx_complete; - - /* here, we unlock, and only unlock, to avoid deadlock. */ - spin_unlock(&dev->lock); - error = usb_ep_queue(dev->out_ep, req, GFP_ATOMIC); - spin_lock(&dev->lock); - if (error) { - DBG(dev, "rx submit --> %d\n", error); - list_add(&req->list, &dev->rx_reqs); - break; - } - /* if the req is empty, then add it into dev->rx_reqs_active. */ - else if (list_empty(&req->list)) { - list_add(&req->list, &dev->rx_reqs_active); - } - } -} - -static ssize_t -printer_read(struct file *fd, char __user *buf, size_t len, loff_t *ptr) -{ - struct printer_dev *dev = fd->private_data; - unsigned long flags; - size_t size; - size_t bytes_copied; - struct usb_request *req; - /* This is a pointer to the current USB rx request. */ - struct usb_request *current_rx_req; - /* This is the number of bytes in the current rx buffer. */ - size_t current_rx_bytes; - /* This is a pointer to the current rx buffer. */ - u8 *current_rx_buf; - - if (len == 0) - return -EINVAL; - - DBG(dev, "printer_read trying to read %d bytes\n", (int)len); - - mutex_lock(&dev->lock_printer_io); - spin_lock_irqsave(&dev->lock, flags); - - /* We will use this flag later to check if a printer reset happened - * after we turn interrupts back on. - */ - dev->reset_printer = 0; - - setup_rx_reqs(dev); - - bytes_copied = 0; - current_rx_req = dev->current_rx_req; - current_rx_bytes = dev->current_rx_bytes; - current_rx_buf = dev->current_rx_buf; - dev->current_rx_req = NULL; - dev->current_rx_bytes = 0; - dev->current_rx_buf = NULL; - - /* Check if there is any data in the read buffers. Please note that - * current_rx_bytes is the number of bytes in the current rx buffer. - * If it is zero then check if there are any other rx_buffers that - * are on the completed list. We are only out of data if all rx - * buffers are empty. - */ - if ((current_rx_bytes == 0) && - (likely(list_empty(&dev->rx_buffers)))) { - /* Turn interrupts back on before sleeping. */ - spin_unlock_irqrestore(&dev->lock, flags); - - /* - * If no data is available check if this is a NON-Blocking - * call or not. - */ - if (fd->f_flags & (O_NONBLOCK|O_NDELAY)) { - mutex_unlock(&dev->lock_printer_io); - return -EAGAIN; - } - - /* Sleep until data is available */ - wait_event_interruptible(dev->rx_wait, - (likely(!list_empty(&dev->rx_buffers)))); - spin_lock_irqsave(&dev->lock, flags); - } - - /* We have data to return then copy it to the caller's buffer.*/ - while ((current_rx_bytes || likely(!list_empty(&dev->rx_buffers))) - && len) { - if (current_rx_bytes == 0) { - req = container_of(dev->rx_buffers.next, - struct usb_request, list); - list_del_init(&req->list); - - if (req->actual && req->buf) { - current_rx_req = req; - current_rx_bytes = req->actual; - current_rx_buf = req->buf; - } else { - list_add(&req->list, &dev->rx_reqs); - continue; - } - } - - /* Don't leave irqs off while doing memory copies */ - spin_unlock_irqrestore(&dev->lock, flags); - - if (len > current_rx_bytes) - size = current_rx_bytes; - else - size = len; - - size -= copy_to_user(buf, current_rx_buf, size); - bytes_copied += size; - len -= size; - buf += size; - - spin_lock_irqsave(&dev->lock, flags); - - /* We've disconnected or reset so return. */ - if (dev->reset_printer) { - list_add(¤t_rx_req->list, &dev->rx_reqs); - spin_unlock_irqrestore(&dev->lock, flags); - mutex_unlock(&dev->lock_printer_io); - return -EAGAIN; - } - - /* If we not returning all the data left in this RX request - * buffer then adjust the amount of data left in the buffer. - * Othewise if we are done with this RX request buffer then - * requeue it to get any incoming data from the USB host. - */ - if (size < current_rx_bytes) { - current_rx_bytes -= size; - current_rx_buf += size; - } else { - list_add(¤t_rx_req->list, &dev->rx_reqs); - current_rx_bytes = 0; - current_rx_buf = NULL; - current_rx_req = NULL; - } - } - - dev->current_rx_req = current_rx_req; - dev->current_rx_bytes = current_rx_bytes; - dev->current_rx_buf = current_rx_buf; - - spin_unlock_irqrestore(&dev->lock, flags); - mutex_unlock(&dev->lock_printer_io); - - DBG(dev, "printer_read returned %d bytes\n", (int)bytes_copied); - - if (bytes_copied) - return bytes_copied; - else - return -EAGAIN; -} - -static ssize_t -printer_write(struct file *fd, const char __user *buf, size_t len, loff_t *ptr) -{ - struct printer_dev *dev = fd->private_data; - unsigned long flags; - size_t size; /* Amount of data in a TX request. */ - size_t bytes_copied = 0; - struct usb_request *req; - - DBG(dev, "printer_write trying to send %d bytes\n", (int)len); - - if (len == 0) - return -EINVAL; - - mutex_lock(&dev->lock_printer_io); - spin_lock_irqsave(&dev->lock, flags); - - /* Check if a printer reset happens while we have interrupts on */ - dev->reset_printer = 0; - - /* Check if there is any available write buffers */ - if (likely(list_empty(&dev->tx_reqs))) { - /* Turn interrupts back on before sleeping. */ - spin_unlock_irqrestore(&dev->lock, flags); - - /* - * If write buffers are available check if this is - * a NON-Blocking call or not. - */ - if (fd->f_flags & (O_NONBLOCK|O_NDELAY)) { - mutex_unlock(&dev->lock_printer_io); - return -EAGAIN; - } - - /* Sleep until a write buffer is available */ - wait_event_interruptible(dev->tx_wait, - (likely(!list_empty(&dev->tx_reqs)))); - spin_lock_irqsave(&dev->lock, flags); - } - - while (likely(!list_empty(&dev->tx_reqs)) && len) { - - if (len > USB_BUFSIZE) - size = USB_BUFSIZE; - else - size = len; - - req = container_of(dev->tx_reqs.next, struct usb_request, - list); - list_del_init(&req->list); - - req->complete = tx_complete; - req->length = size; - - /* Check if we need to send a zero length packet. */ - if (len > size) - /* They will be more TX requests so no yet. */ - req->zero = 0; - else - /* If the data amount is not a multple of the - * maxpacket size then send a zero length packet. - */ - req->zero = ((len % dev->in_ep->maxpacket) == 0); - - /* Don't leave irqs off while doing memory copies */ - spin_unlock_irqrestore(&dev->lock, flags); - - if (copy_from_user(req->buf, buf, size)) { - list_add(&req->list, &dev->tx_reqs); - mutex_unlock(&dev->lock_printer_io); - return bytes_copied; - } - - bytes_copied += size; - len -= size; - buf += size; - - spin_lock_irqsave(&dev->lock, flags); - - /* We've disconnected or reset so free the req and buffer */ - if (dev->reset_printer) { - list_add(&req->list, &dev->tx_reqs); - spin_unlock_irqrestore(&dev->lock, flags); - mutex_unlock(&dev->lock_printer_io); - return -EAGAIN; - } - - if (usb_ep_queue(dev->in_ep, req, GFP_ATOMIC)) { - list_add(&req->list, &dev->tx_reqs); - spin_unlock_irqrestore(&dev->lock, flags); - mutex_unlock(&dev->lock_printer_io); - return -EAGAIN; - } - - list_add(&req->list, &dev->tx_reqs_active); - - } - - spin_unlock_irqrestore(&dev->lock, flags); - mutex_unlock(&dev->lock_printer_io); - - DBG(dev, "printer_write sent %d bytes\n", (int)bytes_copied); - - if (bytes_copied) { - return bytes_copied; - } else { - return -EAGAIN; - } -} - -static int -printer_fsync(struct file *fd, loff_t start, loff_t end, int datasync) -{ - struct printer_dev *dev = fd->private_data; - struct inode *inode = file_inode(fd); - unsigned long flags; - int tx_list_empty; - - mutex_lock(&inode->i_mutex); - spin_lock_irqsave(&dev->lock, flags); - tx_list_empty = (likely(list_empty(&dev->tx_reqs))); - spin_unlock_irqrestore(&dev->lock, flags); - - if (!tx_list_empty) { - /* Sleep until all data has been sent */ - wait_event_interruptible(dev->tx_flush_wait, - (likely(list_empty(&dev->tx_reqs_active)))); - } - mutex_unlock(&inode->i_mutex); - - return 0; -} - -static unsigned int -printer_poll(struct file *fd, poll_table *wait) -{ - struct printer_dev *dev = fd->private_data; - unsigned long flags; - int status = 0; - - mutex_lock(&dev->lock_printer_io); - spin_lock_irqsave(&dev->lock, flags); - setup_rx_reqs(dev); - spin_unlock_irqrestore(&dev->lock, flags); - mutex_unlock(&dev->lock_printer_io); - - poll_wait(fd, &dev->rx_wait, wait); - poll_wait(fd, &dev->tx_wait, wait); - - spin_lock_irqsave(&dev->lock, flags); - if (likely(!list_empty(&dev->tx_reqs))) - status |= POLLOUT | POLLWRNORM; - - if (likely(dev->current_rx_bytes) || - likely(!list_empty(&dev->rx_buffers))) - status |= POLLIN | POLLRDNORM; - - spin_unlock_irqrestore(&dev->lock, flags); - - return status; -} - -static long -printer_ioctl(struct file *fd, unsigned int code, unsigned long arg) -{ - struct printer_dev *dev = fd->private_data; - unsigned long flags; - int status = 0; - - DBG(dev, "printer_ioctl: cmd=0x%4.4x, arg=%lu\n", code, arg); - - /* handle ioctls */ - - spin_lock_irqsave(&dev->lock, flags); - - switch (code) { - case GADGET_GET_PRINTER_STATUS: - status = (int)dev->printer_status; - break; - case GADGET_SET_PRINTER_STATUS: - dev->printer_status = (u8)arg; - break; - default: - /* could not handle ioctl */ - DBG(dev, "printer_ioctl: ERROR cmd=0x%4.4xis not supported\n", - code); - status = -ENOTTY; - } - - spin_unlock_irqrestore(&dev->lock, flags); - - return status; -} - -/* used after endpoint configuration */ -static const struct file_operations printer_io_operations = { - .owner = THIS_MODULE, - .open = printer_open, - .read = printer_read, - .write = printer_write, - .fsync = printer_fsync, - .poll = printer_poll, - .unlocked_ioctl = printer_ioctl, - .release = printer_close, - .llseek = noop_llseek, -}; - -/*-------------------------------------------------------------------------*/ - -static int -set_printer_interface(struct printer_dev *dev) -{ - int result = 0; - - dev->in_ep->desc = ep_desc(dev->gadget, &fs_ep_in_desc, &hs_ep_in_desc, - &ss_ep_in_desc); - dev->in_ep->driver_data = dev; - - dev->out_ep->desc = ep_desc(dev->gadget, &fs_ep_out_desc, - &hs_ep_out_desc, &ss_ep_out_desc); - dev->out_ep->driver_data = dev; - - result = usb_ep_enable(dev->in_ep); - if (result != 0) { - DBG(dev, "enable %s --> %d\n", dev->in_ep->name, result); - goto done; - } - - result = usb_ep_enable(dev->out_ep); - if (result != 0) { - DBG(dev, "enable %s --> %d\n", dev->in_ep->name, result); - goto done; - } - -done: - /* on error, disable any endpoints */ - if (result != 0) { - (void) usb_ep_disable(dev->in_ep); - (void) usb_ep_disable(dev->out_ep); - dev->in_ep->desc = NULL; - dev->out_ep->desc = NULL; - } - - /* caller is responsible for cleanup on error */ - return result; -} - -static void printer_reset_interface(struct printer_dev *dev) -{ - if (dev->interface < 0) - return; - - DBG(dev, "%s\n", __func__); - - if (dev->in_ep->desc) - usb_ep_disable(dev->in_ep); - - if (dev->out_ep->desc) - usb_ep_disable(dev->out_ep); - - dev->in_ep->desc = NULL; - dev->out_ep->desc = NULL; - dev->interface = -1; -} - -/* Change our operational Interface. */ -static int set_interface(struct printer_dev *dev, unsigned number) -{ - int result = 0; - - /* Free the current interface */ - printer_reset_interface(dev); - - result = set_printer_interface(dev); - if (result) - printer_reset_interface(dev); - else - dev->interface = number; - - if (!result) - INFO(dev, "Using interface %x\n", number); - - return result; -} - -static void printer_soft_reset(struct printer_dev *dev) -{ - struct usb_request *req; - - INFO(dev, "Received Printer Reset Request\n"); - - if (usb_ep_disable(dev->in_ep)) - DBG(dev, "Failed to disable USB in_ep\n"); - if (usb_ep_disable(dev->out_ep)) - DBG(dev, "Failed to disable USB out_ep\n"); - - if (dev->current_rx_req != NULL) { - list_add(&dev->current_rx_req->list, &dev->rx_reqs); - dev->current_rx_req = NULL; - } - dev->current_rx_bytes = 0; - dev->current_rx_buf = NULL; - dev->reset_printer = 1; - - while (likely(!(list_empty(&dev->rx_buffers)))) { - req = container_of(dev->rx_buffers.next, struct usb_request, - list); - list_del_init(&req->list); - list_add(&req->list, &dev->rx_reqs); - } - - while (likely(!(list_empty(&dev->rx_reqs_active)))) { - req = container_of(dev->rx_buffers.next, struct usb_request, - list); - list_del_init(&req->list); - list_add(&req->list, &dev->rx_reqs); - } - - while (likely(!(list_empty(&dev->tx_reqs_active)))) { - req = container_of(dev->tx_reqs_active.next, - struct usb_request, list); - list_del_init(&req->list); - list_add(&req->list, &dev->tx_reqs); - } - - if (usb_ep_enable(dev->in_ep)) - DBG(dev, "Failed to enable USB in_ep\n"); - if (usb_ep_enable(dev->out_ep)) - DBG(dev, "Failed to enable USB out_ep\n"); - - wake_up_interruptible(&dev->rx_wait); - wake_up_interruptible(&dev->tx_wait); - wake_up_interruptible(&dev->tx_flush_wait); -} - -/*-------------------------------------------------------------------------*/ - -static bool gprinter_req_match(struct usb_function *f, - const struct usb_ctrlrequest *ctrl) -{ - struct printer_dev *dev = func_to_printer(f); - u16 w_index = le16_to_cpu(ctrl->wIndex); - u16 w_value = le16_to_cpu(ctrl->wValue); - u16 w_length = le16_to_cpu(ctrl->wLength); - - if ((ctrl->bRequestType & USB_RECIP_MASK) != USB_RECIP_INTERFACE || - (ctrl->bRequestType & USB_TYPE_MASK) != USB_TYPE_CLASS) - return false; - - switch (ctrl->bRequest) { - case GET_DEVICE_ID: - w_index >>= 8; - if (w_length <= PNP_STRING_LEN && - (USB_DIR_IN & ctrl->bRequestType)) - break; - return false; - case GET_PORT_STATUS: - if (!w_value && w_length == 1 && - (USB_DIR_IN & ctrl->bRequestType)) - break; - return false; - case SOFT_RESET: - if (!w_value && !w_length && - (USB_DIR_OUT & ctrl->bRequestType)) - break; - /* fall through */ - default: - return false; - } - return w_index == dev->interface; -} - -/* - * The setup() callback implements all the ep0 functionality that's not - * handled lower down. - */ -static int printer_func_setup(struct usb_function *f, - const struct usb_ctrlrequest *ctrl) -{ - struct printer_dev *dev = func_to_printer(f); - struct usb_composite_dev *cdev = f->config->cdev; - struct usb_request *req = cdev->req; - int value = -EOPNOTSUPP; - u16 wIndex = le16_to_cpu(ctrl->wIndex); - u16 wValue = le16_to_cpu(ctrl->wValue); - u16 wLength = le16_to_cpu(ctrl->wLength); - - DBG(dev, "ctrl req%02x.%02x v%04x i%04x l%d\n", - ctrl->bRequestType, ctrl->bRequest, wValue, wIndex, wLength); - - switch (ctrl->bRequestType&USB_TYPE_MASK) { - case USB_TYPE_CLASS: - switch (ctrl->bRequest) { - case GET_DEVICE_ID: /* Get the IEEE-1284 PNP String */ - /* Only one printer interface is supported. */ - if ((wIndex>>8) != dev->interface) - break; - - value = (dev->pnp_string[0] << 8) | dev->pnp_string[1]; - memcpy(req->buf, dev->pnp_string, value); - DBG(dev, "1284 PNP String: %x %s\n", value, - &dev->pnp_string[2]); - break; - - case GET_PORT_STATUS: /* Get Port Status */ - /* Only one printer interface is supported. */ - if (wIndex != dev->interface) - break; - - *(u8 *)req->buf = dev->printer_status; - value = min(wLength, (u16) 1); - break; - - case SOFT_RESET: /* Soft Reset */ - /* Only one printer interface is supported. */ - if (wIndex != dev->interface) - break; - - printer_soft_reset(dev); - - value = 0; - break; - - default: - goto unknown; - } - break; - - default: -unknown: - VDBG(dev, - "unknown ctrl req%02x.%02x v%04x i%04x l%d\n", - ctrl->bRequestType, ctrl->bRequest, - wValue, wIndex, wLength); - break; - } - /* host either stalls (value < 0) or reports success */ - if (value >= 0) { - req->length = value; - req->zero = value < wLength; - value = usb_ep_queue(cdev->gadget->ep0, req, GFP_ATOMIC); - if (value < 0) { - ERROR(dev, "%s:%d Error!\n", __func__, __LINE__); - req->status = 0; - } - } - return value; -} - -static int __init printer_func_bind(struct usb_configuration *c, - struct usb_function *f) -{ - struct usb_gadget *gadget = c->cdev->gadget; - struct printer_dev *dev = func_to_printer(f); - struct device *pdev; - struct usb_composite_dev *cdev = c->cdev; - struct usb_ep *in_ep; - struct usb_ep *out_ep = NULL; - struct usb_request *req; - dev_t devt; - int id; - int ret; - u32 i; - - id = usb_interface_id(c, f); - if (id < 0) - return id; - intf_desc.bInterfaceNumber = id; - - /* finish hookup to lower layer ... */ - dev->gadget = gadget; - - /* all we really need is bulk IN/OUT */ - in_ep = usb_ep_autoconfig(cdev->gadget, &fs_ep_in_desc); - if (!in_ep) { -autoconf_fail: - dev_err(&cdev->gadget->dev, "can't autoconfigure on %s\n", - cdev->gadget->name); - return -ENODEV; - } - in_ep->driver_data = in_ep; /* claim */ - - out_ep = usb_ep_autoconfig(cdev->gadget, &fs_ep_out_desc); - if (!out_ep) - goto autoconf_fail; - out_ep->driver_data = out_ep; /* claim */ - - /* assumes that all endpoints are dual-speed */ - hs_ep_in_desc.bEndpointAddress = fs_ep_in_desc.bEndpointAddress; - hs_ep_out_desc.bEndpointAddress = fs_ep_out_desc.bEndpointAddress; - ss_ep_in_desc.bEndpointAddress = fs_ep_in_desc.bEndpointAddress; - ss_ep_out_desc.bEndpointAddress = fs_ep_out_desc.bEndpointAddress; - - ret = usb_assign_descriptors(f, fs_printer_function, - hs_printer_function, ss_printer_function); - if (ret) - return ret; - - dev->in_ep = in_ep; - dev->out_ep = out_ep; - - ret = -ENOMEM; - for (i = 0; i < dev->q_len; i++) { - req = printer_req_alloc(dev->in_ep, USB_BUFSIZE, GFP_KERNEL); - if (!req) - goto fail_tx_reqs; - list_add(&req->list, &dev->tx_reqs); - } - - for (i = 0; i < dev->q_len; i++) { - req = printer_req_alloc(dev->out_ep, USB_BUFSIZE, GFP_KERNEL); - if (!req) - goto fail_rx_reqs; - list_add(&req->list, &dev->rx_reqs); - } - - /* Setup the sysfs files for the printer gadget. */ - devt = MKDEV(major, dev->minor); - pdev = device_create(usb_gadget_class, NULL, devt, - NULL, "g_printer%d", dev->minor); - if (IS_ERR(pdev)) { - ERROR(dev, "Failed to create device: g_printer\n"); - ret = PTR_ERR(pdev); - goto fail_rx_reqs; - } - - /* - * Register a character device as an interface to a user mode - * program that handles the printer specific functionality. - */ - cdev_init(&dev->printer_cdev, &printer_io_operations); - dev->printer_cdev.owner = THIS_MODULE; - ret = cdev_add(&dev->printer_cdev, devt, 1); - if (ret) { - ERROR(dev, "Failed to open char device\n"); - goto fail_cdev_add; - } - - return 0; - -fail_cdev_add: - device_destroy(usb_gadget_class, devt); - -fail_rx_reqs: - while (!list_empty(&dev->rx_reqs)) { - req = container_of(dev->rx_reqs.next, struct usb_request, list); - list_del(&req->list); - printer_req_free(dev->out_ep, req); - } - -fail_tx_reqs: - while (!list_empty(&dev->tx_reqs)) { - req = container_of(dev->tx_reqs.next, struct usb_request, list); - list_del(&req->list); - printer_req_free(dev->in_ep, req); - } - - return ret; - -} - -static void printer_func_unbind(struct usb_configuration *c, - struct usb_function *f) -{ - struct printer_dev *dev; - struct usb_request *req; - - dev = func_to_printer(f); - - device_destroy(usb_gadget_class, MKDEV(major, dev->minor)); - - /* Remove Character Device */ - cdev_del(&dev->printer_cdev); - - /* we must already have been disconnected ... no i/o may be active */ - WARN_ON(!list_empty(&dev->tx_reqs_active)); - WARN_ON(!list_empty(&dev->rx_reqs_active)); - - /* Free all memory for this driver. */ - while (!list_empty(&dev->tx_reqs)) { - req = container_of(dev->tx_reqs.next, struct usb_request, - list); - list_del(&req->list); - printer_req_free(dev->in_ep, req); - } - - if (dev->current_rx_req != NULL) - printer_req_free(dev->out_ep, dev->current_rx_req); - - while (!list_empty(&dev->rx_reqs)) { - req = container_of(dev->rx_reqs.next, - struct usb_request, list); - list_del(&req->list); - printer_req_free(dev->out_ep, req); - } - - while (!list_empty(&dev->rx_buffers)) { - req = container_of(dev->rx_buffers.next, - struct usb_request, list); - list_del(&req->list); - printer_req_free(dev->out_ep, req); - } - usb_free_all_descriptors(f); - kfree(dev); -} - -static int printer_func_set_alt(struct usb_function *f, - unsigned intf, unsigned alt) -{ - struct printer_dev *dev = func_to_printer(f); - int ret = -ENOTSUPP; - - if (!alt) - ret = set_interface(dev, intf); - - return ret; -} - -static void printer_func_disable(struct usb_function *f) -{ - struct printer_dev *dev = func_to_printer(f); - unsigned long flags; - - DBG(dev, "%s\n", __func__); - - spin_lock_irqsave(&dev->lock, flags); - printer_reset_interface(dev); - spin_unlock_irqrestore(&dev->lock, flags); -} - static struct usb_configuration printer_cfg_driver = { .label = "printer", .bConfigurationValue = 1, .bmAttributes = USB_CONFIG_ATT_ONE | USB_CONFIG_ATT_SELFPOWER, }; -static int f_printer_bind_config(struct usb_configuration *c, char *pnp_str, - char *pnp_string, unsigned q_len, int minor) -{ - struct printer_dev *dev; - int status = -ENOMEM; - size_t len; - - if (minor >= minors) - return -ENOENT; - - dev = kzalloc(sizeof(*dev), GFP_KERNEL); - if (!dev) - return -ENOMEM; - - dev->pnp_string = pnp_string; - dev->minor = minor; - - dev->function.name = shortname; - dev->function.bind = printer_func_bind; - dev->function.setup = printer_func_setup; - dev->function.unbind = printer_func_unbind; - dev->function.set_alt = printer_func_set_alt; - dev->function.disable = printer_func_disable; - dev->function.req_match = gprinter_req_match; - INIT_LIST_HEAD(&dev->tx_reqs); - INIT_LIST_HEAD(&dev->rx_reqs); - INIT_LIST_HEAD(&dev->rx_buffers); - - if (pnp_str) - strlcpy(&dev->pnp_string[2], pnp_str, PNP_STRING_LEN - 2); - - len = strlen(pnp_string); - pnp_string[0] = (len >> 8) & 0xFF; - pnp_string[1] = len & 0xFF; - - spin_lock_init(&dev->lock); - mutex_init(&dev->lock_printer_io); - INIT_LIST_HEAD(&dev->tx_reqs_active); - INIT_LIST_HEAD(&dev->rx_reqs_active); - init_waitqueue_head(&dev->rx_wait); - init_waitqueue_head(&dev->tx_wait); - init_waitqueue_head(&dev->tx_flush_wait); - - dev->interface = -1; - dev->printer_cdev_open = 0; - dev->printer_status = PRINTER_NOT_ERROR; - dev->current_rx_req = NULL; - dev->current_rx_bytes = 0; - dev->current_rx_buf = NULL; - dev->q_len = q_len; - - status = usb_add_function(c, &dev->function); - if (status) { - kfree(dev); - return status; - } - INFO(dev, "%s, version: " DRIVER_VERSION "\n", driver_desc); - return 0; -} - static int __init printer_do_config(struct usb_configuration *c) { struct usb_gadget *gadget = c->cdev->gadget; @@ -1350,43 +144,6 @@ static int __init printer_do_config(struct usb_configuration *c) return f_printer_bind_config(c, iPNPstring, pnp_string, QLEN, 0); } -static int gprinter_setup(int count) -{ - int status; - dev_t devt; - - usb_gadget_class = class_create(THIS_MODULE, "usb_printer_gadget"); - if (IS_ERR(usb_gadget_class)) { - status = PTR_ERR(usb_gadget_class); - usb_gadget_class = NULL; - pr_err("unable to create usb_gadget class %d\n", status); - return status; - } - - status = alloc_chrdev_region(&devt, 0, count, "USB printer gadget"); - if (status) { - pr_err("alloc_chrdev_region %d\n", status); - class_destroy(usb_gadget_class); - usb_gadget_class = NULL; - return status; - } - - major = MAJOR(devt); - minors = count; - - return status; -} - -static void gprinter_cleanup(void) -{ - if (major) { - unregister_chrdev_region(MKDEV(major, 0), minors); - major = minors = 0; - } - class_destroy(usb_gadget_class); - usb_gadget_class = NULL; -} - static int __init printer_bind(struct usb_composite_dev *cdev) { int ret; -- cgit v1.2.3 From b26394bd567e5ebe57ec4dee7fe6cd14023c96e9 Mon Sep 17 00:00:00 2001 From: Andrzej Pietrasiewicz Date: Tue, 3 Mar 2015 10:52:28 +0100 Subject: usb: gadget: f_printer: convert to new function interface with backward compatibility In order to add configfs support, a usb function must be converted to use the new interface. This patch converts the function to the new interface and provides backward compatiblity layer, which can be removed after all its users are converted to use the new interface. 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_printer.c | 185 +++++++++++++++++++++++++++++++- drivers/usb/gadget/function/u_printer.h | 30 ++++++ drivers/usb/gadget/legacy/printer.c | 1 + 5 files changed, 220 insertions(+), 1 deletion(-) create mode 100644 drivers/usb/gadget/function/u_printer.h diff --git a/drivers/usb/gadget/Kconfig b/drivers/usb/gadget/Kconfig index b454d05be583..9d507cf98f94 100644 --- a/drivers/usb/gadget/Kconfig +++ b/drivers/usb/gadget/Kconfig @@ -196,6 +196,9 @@ config USB_F_MIDI config USB_F_HID tristate +config USB_F_PRINTER + tristate + choice tristate "USB Gadget Drivers" default USB_ETH diff --git a/drivers/usb/gadget/function/Makefile b/drivers/usb/gadget/function/Makefile index f71b1aaa0edf..bd7def576955 100644 --- a/drivers/usb/gadget/function/Makefile +++ b/drivers/usb/gadget/function/Makefile @@ -42,3 +42,5 @@ usb_f_midi-y := f_midi.o obj-$(CONFIG_USB_F_MIDI) += usb_f_midi.o usb_f_hid-y := f_hid.o obj-$(CONFIG_USB_F_HID) += usb_f_hid.o +usb_f_printer-y := f_printer.o +obj-$(CONFIG_USB_F_PRINTER) += usb_f_printer.o diff --git a/drivers/usb/gadget/function/f_printer.c b/drivers/usb/gadget/function/f_printer.c index 0847972b9686..93f4d4e61fef 100644 --- a/drivers/usb/gadget/function/f_printer.c +++ b/drivers/usb/gadget/function/f_printer.c @@ -24,6 +24,7 @@ #include #include #include +#include #include #include #include @@ -46,6 +47,8 @@ #include #include +#include "u_printer.h" + #define PNP_STRING_LEN 1024 #define PRINTER_MINORS 4 #define GET_DEVICE_ID 0 @@ -54,6 +57,10 @@ static int major, minors; static struct class *usb_gadget_class; +#ifndef USBF_PRINTER_INCLUDED +static DEFINE_IDA(printer_ida); +static DEFINE_MUTEX(printer_ida_lock); /* protects access do printer_ida */ +#endif /*-------------------------------------------------------------------------*/ @@ -999,7 +1006,7 @@ unknown: return value; } -static int __init printer_func_bind(struct usb_configuration *c, +static int printer_func_bind(struct usb_configuration *c, struct usb_function *f) { struct usb_gadget *gadget = c->cdev->gadget; @@ -1111,6 +1118,7 @@ fail_tx_reqs: } +#ifdef USBF_PRINTER_INCLUDED static void printer_func_unbind(struct usb_configuration *c, struct usb_function *f) { @@ -1155,6 +1163,7 @@ static void printer_func_unbind(struct usb_configuration *c, usb_free_all_descriptors(f); kfree(dev); } +#endif static int printer_func_set_alt(struct usb_function *f, unsigned intf, unsigned alt) @@ -1180,6 +1189,7 @@ static void printer_func_disable(struct usb_function *f) spin_unlock_irqrestore(&dev->lock, flags); } +#ifdef USBF_PRINTER_INCLUDED static int f_printer_bind_config(struct usb_configuration *c, char *pnp_str, char *pnp_string, unsigned q_len, int minor) { @@ -1240,6 +1250,179 @@ static int f_printer_bind_config(struct usb_configuration *c, char *pnp_str, INFO(dev, "%s, version: " DRIVER_VERSION "\n", driver_desc); return 0; } +#else +static inline int gprinter_get_minor(void) +{ + return ida_simple_get(&printer_ida, 0, 0, GFP_KERNEL); +} + +static inline void gprinter_put_minor(int minor) +{ + ida_simple_remove(&printer_ida, minor); +} + +static int gprinter_setup(int); +static void gprinter_cleanup(void); + +static void gprinter_free_inst(struct usb_function_instance *f) +{ + struct f_printer_opts *opts; + + opts = container_of(f, struct f_printer_opts, func_inst); + + mutex_lock(&printer_ida_lock); + + gprinter_put_minor(opts->minor); + if (idr_is_empty(&printer_ida.idr)) + gprinter_cleanup(); + + mutex_unlock(&printer_ida_lock); + + kfree(opts); +} + +static struct usb_function_instance *gprinter_alloc_inst(void) +{ + struct f_printer_opts *opts; + struct usb_function_instance *ret; + int status = 0; + + opts = kzalloc(sizeof(*opts), GFP_KERNEL); + if (!opts) + return ERR_PTR(-ENOMEM); + + opts->func_inst.free_func_inst = gprinter_free_inst; + ret = &opts->func_inst; + + mutex_lock(&printer_ida_lock); + + if (idr_is_empty(&printer_ida.idr)) { + status = gprinter_setup(PRINTER_MINORS); + if (status) { + ret = ERR_PTR(status); + kfree(opts); + goto unlock; + } + } + + opts->minor = gprinter_get_minor(); + if (opts->minor < 0) { + ret = ERR_PTR(opts->minor); + kfree(opts); + if (idr_is_empty(&printer_ida.idr)) + gprinter_cleanup(); + } + +unlock: + mutex_unlock(&printer_ida_lock); + return ret; +} + +static void gprinter_free(struct usb_function *f) +{ + struct printer_dev *dev = func_to_printer(f); + + kfree(dev); +} + +static void printer_func_unbind(struct usb_configuration *c, + struct usb_function *f) +{ + struct printer_dev *dev; + struct usb_request *req; + + dev = func_to_printer(f); + + device_destroy(usb_gadget_class, MKDEV(major, dev->minor)); + + /* Remove Character Device */ + cdev_del(&dev->printer_cdev); + + /* we must already have been disconnected ... no i/o may be active */ + WARN_ON(!list_empty(&dev->tx_reqs_active)); + WARN_ON(!list_empty(&dev->rx_reqs_active)); + + /* Free all memory for this driver. */ + while (!list_empty(&dev->tx_reqs)) { + req = container_of(dev->tx_reqs.next, struct usb_request, + list); + list_del(&req->list); + printer_req_free(dev->in_ep, req); + } + + if (dev->current_rx_req != NULL) + printer_req_free(dev->out_ep, dev->current_rx_req); + + while (!list_empty(&dev->rx_reqs)) { + req = container_of(dev->rx_reqs.next, + struct usb_request, list); + list_del(&req->list); + printer_req_free(dev->out_ep, req); + } + + while (!list_empty(&dev->rx_buffers)) { + req = container_of(dev->rx_buffers.next, + struct usb_request, list); + list_del(&req->list); + printer_req_free(dev->out_ep, req); + } + usb_free_all_descriptors(f); +} + +static struct usb_function *gprinter_alloc(struct usb_function_instance *fi) +{ + struct printer_dev *dev; + struct f_printer_opts *opts; + + opts = container_of(fi, struct f_printer_opts, func_inst); + + if (opts->minor >= minors) + return ERR_PTR(-ENOENT); + + dev = kzalloc(sizeof(*dev), GFP_KERNEL); + if (!dev) + return ERR_PTR(-ENOMEM); + + dev->minor = opts->minor; + dev->pnp_string = opts->pnp_string; + dev->q_len = opts->q_len; + + dev->function.name = "printer"; + dev->function.bind = printer_func_bind; + dev->function.setup = printer_func_setup; + dev->function.unbind = printer_func_unbind; + dev->function.set_alt = printer_func_set_alt; + dev->function.disable = printer_func_disable; + dev->function.req_match = gprinter_req_match; + dev->function.free_func = gprinter_free; + + INIT_LIST_HEAD(&dev->tx_reqs); + INIT_LIST_HEAD(&dev->rx_reqs); + INIT_LIST_HEAD(&dev->rx_buffers); + INIT_LIST_HEAD(&dev->tx_reqs_active); + INIT_LIST_HEAD(&dev->rx_reqs_active); + + spin_lock_init(&dev->lock); + mutex_init(&dev->lock_printer_io); + init_waitqueue_head(&dev->rx_wait); + init_waitqueue_head(&dev->tx_wait); + init_waitqueue_head(&dev->tx_flush_wait); + + dev->interface = -1; + dev->printer_cdev_open = 0; + dev->printer_status = PRINTER_NOT_ERROR; + dev->current_rx_req = NULL; + dev->current_rx_bytes = 0; + dev->current_rx_buf = NULL; + + return &dev->function; +} + +DECLARE_USB_FUNCTION_INIT(printer, gprinter_alloc_inst, gprinter_alloc); +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Craig Nadler"); + +#endif static int gprinter_setup(int count) { diff --git a/drivers/usb/gadget/function/u_printer.h b/drivers/usb/gadget/function/u_printer.h new file mode 100644 index 000000000000..b2338cacdfe4 --- /dev/null +++ b/drivers/usb/gadget/function/u_printer.h @@ -0,0 +1,30 @@ +/* + * u_printer.h + * + * Utility definitions for the printer function + * + * Copyright (c) 2015 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_PRINTER_H +#define U_PRINTER_H + +#include + +#define PNP_STRING_LEN 1024 + +struct f_printer_opts { + struct usb_function_instance func_inst; + int minor; + char pnp_string[PNP_STRING_LEN]; + unsigned q_len; +}; + +#endif /* U_PRINTER_H */ diff --git a/drivers/usb/gadget/legacy/printer.c b/drivers/usb/gadget/legacy/printer.c index 4d926d08df02..770b5041323e 100644 --- a/drivers/usb/gadget/legacy/printer.c +++ b/drivers/usb/gadget/legacy/printer.c @@ -33,6 +33,7 @@ static const char driver_desc [] = DRIVER_DESC; * This will be changed when f_printer is converted * to the new function interface. */ +#define USBF_PRINTER_INCLUDED #include "f_printer.c" /*-------------------------------------------------------------------------*/ -- cgit v1.2.3 From 69504f808d6770940f1404b6f6f43c50cfe0be72 Mon Sep 17 00:00:00 2001 From: Andrzej Pietrasiewicz Date: Tue, 3 Mar 2015 10:52:29 +0100 Subject: usb: gadget: printer: convert to new interface of f_printer The goal is to remove the old function interface, so its (only) user must be converted to the new interface. Signed-off-by: Andrzej Pietrasiewicz Signed-off-by: Felipe Balbi --- drivers/usb/gadget/legacy/Kconfig | 1 + drivers/usb/gadget/legacy/printer.c | 50 ++++++++++++++++++++++++++----------- 2 files changed, 37 insertions(+), 14 deletions(-) diff --git a/drivers/usb/gadget/legacy/Kconfig b/drivers/usb/gadget/legacy/Kconfig index 113c87e22117..d5a7102de696 100644 --- a/drivers/usb/gadget/legacy/Kconfig +++ b/drivers/usb/gadget/legacy/Kconfig @@ -301,6 +301,7 @@ config USB_MIDI_GADGET config USB_G_PRINTER tristate "Printer Gadget" select USB_LIBCOMPOSITE + select USB_F_PRINTER help The Printer Gadget channels data between the USB host and a userspace program driving the print engine. The user space diff --git a/drivers/usb/gadget/legacy/printer.c b/drivers/usb/gadget/legacy/printer.c index 770b5041323e..a8050f8cbe11 100644 --- a/drivers/usb/gadget/legacy/printer.c +++ b/drivers/usb/gadget/legacy/printer.c @@ -29,12 +29,7 @@ USB_GADGET_COMPOSITE_OPTIONS(); static const char shortname [] = "printer"; static const char driver_desc [] = DRIVER_DESC; -/* - * This will be changed when f_printer is converted - * to the new function interface. - */ -#define USBF_PRINTER_INCLUDED -#include "f_printer.c" +#include "u_printer.h" /*-------------------------------------------------------------------------*/ @@ -65,6 +60,9 @@ module_param(qlen, uint, S_IRUGO|S_IWUSR); #define QLEN qlen +static struct usb_function_instance *fi_printer; +static struct usb_function *f_printer; + /*-------------------------------------------------------------------------*/ /* @@ -131,6 +129,7 @@ static struct usb_configuration printer_cfg_driver = { static int __init printer_do_config(struct usb_configuration *c) { struct usb_gadget *gadget = c->cdev->gadget; + int status = 0; usb_ep_autoconfig_reset(gadget); @@ -142,20 +141,41 @@ static int __init printer_do_config(struct usb_configuration *c) printer_cfg_driver.bmAttributes |= USB_CONFIG_ATT_WAKEUP; } - return f_printer_bind_config(c, iPNPstring, pnp_string, QLEN, 0); + f_printer = usb_get_function(fi_printer); + if (IS_ERR(f_printer)) + return PTR_ERR(f_printer); + + status = usb_add_function(c, f_printer); + if (status < 0) + usb_put_function(f_printer); + + return status; } static int __init printer_bind(struct usb_composite_dev *cdev) { - int ret; + struct f_printer_opts *opts; + int ret, len; - ret = gprinter_setup(PRINTER_MINORS); - if (ret) - return ret; + fi_printer = usb_get_function_instance("printer"); + if (IS_ERR(fi_printer)) + return PTR_ERR(fi_printer); + + if (iPNPstring) + strlcpy(&pnp_string[2], iPNPstring, PNP_STRING_LEN - 2); + + len = strlen(pnp_string); + pnp_string[0] = (len >> 8) & 0xFF; + pnp_string[1] = len & 0xFF; + + opts = container_of(fi_printer, struct f_printer_opts, func_inst); + opts->minor = 0; + memcpy(opts->pnp_string, pnp_string, PNP_STRING_LEN); + opts->q_len = QLEN; ret = usb_string_ids_tab(cdev, strings); if (ret < 0) { - gprinter_cleanup(); + usb_put_function_instance(fi_printer); return ret; } device_desc.iManufacturer = strings[USB_GADGET_MANUFACTURER_IDX].id; @@ -164,7 +184,7 @@ static int __init printer_bind(struct usb_composite_dev *cdev) ret = usb_add_config(cdev, &printer_cfg_driver, printer_do_config); if (ret) { - gprinter_cleanup(); + usb_put_function_instance(fi_printer); return ret; } usb_composite_overwrite_options(cdev, &coverwrite); @@ -173,7 +193,9 @@ static int __init printer_bind(struct usb_composite_dev *cdev) static int __exit printer_unbind(struct usb_composite_dev *cdev) { - gprinter_cleanup(); + usb_put_function(f_printer); + usb_put_function_instance(fi_printer); + return 0; } -- cgit v1.2.3 From d85dc4824c5793f4b71225d7ba5b50e737045830 Mon Sep 17 00:00:00 2001 From: Andrzej Pietrasiewicz Date: Tue, 3 Mar 2015 10:52:30 +0100 Subject: usb: gadget: f_printer: remove compatibility layer There are no old interface users left, so it can be removed. Signed-off-by: Andrzej Pietrasiewicz Signed-off-by: Felipe Balbi --- drivers/usb/gadget/function/f_printer.c | 113 -------------------------------- 1 file changed, 113 deletions(-) diff --git a/drivers/usb/gadget/function/f_printer.c b/drivers/usb/gadget/function/f_printer.c index 93f4d4e61fef..7afe17d76f17 100644 --- a/drivers/usb/gadget/function/f_printer.c +++ b/drivers/usb/gadget/function/f_printer.c @@ -57,10 +57,8 @@ static int major, minors; static struct class *usb_gadget_class; -#ifndef USBF_PRINTER_INCLUDED static DEFINE_IDA(printer_ida); static DEFINE_MUTEX(printer_ida_lock); /* protects access do printer_ida */ -#endif /*-------------------------------------------------------------------------*/ @@ -1118,53 +1116,6 @@ fail_tx_reqs: } -#ifdef USBF_PRINTER_INCLUDED -static void printer_func_unbind(struct usb_configuration *c, - struct usb_function *f) -{ - struct printer_dev *dev; - struct usb_request *req; - - dev = func_to_printer(f); - - device_destroy(usb_gadget_class, MKDEV(major, dev->minor)); - - /* Remove Character Device */ - cdev_del(&dev->printer_cdev); - - /* we must already have been disconnected ... no i/o may be active */ - WARN_ON(!list_empty(&dev->tx_reqs_active)); - WARN_ON(!list_empty(&dev->rx_reqs_active)); - - /* Free all memory for this driver. */ - while (!list_empty(&dev->tx_reqs)) { - req = container_of(dev->tx_reqs.next, struct usb_request, - list); - list_del(&req->list); - printer_req_free(dev->in_ep, req); - } - - if (dev->current_rx_req != NULL) - printer_req_free(dev->out_ep, dev->current_rx_req); - - while (!list_empty(&dev->rx_reqs)) { - req = container_of(dev->rx_reqs.next, - struct usb_request, list); - list_del(&req->list); - printer_req_free(dev->out_ep, req); - } - - while (!list_empty(&dev->rx_buffers)) { - req = container_of(dev->rx_buffers.next, - struct usb_request, list); - list_del(&req->list); - printer_req_free(dev->out_ep, req); - } - usb_free_all_descriptors(f); - kfree(dev); -} -#endif - static int printer_func_set_alt(struct usb_function *f, unsigned intf, unsigned alt) { @@ -1189,68 +1140,6 @@ static void printer_func_disable(struct usb_function *f) spin_unlock_irqrestore(&dev->lock, flags); } -#ifdef USBF_PRINTER_INCLUDED -static int f_printer_bind_config(struct usb_configuration *c, char *pnp_str, - char *pnp_string, unsigned q_len, int minor) -{ - struct printer_dev *dev; - int status = -ENOMEM; - size_t len; - - if (minor >= minors) - return -ENOENT; - - dev = kzalloc(sizeof(*dev), GFP_KERNEL); - if (!dev) - return -ENOMEM; - - dev->pnp_string = pnp_string; - dev->minor = minor; - - dev->function.name = shortname; - dev->function.bind = printer_func_bind; - dev->function.setup = printer_func_setup; - dev->function.unbind = printer_func_unbind; - dev->function.set_alt = printer_func_set_alt; - dev->function.disable = printer_func_disable; - dev->function.req_match = gprinter_req_match; - INIT_LIST_HEAD(&dev->tx_reqs); - INIT_LIST_HEAD(&dev->rx_reqs); - INIT_LIST_HEAD(&dev->rx_buffers); - - if (pnp_str) - strlcpy(&dev->pnp_string[2], pnp_str, PNP_STRING_LEN - 2); - - len = strlen(pnp_string); - pnp_string[0] = (len >> 8) & 0xFF; - pnp_string[1] = len & 0xFF; - - spin_lock_init(&dev->lock); - mutex_init(&dev->lock_printer_io); - INIT_LIST_HEAD(&dev->tx_reqs_active); - INIT_LIST_HEAD(&dev->rx_reqs_active); - init_waitqueue_head(&dev->rx_wait); - init_waitqueue_head(&dev->tx_wait); - init_waitqueue_head(&dev->tx_flush_wait); - - dev->interface = -1; - dev->printer_cdev_open = 0; - dev->printer_status = PRINTER_NOT_ERROR; - dev->current_rx_req = NULL; - dev->current_rx_bytes = 0; - dev->current_rx_buf = NULL; - dev->q_len = q_len; - - status = usb_add_function(c, &dev->function); - if (status) { - kfree(dev); - return status; - } - - INFO(dev, "%s, version: " DRIVER_VERSION "\n", driver_desc); - return 0; -} -#else static inline int gprinter_get_minor(void) { return ida_simple_get(&printer_ida, 0, 0, GFP_KERNEL); @@ -1422,8 +1311,6 @@ DECLARE_USB_FUNCTION_INIT(printer, gprinter_alloc_inst, gprinter_alloc); MODULE_LICENSE("GPL"); MODULE_AUTHOR("Craig Nadler"); -#endif - static int gprinter_setup(int count) { int status; -- cgit v1.2.3 From a2a8e48a94c78c72b5dd1e4c8d190c5c54aca7a4 Mon Sep 17 00:00:00 2001 From: Andrzej Pietrasiewicz Date: Tue, 3 Mar 2015 10:52:31 +0100 Subject: usb: gadget: printer: use module_usb_composite_driver helper macro Substitute some boilerplate code with a dedicated macro. Signed-off-by: Andrzej Pietrasiewicz Signed-off-by: Felipe Balbi --- drivers/usb/gadget/legacy/printer.c | 14 +------------- 1 file changed, 1 insertion(+), 13 deletions(-) diff --git a/drivers/usb/gadget/legacy/printer.c b/drivers/usb/gadget/legacy/printer.c index a8050f8cbe11..d5b6ee725a2a 100644 --- a/drivers/usb/gadget/legacy/printer.c +++ b/drivers/usb/gadget/legacy/printer.c @@ -208,19 +208,7 @@ static __refdata struct usb_composite_driver printer_driver = { .unbind = printer_unbind, }; -static int __init -init(void) -{ - return usb_composite_probe(&printer_driver); -} -module_init(init); - -static void __exit -cleanup(void) -{ - usb_composite_unregister(&printer_driver); -} -module_exit(cleanup); +module_usb_composite_driver(printer_driver); MODULE_DESCRIPTION(DRIVER_DESC); MODULE_AUTHOR("Craig Nadler"); -- cgit v1.2.3 From ee1cd515e889d222f5a7397fead0a9db1214edea Mon Sep 17 00:00:00 2001 From: Andrzej Pietrasiewicz Date: Tue, 3 Mar 2015 10:52:32 +0100 Subject: usb: gadget: printer: add configfs support Add support for configfs interface so that f_printer can be used as a component of usb gadgets composed with it. Signed-off-by: Andrzej Pietrasiewicz Signed-off-by: Felipe Balbi --- .../ABI/testing/configfs-usb-gadget-printer | 9 ++ Documentation/usb/gadget-testing.txt | 47 ++++++++ drivers/usb/gadget/Kconfig | 13 +++ drivers/usb/gadget/function/f_printer.c | 130 ++++++++++++++++++++- drivers/usb/gadget/function/u_printer.h | 7 ++ 5 files changed, 204 insertions(+), 2 deletions(-) create mode 100644 Documentation/ABI/testing/configfs-usb-gadget-printer diff --git a/Documentation/ABI/testing/configfs-usb-gadget-printer b/Documentation/ABI/testing/configfs-usb-gadget-printer new file mode 100644 index 000000000000..6b0714e3c605 --- /dev/null +++ b/Documentation/ABI/testing/configfs-usb-gadget-printer @@ -0,0 +1,9 @@ +What: /config/usb-gadget/gadget/functions/printer.name +Date: Apr 2015 +KernelVersion: 4.1 +Description: + The attributes: + + pnp_string - Data to be passed to the host in pnp string + q_len - Number of requests per endpoint + diff --git a/Documentation/usb/gadget-testing.txt b/Documentation/usb/gadget-testing.txt index 076ac7ba7f93..f45b2bf4b41d 100644 --- a/Documentation/usb/gadget-testing.txt +++ b/Documentation/usb/gadget-testing.txt @@ -19,6 +19,7 @@ provided by gadgets. 16. UAC1 function 17. UAC2 function 18. UVC function +19. PRINTER function 1. ACM function @@ -726,3 +727,49 @@ with these patches: http://www.spinics.net/lists/linux-usb/msg99220.html host: luvcview -f yuv + +19. PRINTER function +==================== + +The function is provided by usb_f_printer.ko module. + +Function-specific configfs interface +------------------------------------ + +The function name to use when creating the function directory is "printer". +The printer function provides these attributes in its function directory: + + pnp_string - Data to be passed to the host in pnp string + q_len - Number of requests per endpoint + +Testing the PRINTER function +---------------------------- + +The most basic testing: + +device: run the gadget +# ls -l /devices/virtual/usb_printer_gadget/ + +should show g_printer. + +If udev is active, then /dev/g_printer should appear automatically. + +host: + +If udev is active, then e.g. /dev/usb/lp0 should appear. + +host->device transmission: + +device: +# cat /dev/g_printer +host: +# cat > /dev/usb/lp0 + +device->host transmission: + +# cat > /dev/g_printer +host: +# cat /dev/usb/lp0 + +More advanced testing can be done with the prn_example +described in Documentation/usb/gadget-printer.txt. diff --git a/drivers/usb/gadget/Kconfig b/drivers/usb/gadget/Kconfig index 9d507cf98f94..3bb0e67fded2 100644 --- a/drivers/usb/gadget/Kconfig +++ b/drivers/usb/gadget/Kconfig @@ -437,6 +437,19 @@ config USB_CONFIGFS_F_UVC device. It provides a userspace API to process UVC control requests and stream video data to the host. +config USB_CONFIGFS_F_PRINTER + bool "Printer function" + select USB_F_PRINTER + help + The Printer function channels data between the USB host and a + userspace program driving the print engine. The user space + program reads and writes the device file /dev/g_printer to + receive or send printer data. It can use ioctl calls to + the device file to get or set printer status. + + For more information, see Documentation/usb/gadget_printer.txt + which includes sample code for accessing the device file. + source "drivers/usb/gadget/legacy/Kconfig" endchoice diff --git a/drivers/usb/gadget/function/f_printer.c b/drivers/usb/gadget/function/f_printer.c index 7afe17d76f17..757fcf070013 100644 --- a/drivers/usb/gadget/function/f_printer.c +++ b/drivers/usb/gadget/function/f_printer.c @@ -1140,6 +1140,117 @@ static void printer_func_disable(struct usb_function *f) spin_unlock_irqrestore(&dev->lock, flags); } +static inline struct f_printer_opts +*to_f_printer_opts(struct config_item *item) +{ + return container_of(to_config_group(item), struct f_printer_opts, + func_inst.group); +} + +CONFIGFS_ATTR_STRUCT(f_printer_opts); +CONFIGFS_ATTR_OPS(f_printer_opts); + +static void printer_attr_release(struct config_item *item) +{ + struct f_printer_opts *opts = to_f_printer_opts(item); + + usb_put_function_instance(&opts->func_inst); +} + +static struct configfs_item_operations printer_item_ops = { + .release = printer_attr_release, + .show_attribute = f_printer_opts_attr_show, + .store_attribute = f_printer_opts_attr_store, +}; + +static ssize_t f_printer_opts_pnp_string_show(struct f_printer_opts *opts, + char *page) +{ + int result; + + mutex_lock(&opts->lock); + result = strlcpy(page, opts->pnp_string + 2, PNP_STRING_LEN - 2); + mutex_unlock(&opts->lock); + + return result; +} + +static ssize_t f_printer_opts_pnp_string_store(struct f_printer_opts *opts, + const char *page, size_t len) +{ + int result, l; + + mutex_lock(&opts->lock); + result = strlcpy(opts->pnp_string + 2, page, PNP_STRING_LEN - 2); + l = strlen(opts->pnp_string + 2) + 2; + opts->pnp_string[0] = (l >> 8) & 0xFF; + opts->pnp_string[1] = l & 0xFF; + mutex_unlock(&opts->lock); + + return result; +} + +static struct f_printer_opts_attribute f_printer_opts_pnp_string = + __CONFIGFS_ATTR(pnp_string, S_IRUGO | S_IWUSR, + f_printer_opts_pnp_string_show, + f_printer_opts_pnp_string_store); + +static ssize_t f_printer_opts_q_len_show(struct f_printer_opts *opts, + char *page) +{ + int result; + + mutex_lock(&opts->lock); + result = sprintf(page, "%d\n", opts->q_len); + mutex_unlock(&opts->lock); + + return result; +} + +static ssize_t f_printer_opts_q_len_store(struct f_printer_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 > 65535) { + ret = -EINVAL; + goto end; + } + + opts->q_len = (unsigned)num; + ret = len; +end: + mutex_unlock(&opts->lock); + return ret; +} + +static struct f_printer_opts_attribute f_printer_opts_q_len = + __CONFIGFS_ATTR(q_len, S_IRUGO | S_IWUSR, f_printer_opts_q_len_show, + f_printer_opts_q_len_store); + +static struct configfs_attribute *printer_attrs[] = { + &f_printer_opts_pnp_string.attr, + &f_printer_opts_q_len.attr, + NULL, +}; + +static struct config_item_type printer_func_type = { + .ct_item_ops = &printer_item_ops, + .ct_attrs = printer_attrs, + .ct_owner = THIS_MODULE, +}; + static inline int gprinter_get_minor(void) { return ida_simple_get(&printer_ida, 0, 0, GFP_KERNEL); @@ -1180,6 +1291,7 @@ static struct usb_function_instance *gprinter_alloc_inst(void) if (!opts) return ERR_PTR(-ENOMEM); + mutex_init(&opts->lock); opts->func_inst.free_func_inst = gprinter_free_inst; ret = &opts->func_inst; @@ -1201,6 +1313,8 @@ static struct usb_function_instance *gprinter_alloc_inst(void) if (idr_is_empty(&printer_ida.idr)) gprinter_cleanup(); } + config_group_init_type_name(&opts->func_inst.group, "", + &printer_func_type); unlock: mutex_unlock(&printer_ida_lock); @@ -1210,8 +1324,13 @@ unlock: static void gprinter_free(struct usb_function *f) { struct printer_dev *dev = func_to_printer(f); + struct f_printer_opts *opts; + opts = container_of(f->fi, struct f_printer_opts, func_inst); kfree(dev); + mutex_lock(&opts->lock); + --opts->refcnt; + mutex_unlock(&opts->lock); } static void printer_func_unbind(struct usb_configuration *c, @@ -1265,16 +1384,23 @@ static struct usb_function *gprinter_alloc(struct usb_function_instance *fi) opts = container_of(fi, struct f_printer_opts, func_inst); - if (opts->minor >= minors) + mutex_lock(&opts->lock); + if (opts->minor >= minors) { + mutex_unlock(&opts->lock); return ERR_PTR(-ENOENT); + } dev = kzalloc(sizeof(*dev), GFP_KERNEL); - if (!dev) + if (!dev) { + mutex_unlock(&opts->lock); return ERR_PTR(-ENOMEM); + } + ++opts->refcnt; dev->minor = opts->minor; dev->pnp_string = opts->pnp_string; dev->q_len = opts->q_len; + mutex_unlock(&opts->lock); dev->function.name = "printer"; dev->function.bind = printer_func_bind; diff --git a/drivers/usb/gadget/function/u_printer.h b/drivers/usb/gadget/function/u_printer.h index b2338cacdfe4..0e2c49d4274e 100644 --- a/drivers/usb/gadget/function/u_printer.h +++ b/drivers/usb/gadget/function/u_printer.h @@ -25,6 +25,13 @@ struct f_printer_opts { int minor; char pnp_string[PNP_STRING_LEN]; unsigned q_len; + + /* + * Protect the data from concurrent access by read/write + * and create symlink/remove symlink + */ + struct mutex lock; + int refcnt; }; #endif /* U_PRINTER_H */ -- cgit v1.2.3 From 983f3cabf6677340dd5b9aec5440b427b8c47ad9 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Wed, 11 Mar 2015 10:18:40 -0500 Subject: usb: musb: dsps: request phy using our device pointer musb shouldn't have of_node and phy phandle is passed to dsps device, not musb's. Signed-off-by: Felipe Balbi --- drivers/usb/musb/musb_dsps.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/usb/musb/musb_dsps.c b/drivers/usb/musb/musb_dsps.c index baa757ba1353..a528d3be70c5 100644 --- a/drivers/usb/musb/musb_dsps.c +++ b/drivers/usb/musb/musb_dsps.c @@ -432,7 +432,7 @@ static int dsps_musb_init(struct musb *musb) musb->ctrl_base = reg_base; /* NOP driver needs change if supporting dual instance */ - musb->xceiv = devm_usb_get_phy_by_phandle(dev, "phys", 0); + musb->xceiv = devm_usb_get_phy_by_phandle(dev->parent, "phys", 0); if (IS_ERR(musb->xceiv)) return PTR_ERR(musb->xceiv); -- cgit v1.2.3 From 33c300cb90a6c0072507a949f78c4728238a81f0 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Tue, 10 Mar 2015 10:42:12 -0500 Subject: usb: musb: dsps: don't fake of_node to musb core If we pass our own of_node to musb_core, at least pinctrl settings will be duplicated, meaning that pinctrl framework will try to select default pin state for musb_core when they were already requested by musb-dsps. A Warning will be printed however things will still work. Reported-by: Tony Lindgren Signed-off-by: Felipe Balbi --- drivers/usb/musb/musb_dsps.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/usb/musb/musb_dsps.c b/drivers/usb/musb/musb_dsps.c index a528d3be70c5..09648b4e40c2 100644 --- a/drivers/usb/musb/musb_dsps.c +++ b/drivers/usb/musb/musb_dsps.c @@ -697,7 +697,6 @@ static int dsps_create_musb_pdev(struct dsps_glue *glue, musb->dev.parent = dev; musb->dev.dma_mask = &musb_dmamask; musb->dev.coherent_dma_mask = musb_dmamask; - musb->dev.of_node = of_node_get(dn); glue->musb = musb; -- cgit v1.2.3 From 06ed0de5188c9ef6553b2824b6cdd767ad46ece5 Mon Sep 17 00:00:00 2001 From: Masanari Iida Date: Tue, 10 Mar 2015 22:37:46 +0900 Subject: usb: gadget: Fix typo fond in Documentation/Docbook/gadget.xml This patch fix some spelling typo found in gadget.xml. It is because this file is generated from comments in sources, I had to fix comments in the source, instead of xml file itself. Signed-off-by: Masanari Iida Signed-off-by: Felipe Balbi --- drivers/usb/gadget/composite.c | 4 ++-- include/linux/usb/gadget.h | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/usb/gadget/composite.c b/drivers/usb/gadget/composite.c index 4d25e11b1f72..4e3447bbd097 100644 --- a/drivers/usb/gadget/composite.c +++ b/drivers/usb/gadget/composite.c @@ -1161,11 +1161,11 @@ static struct usb_gadget_string_container *copy_gadget_strings( * This function will create a deep copy of usb_gadget_strings and usb_string * and attach it to the cdev. The actual string (usb_string.s) will not be * copied but only a referenced will be made. The struct usb_gadget_strings - * array may contain multiple languges and should be NULL terminated. + * array may contain multiple languages and should be NULL terminated. * The ->language pointer of each struct usb_gadget_strings has to contain the * same amount of entries. * For instance: sp[0] is en-US, sp[1] is es-ES. It is expected that the first - * usb_string entry of es-ES containts the translation of the first usb_string + * usb_string entry of es-ES contains the translation of the first usb_string * entry of en-US. Therefore both entries become the same id assign. */ struct usb_string *usb_gstrings_attach(struct usb_composite_dev *cdev, diff --git a/include/linux/usb/gadget.h b/include/linux/usb/gadget.h index e2f00fd8cd47..02476b3a1109 100644 --- a/include/linux/usb/gadget.h +++ b/include/linux/usb/gadget.h @@ -190,7 +190,7 @@ struct usb_ep { * @ep:the endpoint being configured * @maxpacket_limit:value of maximum packet size limit * - * This function shoud be used only in UDC drivers to initialize endpoint + * This function should be used only in UDC drivers to initialize endpoint * (usually in probe function). */ static inline void usb_ep_set_maxpacket_limit(struct usb_ep *ep, -- cgit v1.2.3 From fddc26f573e28dde62013cd5d8d5c60aa85e74a4 Mon Sep 17 00:00:00 2001 From: Tal Shorer Date: Fri, 6 Mar 2015 21:53:32 +0200 Subject: usb: gadget: f_mass_storage: use defined constant instead of numeric value replace numeric value with TYPE_NO_LUN (defined in ) Signed-off-by: Tal Shorer Acked-by: Michal Nazarewicz Signed-off-by: Felipe Balbi --- drivers/usb/gadget/function/f_mass_storage.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/usb/gadget/function/f_mass_storage.c b/drivers/usb/gadget/function/f_mass_storage.c index 811929cd4c9e..6d5ca2b2d052 100644 --- a/drivers/usb/gadget/function/f_mass_storage.c +++ b/drivers/usb/gadget/function/f_mass_storage.c @@ -1085,7 +1085,7 @@ static int do_inquiry(struct fsg_common *common, struct fsg_buffhd *bh) if (!curlun) { /* Unsupported LUNs are okay */ common->bad_lun_okay = 1; memset(buf, 0, 36); - buf[0] = 0x7f; /* Unsupported, no device-type */ + buf[0] = TYPE_NO_LUN; /* Unsupported, no device-type */ buf[4] = 31; /* Additional length */ return 36; } -- cgit v1.2.3 From 2b08977b8dcb18c75b4972184c9ab54937119cba Mon Sep 17 00:00:00 2001 From: Mickael Maison Date: Sun, 8 Mar 2015 19:24:02 +0000 Subject: usb: phy: ab8500: fixed comment typo Fixed a comment typo in drivers/usb/phy/phy-ab8500-usb.c Signed-off-by: Mickael Maison Signed-off-by: Felipe Balbi --- drivers/usb/phy/phy-ab8500-usb.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/usb/phy/phy-ab8500-usb.c b/drivers/usb/phy/phy-ab8500-usb.c index 0b1bd2369293..59cccfadae96 100644 --- a/drivers/usb/phy/phy-ab8500-usb.c +++ b/drivers/usb/phy/phy-ab8500-usb.c @@ -893,7 +893,7 @@ static int abx500_usb_link_status_update(struct ab8500_usb *ab) /* * Disconnection Sequence: - * 1. Disconect Interrupt + * 1. Disconnect Interrupt * 2. Disable regulators * 3. Disable AB clock * 4. Disable the Phy -- cgit v1.2.3 From 656e7c36bd70e115266cf280e0ff049506ceb16c Mon Sep 17 00:00:00 2001 From: Mickael Maison Date: Sun, 8 Mar 2015 19:26:52 +0000 Subject: usb: phy: fixed comment typo Fixed a comment typo in drivers/usb/phy/of.c Signed-off-by: Mickael Maison Signed-off-by: Felipe Balbi --- drivers/usb/phy/of.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/usb/phy/of.c b/drivers/usb/phy/of.c index 7ea0154da9d5..66ffa82457a8 100644 --- a/drivers/usb/phy/of.c +++ b/drivers/usb/phy/of.c @@ -27,7 +27,7 @@ static const char *const usbphy_modes[] = { * @np: Pointer to the given device_node * * The function gets phy interface string from property 'phy_type', - * and returns the correspondig enum usb_phy_interface + * and returns the corresponding enum usb_phy_interface */ enum usb_phy_interface of_usb_get_phy_mode(struct device_node *np) { -- cgit v1.2.3 From 227ab58cff3c26ec279598112887d6bcca677a0c Mon Sep 17 00:00:00 2001 From: Sylvain Rochet Date: Thu, 12 Feb 2015 18:54:04 +0100 Subject: usb: gadget: atmel_usba_udc: Fixed vbus_prev initial state If vbus gpio is high at init, we should set vbus_prev to true accordingly to the current vbus state. Without that, we skip the first vbus interrupt because the saved vbus state is not consistent. Signed-off-by: Sylvain Rochet Acked-by: Nicolas Ferre Acked-by: Boris Brezillon Signed-off-by: Felipe Balbi --- drivers/usb/gadget/udc/atmel_usba_udc.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/usb/gadget/udc/atmel_usba_udc.c b/drivers/usb/gadget/udc/atmel_usba_udc.c index d79cb35dbf8a..e63c6fc8173b 100644 --- a/drivers/usb/gadget/udc/atmel_usba_udc.c +++ b/drivers/usb/gadget/udc/atmel_usba_udc.c @@ -1811,6 +1811,8 @@ static int atmel_usba_start(struct usb_gadget *gadget, toggle_bias(udc, 1); usba_writel(udc, CTRL, USBA_ENABLE_MASK); usba_int_enb_set(udc, USBA_END_OF_RESET); + + udc->vbus_prev = 1; } spin_unlock_irqrestore(&udc->lock, flags); -- cgit v1.2.3 From bb0a203c3a4e37b4bbb1f8ef55a855618dbba265 Mon Sep 17 00:00:00 2001 From: Sylvain Rochet Date: Thu, 12 Feb 2015 18:54:05 +0100 Subject: usb: gadget: atmel_usba_udc: Request an auto disabled Vbus signal IRQ Vbus IRQ handler needs a started UDC driver to work because it uses udc->driver, which is set by the UDC start handler. The previous way chosen was to return from interrupt if udc->driver is NULL using a spinlock around the check. We now request an auto disabled (IRQ_NOAUTOEN) Vbus signal IRQ instead of an auto enabled IRQ followed by disable_irq(). This way we remove the very small timeslot of enabled IRQ which existed previously between request() and disable(). We don't need anymore to check if udc->driver is NULL in IRQ handler. Signed-off-by: Sylvain Rochet Suggested-by: Boris Brezillon Acked-by: Boris Brezillon Acked-by: Nicolas Ferre Signed-off-by: Felipe Balbi --- drivers/usb/gadget/udc/atmel_usba_udc.c | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/drivers/usb/gadget/udc/atmel_usba_udc.c b/drivers/usb/gadget/udc/atmel_usba_udc.c index e63c6fc8173b..bbbd5f11f767 100644 --- a/drivers/usb/gadget/udc/atmel_usba_udc.c +++ b/drivers/usb/gadget/udc/atmel_usba_udc.c @@ -1749,10 +1749,6 @@ static irqreturn_t usba_vbus_irq(int irq, void *devid) spin_lock(&udc->lock); - /* May happen if Vbus pin toggles during probe() */ - if (!udc->driver) - goto out; - vbus = vbus_is_present(udc); if (vbus != udc->vbus_prev) { if (vbus) { @@ -1773,7 +1769,6 @@ static irqreturn_t usba_vbus_irq(int irq, void *devid) udc->vbus_prev = vbus; } -out: spin_unlock(&udc->lock); return IRQ_HANDLED; @@ -2113,6 +2108,8 @@ static int usba_udc_probe(struct platform_device *pdev) if (gpio_is_valid(udc->vbus_pin)) { if (!devm_gpio_request(&pdev->dev, udc->vbus_pin, "atmel_usba_udc")) { + irq_set_status_flags(gpio_to_irq(udc->vbus_pin), + IRQ_NOAUTOEN); ret = devm_request_irq(&pdev->dev, gpio_to_irq(udc->vbus_pin), usba_vbus_irq, 0, @@ -2122,8 +2119,6 @@ static int usba_udc_probe(struct platform_device *pdev) dev_warn(&udc->pdev->dev, "failed to request vbus irq; " "assuming always on\n"); - } else { - disable_irq(gpio_to_irq(udc->vbus_pin)); } } else { /* gpio_request fail so use -EINVAL for gpio_is_valid */ -- cgit v1.2.3 From a64ef71ddc13fc76a6f4af8c61e0106a62004380 Mon Sep 17 00:00:00 2001 From: Sylvain Rochet Date: Thu, 12 Feb 2015 18:54:06 +0100 Subject: usb: gadget: atmel_usba_udc: condition clocks to vbus state If USB PLL is not necessary for other USB drivers (e.g. OHCI and EHCI) we will reduce power consumption by switching off the USB PLL if no USB Host is currently connected to this USB Device. We are using Vbus GPIO signal to detect Host presence. If Vbus signal is not available then the device stays continuously clocked. Signed-off-by: Sylvain Rochet Acked-by: Nicolas Ferre Acked-by: Boris Brezillon Signed-off-by: Felipe Balbi --- drivers/usb/gadget/udc/atmel_usba_udc.c | 144 +++++++++++++++++++++----------- drivers/usb/gadget/udc/atmel_usba_udc.h | 4 + 2 files changed, 100 insertions(+), 48 deletions(-) diff --git a/drivers/usb/gadget/udc/atmel_usba_udc.c b/drivers/usb/gadget/udc/atmel_usba_udc.c index bbbd5f11f767..999e2f2cd2cc 100644 --- a/drivers/usb/gadget/udc/atmel_usba_udc.c +++ b/drivers/usb/gadget/udc/atmel_usba_udc.c @@ -1739,7 +1739,72 @@ static irqreturn_t usba_udc_irq(int irq, void *devid) return IRQ_HANDLED; } -static irqreturn_t usba_vbus_irq(int irq, void *devid) +static int start_clock(struct usba_udc *udc) +{ + int ret; + + if (udc->clocked) + return 0; + + ret = clk_prepare_enable(udc->pclk); + if (ret) + return ret; + ret = clk_prepare_enable(udc->hclk); + if (ret) { + clk_disable_unprepare(udc->pclk); + return ret; + } + + udc->clocked = true; + return 0; +} + +static void stop_clock(struct usba_udc *udc) +{ + if (!udc->clocked) + return; + + clk_disable_unprepare(udc->hclk); + clk_disable_unprepare(udc->pclk); + + udc->clocked = false; +} + +static int usba_start(struct usba_udc *udc) +{ + unsigned long flags; + int ret; + + ret = start_clock(udc); + if (ret) + return ret; + + spin_lock_irqsave(&udc->lock, flags); + toggle_bias(udc, 1); + usba_writel(udc, CTRL, USBA_ENABLE_MASK); + usba_int_enb_set(udc, USBA_END_OF_RESET); + spin_unlock_irqrestore(&udc->lock, flags); + + return 0; +} + +static void usba_stop(struct usba_udc *udc) +{ + unsigned long flags; + + spin_lock_irqsave(&udc->lock, flags); + udc->gadget.speed = USB_SPEED_UNKNOWN; + reset_all_endpoints(udc); + + /* This will also disable the DP pullup */ + toggle_bias(udc, 0); + usba_writel(udc, CTRL, USBA_DISABLE_MASK); + spin_unlock_irqrestore(&udc->lock, flags); + + stop_clock(udc); +} + +static irqreturn_t usba_vbus_irq_thread(int irq, void *devid) { struct usba_udc *udc = devid; int vbus; @@ -1747,30 +1812,22 @@ static irqreturn_t usba_vbus_irq(int irq, void *devid) /* debounce */ udelay(10); - spin_lock(&udc->lock); + mutex_lock(&udc->vbus_mutex); vbus = vbus_is_present(udc); if (vbus != udc->vbus_prev) { if (vbus) { - toggle_bias(udc, 1); - usba_writel(udc, CTRL, USBA_ENABLE_MASK); - usba_int_enb_set(udc, USBA_END_OF_RESET); + usba_start(udc); } else { - udc->gadget.speed = USB_SPEED_UNKNOWN; - reset_all_endpoints(udc); - toggle_bias(udc, 0); - usba_writel(udc, CTRL, USBA_DISABLE_MASK); - if (udc->driver->disconnect) { - spin_unlock(&udc->lock); + usba_stop(udc); + + if (udc->driver->disconnect) udc->driver->disconnect(&udc->gadget); - spin_lock(&udc->lock); - } } udc->vbus_prev = vbus; } - spin_unlock(&udc->lock); - + mutex_unlock(&udc->vbus_mutex); return IRQ_HANDLED; } @@ -1782,57 +1839,47 @@ static int atmel_usba_start(struct usb_gadget *gadget, unsigned long flags; spin_lock_irqsave(&udc->lock, flags); - udc->devstatus = 1 << USB_DEVICE_SELF_POWERED; udc->driver = driver; spin_unlock_irqrestore(&udc->lock, flags); - ret = clk_prepare_enable(udc->pclk); - if (ret) - return ret; - ret = clk_prepare_enable(udc->hclk); - if (ret) { - clk_disable_unprepare(udc->pclk); - return ret; - } + mutex_lock(&udc->vbus_mutex); - udc->vbus_prev = 0; if (gpio_is_valid(udc->vbus_pin)) enable_irq(gpio_to_irq(udc->vbus_pin)); /* If Vbus is present, enable the controller and wait for reset */ - spin_lock_irqsave(&udc->lock, flags); - if (vbus_is_present(udc) && udc->vbus_prev == 0) { - toggle_bias(udc, 1); - usba_writel(udc, CTRL, USBA_ENABLE_MASK); - usba_int_enb_set(udc, USBA_END_OF_RESET); - - udc->vbus_prev = 1; + udc->vbus_prev = vbus_is_present(udc); + if (udc->vbus_prev) { + ret = usba_start(udc); + if (ret) + goto err; } - spin_unlock_irqrestore(&udc->lock, flags); + mutex_unlock(&udc->vbus_mutex); return 0; + +err: + if (gpio_is_valid(udc->vbus_pin)) + disable_irq(gpio_to_irq(udc->vbus_pin)); + + mutex_unlock(&udc->vbus_mutex); + + spin_lock_irqsave(&udc->lock, flags); + udc->devstatus &= ~(1 << USB_DEVICE_SELF_POWERED); + udc->driver = NULL; + spin_unlock_irqrestore(&udc->lock, flags); + return ret; } static int atmel_usba_stop(struct usb_gadget *gadget) { struct usba_udc *udc = container_of(gadget, struct usba_udc, gadget); - unsigned long flags; if (gpio_is_valid(udc->vbus_pin)) disable_irq(gpio_to_irq(udc->vbus_pin)); - spin_lock_irqsave(&udc->lock, flags); - udc->gadget.speed = USB_SPEED_UNKNOWN; - reset_all_endpoints(udc); - spin_unlock_irqrestore(&udc->lock, flags); - - /* This will also disable the DP pullup */ - toggle_bias(udc, 0); - usba_writel(udc, CTRL, USBA_DISABLE_MASK); - - clk_disable_unprepare(udc->hclk); - clk_disable_unprepare(udc->pclk); + usba_stop(udc); udc->driver = NULL; @@ -2054,6 +2101,7 @@ static int usba_udc_probe(struct platform_device *pdev) return PTR_ERR(hclk); spin_lock_init(&udc->lock); + mutex_init(&udc->vbus_mutex); udc->pdev = pdev; udc->pclk = pclk; udc->hclk = hclk; @@ -2110,9 +2158,9 @@ static int usba_udc_probe(struct platform_device *pdev) if (!devm_gpio_request(&pdev->dev, udc->vbus_pin, "atmel_usba_udc")) { irq_set_status_flags(gpio_to_irq(udc->vbus_pin), IRQ_NOAUTOEN); - ret = devm_request_irq(&pdev->dev, - gpio_to_irq(udc->vbus_pin), - usba_vbus_irq, 0, + ret = devm_request_threaded_irq(&pdev->dev, + gpio_to_irq(udc->vbus_pin), NULL, + usba_vbus_irq_thread, IRQF_ONESHOT, "atmel_usba_udc", udc); if (ret) { udc->vbus_pin = -ENODEV; diff --git a/drivers/usb/gadget/udc/atmel_usba_udc.h b/drivers/usb/gadget/udc/atmel_usba_udc.h index 497cd18836f3..085749a649e1 100644 --- a/drivers/usb/gadget/udc/atmel_usba_udc.h +++ b/drivers/usb/gadget/udc/atmel_usba_udc.h @@ -313,6 +313,9 @@ struct usba_udc { /* Protect hw registers from concurrent modifications */ spinlock_t lock; + /* Mutex to prevent concurrent start or stop */ + struct mutex vbus_mutex; + void __iomem *regs; void __iomem *fifo; @@ -328,6 +331,7 @@ struct usba_udc { struct clk *hclk; struct usba_ep *usba_ep; bool bias_pulse_needed; + bool clocked; u16 devstatus; -- cgit v1.2.3 From 112bf24471b50f806cbcbead7bd485da70401b83 Mon Sep 17 00:00:00 2001 From: Sylvain Rochet Date: Thu, 12 Feb 2015 18:54:07 +0100 Subject: usb: gadget: atmel_usba_udc: Add suspend/resume with wakeup support This patch add suspend/resume with wakeup support for Atmel USBA. On suspend: We stay continuously clocked if Vbus signal is not available. If Vbus signal is available we set the Vbus signal as a wake up source then we stop the USBA itself and all clocks used by USBA. On resume: We recover clocks and USBA if we stopped them. If a device is currently connected at resume time we enable the controller. Signed-off-by: Sylvain Rochet Acked-by: Boris Brezillon Signed-off-by: Felipe Balbi --- drivers/usb/gadget/udc/atmel_usba_udc.c | 57 +++++++++++++++++++++++++++++++++ 1 file changed, 57 insertions(+) diff --git a/drivers/usb/gadget/udc/atmel_usba_udc.c b/drivers/usb/gadget/udc/atmel_usba_udc.c index 999e2f2cd2cc..d019b6c9d74d 100644 --- a/drivers/usb/gadget/udc/atmel_usba_udc.c +++ b/drivers/usb/gadget/udc/atmel_usba_udc.c @@ -2177,6 +2177,7 @@ static int usba_udc_probe(struct platform_device *pdev) ret = usb_add_gadget_udc(&pdev->dev, &udc->gadget); if (ret) return ret; + device_init_wakeup(&pdev->dev, 1); usba_init_debugfs(udc); for (i = 1; i < udc->num_ep; i++) @@ -2192,6 +2193,7 @@ static int __exit usba_udc_remove(struct platform_device *pdev) udc = platform_get_drvdata(pdev); + device_init_wakeup(&pdev->dev, 0); usb_del_gadget_udc(&udc->gadget); for (i = 1; i < udc->num_ep; i++) @@ -2201,10 +2203,65 @@ static int __exit usba_udc_remove(struct platform_device *pdev) return 0; } +#ifdef CONFIG_PM +static int usba_udc_suspend(struct device *dev) +{ + struct usba_udc *udc = dev_get_drvdata(dev); + + /* Not started */ + if (!udc->driver) + return 0; + + mutex_lock(&udc->vbus_mutex); + + if (!device_may_wakeup(dev)) { + usba_stop(udc); + goto out; + } + + /* + * Device may wake up. We stay clocked if we failed + * to request vbus irq, assuming always on. + */ + if (gpio_is_valid(udc->vbus_pin)) { + usba_stop(udc); + enable_irq_wake(gpio_to_irq(udc->vbus_pin)); + } + +out: + mutex_unlock(&udc->vbus_mutex); + return 0; +} + +static int usba_udc_resume(struct device *dev) +{ + struct usba_udc *udc = dev_get_drvdata(dev); + + /* Not started */ + if (!udc->driver) + return 0; + + if (device_may_wakeup(dev) && gpio_is_valid(udc->vbus_pin)) + disable_irq_wake(gpio_to_irq(udc->vbus_pin)); + + /* If Vbus is present, enable the controller and wait for reset */ + mutex_lock(&udc->vbus_mutex); + udc->vbus_prev = vbus_is_present(udc); + if (udc->vbus_prev) + usba_start(udc); + mutex_unlock(&udc->vbus_mutex); + + return 0; +} +#endif + +static SIMPLE_DEV_PM_OPS(usba_udc_pm_ops, usba_udc_suspend, usba_udc_resume); + static struct platform_driver udc_driver = { .remove = __exit_p(usba_udc_remove), .driver = { .name = "atmel_usba_udc", + .pm = &usba_udc_pm_ops, .of_match_table = of_match_ptr(atmel_udc_dt_ids), }, }; -- cgit v1.2.3 From fa84acf0f6142e7f35da785e25ee978518ff21ac Mon Sep 17 00:00:00 2001 From: Rasmus Villemoes Date: Fri, 6 Feb 2015 00:42:00 +0100 Subject: usb: gadget: dummy-hcd: Remove utf8 from format string Not everybody uses a utf8 locale (unfortunately), so let's avoid non-ascii characters in the kernel log. Replace the 3-byte utf8 sequence with a 3-byte ascii equivalent. Signed-off-by: Rasmus Villemoes Signed-off-by: Felipe Balbi --- drivers/usb/gadget/udc/dummy_hcd.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/usb/gadget/udc/dummy_hcd.c b/drivers/usb/gadget/udc/dummy_hcd.c index 8dda48445f6f..7592db7824c6 100644 --- a/drivers/usb/gadget/udc/dummy_hcd.c +++ b/drivers/usb/gadget/udc/dummy_hcd.c @@ -2631,7 +2631,7 @@ static int __init init(void) return -EINVAL; if (mod_data.num < 1 || mod_data.num > MAX_NUM_UDC) { - pr_err("Number of emulated UDC must be in range of 1…%d\n", + pr_err("Number of emulated UDC must be in range of 1...%d\n", MAX_NUM_UDC); return -EINVAL; } -- cgit v1.2.3 From f4e4f8dae3753685e577143e8116cbac52f13cc4 Mon Sep 17 00:00:00 2001 From: Robert Baldyga Date: Tue, 10 Feb 2015 17:30:36 +0100 Subject: usb: gadget: f_hid: remove unnecessary usb_ep_dequeue() Function usb_ep_disable() causes completion of all requests queued for given endpoint, so there is no need to dequeue them after endpoint disabling. Signed-off-by: Robert Baldyga Signed-off-by: Felipe Balbi --- drivers/usb/gadget/function/f_hid.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/usb/gadget/function/f_hid.c b/drivers/usb/gadget/function/f_hid.c index a2612fb79eff..13dfc9915b1d 100644 --- a/drivers/usb/gadget/function/f_hid.c +++ b/drivers/usb/gadget/function/f_hid.c @@ -908,7 +908,6 @@ static void hidg_unbind(struct usb_configuration *c, struct usb_function *f) /* disable/free request and end point */ usb_ep_disable(hidg->in_ep); - usb_ep_dequeue(hidg->in_ep, hidg->req); kfree(hidg->req->buf); usb_ep_free_request(hidg->in_ep, hidg->req); -- cgit v1.2.3 From 168bdb88c3ab1f66c061a6220c18939ef20ba42e Mon Sep 17 00:00:00 2001 From: Fabio Estevam Date: Tue, 3 Feb 2015 19:18:17 -0200 Subject: usb: phy: phy-generic: No need to call gpiod_direction_output() twice Commit 9eb0797722895f4309b4 ("usb: phy: generic: fix the gpios to be optional") calls gpiod_direction_output() in the probe function, so there is no need to call it again, as we can simply call gpiod_set_value() directly. Also, in usb_gen_phy_shutdown() we can simply put the GPIO directly in its active level state and this allows us to simplify the nop_reset function to treat only the reset case. Signed-off-by: Fabio Estevam Signed-off-by: Felipe Balbi --- drivers/usb/phy/phy-generic.c | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/drivers/usb/phy/phy-generic.c b/drivers/usb/phy/phy-generic.c index 70be50b734b2..deee68eafb72 100644 --- a/drivers/usb/phy/phy-generic.c +++ b/drivers/usb/phy/phy-generic.c @@ -62,14 +62,14 @@ static int nop_set_suspend(struct usb_phy *x, int suspend) return 0; } -static void nop_reset_set(struct usb_phy_generic *nop, int asserted) +static void nop_reset(struct usb_phy_generic *nop) { if (!nop->gpiod_reset) return; - gpiod_direction_output(nop->gpiod_reset, !asserted); + gpiod_set_value(nop->gpiod_reset, 1); usleep_range(10000, 20000); - gpiod_set_value(nop->gpiod_reset, asserted); + gpiod_set_value(nop->gpiod_reset, 0); } /* interface to regulator framework */ @@ -151,8 +151,7 @@ int usb_gen_phy_init(struct usb_phy *phy) if (!IS_ERR(nop->clk)) clk_prepare_enable(nop->clk); - /* De-assert RESET */ - nop_reset_set(nop, 0); + nop_reset(nop); return 0; } @@ -162,8 +161,7 @@ void usb_gen_phy_shutdown(struct usb_phy *phy) { struct usb_phy_generic *nop = dev_get_drvdata(phy->dev); - /* Assert RESET */ - nop_reset_set(nop, 1); + gpiod_set_value(nop->gpiod_reset, 1); if (!IS_ERR(nop->clk)) clk_disable_unprepare(nop->clk); -- cgit v1.2.3 From 72a472d2f912292457d6e3579df342c1138f26d5 Mon Sep 17 00:00:00 2001 From: Takeyoshi Kikuchi Date: Mon, 2 Mar 2015 11:03:51 +0900 Subject: usb: musb: cppi41: fix condition to call cppi41_trans_done(). connect AR9271(USB wifi) to AM335x, and send a flood ping from Mac OSX, AR9271 is stopped. on USB bus, the following occurs. - OUT transaction is ACKed (NYET). - IN transaction is ACKed (512bytes). - PING-NAK transaction is continued for about 2 seconds (AR9271 timeout?). In current imprementation, IN-transaction is not completed because it checks the empty of TX-FIFO in cppi41_dma_callback(). As a result, communication to AR9271 stops. This patch modified to check the empty of TX-FIFO only when OUT-transaction. Signed-off-by: Takeyoshi Kikuchi Signed-off-by: Felipe Balbi --- drivers/usb/musb/musb_cppi41.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/drivers/usb/musb/musb_cppi41.c b/drivers/usb/musb/musb_cppi41.c index 9dc45a4a9fa8..8bd8c5e26921 100644 --- a/drivers/usb/musb/musb_cppi41.c +++ b/drivers/usb/musb/musb_cppi41.c @@ -250,8 +250,10 @@ static void cppi41_dma_callback(void *private_data) transferred < cppi41_channel->packet_sz) cppi41_channel->prog_len = 0; - empty = musb_is_tx_fifo_empty(hw_ep); - if (empty) { + if (cppi41_channel->is_tx) + empty = musb_is_tx_fifo_empty(hw_ep); + + if (!cppi41_channel->is_tx || empty) { cppi41_trans_done(cppi41_channel); goto out; } -- cgit v1.2.3 From 005a64307d5d3ef895e7821df4cad7739bab392e Mon Sep 17 00:00:00 2001 From: Peter Chen Date: Wed, 11 Mar 2015 10:07:47 +0800 Subject: usb: gadget: lpc32xxx_udc: Fix NULL dereference udc is then checked for NULL, if NULL, it is then dereferenced as udc->dev, it is found using Coccinelle. We simplify the code to fix this problem, and we delete some conditions at if {} which will never be met. Reported-by: Tapasweni Pathak Reported-by : Julia Lawall Signed-off-by: Peter Chen Signed-off-by: Felipe Balbi --- drivers/usb/gadget/udc/lpc32xx_udc.c | 15 +++------------ 1 file changed, 3 insertions(+), 12 deletions(-) diff --git a/drivers/usb/gadget/udc/lpc32xx_udc.c b/drivers/usb/gadget/udc/lpc32xx_udc.c index 27fd41333f71..3b6a7852822d 100644 --- a/drivers/usb/gadget/udc/lpc32xx_udc.c +++ b/drivers/usb/gadget/udc/lpc32xx_udc.c @@ -1803,23 +1803,14 @@ static int lpc32xx_ep_queue(struct usb_ep *_ep, req = container_of(_req, struct lpc32xx_request, req); ep = container_of(_ep, struct lpc32xx_ep, ep); - if (!_req || !_req->complete || !_req->buf || + if (!_ep || !_req || !_req->complete || !_req->buf || !list_empty(&req->queue)) return -EINVAL; udc = ep->udc; - if (!_ep) { - dev_dbg(udc->dev, "invalid ep\n"); - return -EINVAL; - } - - - if ((!udc) || (!udc->driver) || - (udc->gadget.speed == USB_SPEED_UNKNOWN)) { - dev_dbg(udc->dev, "invalid device\n"); - return -EINVAL; - } + if (udc->gadget.speed == USB_SPEED_UNKNOWN) + return -EPIPE; if (ep->lep) { struct lpc32xx_usbd_dd_gad *dd; -- cgit v1.2.3 From 9024c495f35be735a917571406fab30a789c27d1 Mon Sep 17 00:00:00 2001 From: John Youn Date: Tue, 3 Mar 2015 17:17:49 -0800 Subject: usb: dwc2: pci: Add device mode to the dwc2-pci driver The pci driver now registers a platform driver, like in dwc3, and lets its probe function do all the initialization. This allows it to account for changes to the platform driver that were not added to the pci driver. Also future changes to the probe function don't have to be duplicated. This also has the effect of adding device and DRD mode to the pci driver. Tested on the Synopsys HAPS PCIe platform. Signed-off-by: John Youn Signed-off-by: Felipe Balbi --- drivers/usb/dwc2/Kconfig | 7 ++- drivers/usb/dwc2/pci.c | 159 +++++++++++++++++++++-------------------------- 2 files changed, 76 insertions(+), 90 deletions(-) diff --git a/drivers/usb/dwc2/Kconfig b/drivers/usb/dwc2/Kconfig index 76b9ba4dc925..3db204f21ff9 100644 --- a/drivers/usb/dwc2/Kconfig +++ b/drivers/usb/dwc2/Kconfig @@ -59,11 +59,12 @@ config USB_DWC2_PLATFORM config USB_DWC2_PCI tristate "DWC2 PCI" - depends on USB_DWC2_HOST && PCI - default USB_DWC2_HOST + depends on PCI + default n + select USB_DWC2_PLATFORM help The Designware USB2.0 PCI interface module for controllers - connected to a PCI bus. This is only used for host mode. + connected to a PCI bus. config USB_DWC2_DEBUG bool "Enable Debugging Messages" diff --git a/drivers/usb/dwc2/pci.c b/drivers/usb/dwc2/pci.c index 6646adb1fb17..ae419615a176 100644 --- a/drivers/usb/dwc2/pci.c +++ b/drivers/usb/dwc2/pci.c @@ -50,112 +50,97 @@ #include #include - -#include "core.h" -#include "hcd.h" +#include +#include #define PCI_PRODUCT_ID_HAPS_HSOTG 0xabc0 -static const char dwc2_driver_name[] = "dwc2"; - -static const struct dwc2_core_params dwc2_module_params = { - .otg_cap = -1, - .otg_ver = -1, - .dma_enable = -1, - .dma_desc_enable = 0, - .speed = -1, - .enable_dynamic_fifo = -1, - .en_multiple_tx_fifo = -1, - .host_rx_fifo_size = 1024, - .host_nperio_tx_fifo_size = 256, - .host_perio_tx_fifo_size = 1024, - .max_transfer_size = 65535, - .max_packet_count = 511, - .host_channels = -1, - .phy_type = -1, - .phy_utmi_width = -1, - .phy_ulpi_ddr = -1, - .phy_ulpi_ext_vbus = -1, - .i2c_enable = -1, - .ulpi_fs_ls = -1, - .host_support_fs_ls_low_power = -1, - .host_ls_low_power_phy_clk = -1, - .ts_dline = -1, - .reload_ctl = -1, - .ahbcfg = -1, - .uframe_sched = -1, +static const char dwc2_driver_name[] = "dwc2-pci"; + +struct dwc2_pci_glue { + struct platform_device *dwc2; + struct platform_device *phy; }; -/** - * dwc2_driver_remove() - Called when the DWC_otg core is unregistered with the - * DWC_otg driver - * - * @dev: Bus device - * - * This routine is called, for example, when the rmmod command is executed. The - * device may or may not be electrically present. If it is present, the driver - * stops device processing. Any resources used on behalf of this device are - * freed. - */ -static void dwc2_driver_remove(struct pci_dev *dev) +static void dwc2_pci_remove(struct pci_dev *pci) { - struct dwc2_hsotg *hsotg = pci_get_drvdata(dev); + struct dwc2_pci_glue *glue = pci_get_drvdata(pci); - dwc2_hcd_remove(hsotg); - pci_disable_device(dev); + platform_device_unregister(glue->dwc2); + usb_phy_generic_unregister(glue->phy); + kfree(glue); + pci_set_drvdata(pci, NULL); } -/** - * dwc2_driver_probe() - Called when the DWC_otg core is bound to the DWC_otg - * driver - * - * @dev: Bus device - * - * This routine creates the driver components required to control the device - * (core, HCD, and PCD) and initializes the device. The driver components are - * stored in a dwc2_hsotg structure. A reference to the dwc2_hsotg is saved - * in the device private data. This allows the driver to access the dwc2_hsotg - * structure on subsequent calls to driver methods for this device. - */ -static int dwc2_driver_probe(struct pci_dev *dev, - const struct pci_device_id *id) +static int dwc2_pci_probe(struct pci_dev *pci, + const struct pci_device_id *id) { - struct dwc2_hsotg *hsotg; - int retval; + struct resource res[2]; + struct platform_device *dwc2; + struct platform_device *phy; + int ret; + struct device *dev = &pci->dev; + struct dwc2_pci_glue *glue; + + ret = pcim_enable_device(pci); + if (ret) { + dev_err(dev, "failed to enable pci device\n"); + return -ENODEV; + } + + pci_set_master(pci); - hsotg = devm_kzalloc(&dev->dev, sizeof(*hsotg), GFP_KERNEL); - if (!hsotg) + dwc2 = platform_device_alloc("dwc2", PLATFORM_DEVID_AUTO); + if (!dwc2) { + dev_err(dev, "couldn't allocate dwc2 device\n"); return -ENOMEM; + } - hsotg->dev = &dev->dev; - hsotg->regs = devm_ioremap_resource(&dev->dev, &dev->resource[0]); - if (IS_ERR(hsotg->regs)) - return PTR_ERR(hsotg->regs); + memset(res, 0x00, sizeof(struct resource) * ARRAY_SIZE(res)); - dev_dbg(&dev->dev, "mapped PA %08lx to VA %p\n", - (unsigned long)pci_resource_start(dev, 0), hsotg->regs); + res[0].start = pci_resource_start(pci, 0); + res[0].end = pci_resource_end(pci, 0); + res[0].name = "dwc2"; + res[0].flags = IORESOURCE_MEM; - if (pci_enable_device(dev) < 0) - return -ENODEV; + res[1].start = pci->irq; + res[1].name = "dwc2"; + res[1].flags = IORESOURCE_IRQ; - pci_set_master(dev); + ret = platform_device_add_resources(dwc2, res, ARRAY_SIZE(res)); + if (ret) { + dev_err(dev, "couldn't add resources to dwc2 device\n"); + return ret; + } - retval = devm_request_irq(hsotg->dev, dev->irq, - dwc2_handle_common_intr, IRQF_SHARED, - dev_name(hsotg->dev), hsotg); - if (retval) - return retval; + dwc2->dev.parent = dev; - spin_lock_init(&hsotg->lock); - retval = dwc2_hcd_init(hsotg, dev->irq, &dwc2_module_params); - if (retval) { - pci_disable_device(dev); - return retval; + phy = usb_phy_generic_register(); + if (IS_ERR(phy)) { + dev_err(dev, "error registering generic PHY (%ld)\n", + PTR_ERR(phy)); + return PTR_ERR(phy); } - pci_set_drvdata(dev, hsotg); + ret = platform_device_add(dwc2); + if (ret) { + dev_err(dev, "failed to register dwc2 device\n"); + goto err; + } + + glue = kzalloc(sizeof(*glue), GFP_KERNEL); + if (!glue) + return -ENOMEM; + + glue->phy = phy; + glue->dwc2 = dwc2; + pci_set_drvdata(pci, glue); - return retval; + return 0; +err: + usb_phy_generic_unregister(phy); + platform_device_put(dwc2); + return ret; } static const struct pci_device_id dwc2_pci_ids[] = { @@ -173,8 +158,8 @@ MODULE_DEVICE_TABLE(pci, dwc2_pci_ids); static struct pci_driver dwc2_pci_driver = { .name = dwc2_driver_name, .id_table = dwc2_pci_ids, - .probe = dwc2_driver_probe, - .remove = dwc2_driver_remove, + .probe = dwc2_pci_probe, + .remove = dwc2_pci_remove, }; module_pci_driver(dwc2_pci_driver); -- cgit v1.2.3 From e39af88f18dfe9a7938765c97ce9ed448915e6d5 Mon Sep 17 00:00:00 2001 From: Marek Szyprowski Date: Tue, 10 Mar 2015 13:41:10 +0100 Subject: usb: dwc2: rework initialization of host and gadget in dual-role mode If device is configured to work only in HOST or DEVICE mode, there is no point in initializing both subdrivers. This patch also fixes resource leakage if host subdriver fails to initialize. Acked-by: John Youn Signed-off-by: Marek Szyprowski Signed-off-by: Felipe Balbi --- drivers/usb/dwc2/core.h | 2 ++ drivers/usb/dwc2/platform.c | 29 +++++++++++++++++++++-------- 2 files changed, 23 insertions(+), 8 deletions(-) diff --git a/drivers/usb/dwc2/core.h b/drivers/usb/dwc2/core.h index f74304b12652..836c012c7707 100644 --- a/drivers/usb/dwc2/core.h +++ b/drivers/usb/dwc2/core.h @@ -593,6 +593,8 @@ struct dwc2_hsotg { struct dwc2_core_params *core_params; enum usb_otg_state op_state; enum usb_dr_mode dr_mode; + unsigned int hcd_enabled:1; + unsigned int gadget_enabled:1; struct phy *phy; struct usb_phy *uphy; diff --git a/drivers/usb/dwc2/platform.c b/drivers/usb/dwc2/platform.c index ae095f009b4f..185663e0b5f4 100644 --- a/drivers/usb/dwc2/platform.c +++ b/drivers/usb/dwc2/platform.c @@ -121,8 +121,10 @@ static int dwc2_driver_remove(struct platform_device *dev) { struct dwc2_hsotg *hsotg = platform_get_drvdata(dev); - dwc2_hcd_remove(hsotg); - s3c_hsotg_remove(hsotg); + if (hsotg->hcd_enabled) + dwc2_hcd_remove(hsotg); + if (hsotg->gadget_enabled) + s3c_hsotg_remove(hsotg); return 0; } @@ -234,12 +236,23 @@ static int dwc2_driver_probe(struct platform_device *dev) spin_lock_init(&hsotg->lock); mutex_init(&hsotg->init_mutex); - retval = dwc2_gadget_init(hsotg, irq); - if (retval) - return retval; - retval = dwc2_hcd_init(hsotg, irq, params); - if (retval) - return retval; + + if (hsotg->dr_mode != USB_DR_MODE_HOST) { + retval = dwc2_gadget_init(hsotg, irq); + if (retval) + return retval; + hsotg->gadget_enabled = 1; + } + + if (hsotg->dr_mode != USB_DR_MODE_PERIPHERAL) { + retval = dwc2_hcd_init(hsotg, irq, params); + if (retval) { + if (hsotg->gadget_enabled) + s3c_hsotg_remove(hsotg); + return retval; + } + hsotg->hcd_enabled = 1; + } platform_set_drvdata(dev, hsotg); -- cgit v1.2.3 From 16d9efa4b3db0ab69605ff4d03d707d0210f4bf7 Mon Sep 17 00:00:00 2001 From: Scott Wood Date: Thu, 12 Mar 2015 16:46:01 -0500 Subject: usb: gadget: serial: %pf is only for function pointers Use %ps for actual addresses, otherwise you'll get bad output on arches like ppc64 where %pf expects a function descriptor (which is not what __builtin_return_address returns). Reviewed-by: Fabio Estevam Signed-off-by: Scott Wood Cc: linux-usb@vger.kernel.org CC: Sergei Shtylyov Signed-off-by: Felipe Balbi --- drivers/usb/gadget/function/u_serial.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/usb/gadget/function/u_serial.c b/drivers/usb/gadget/function/u_serial.c index 491082aaf103..89179ab20c10 100644 --- a/drivers/usb/gadget/function/u_serial.c +++ b/drivers/usb/gadget/function/u_serial.c @@ -912,7 +912,7 @@ static int gs_put_char(struct tty_struct *tty, unsigned char ch) unsigned long flags; int status; - pr_vdebug("gs_put_char: (%d,%p) char=0x%x, called from %pf\n", + pr_vdebug("gs_put_char: (%d,%p) char=0x%x, called from %ps\n", port->port_num, tty, ch, __builtin_return_address(0)); spin_lock_irqsave(&port->port_lock, flags); -- cgit v1.2.3 From d4ae02cc904d916ea6b292e61b79fd3497054067 Mon Sep 17 00:00:00 2001 From: John Youn Date: Thu, 12 Mar 2015 10:04:42 -0700 Subject: usb: dwc2: pci: Select the generic PHY for dwc2-pci driver The dwc2-pci driver requires the generic PHY. This fixes undefined reference issues when it is not selected. Reported-by: kbuild test robot Signed-off-by: John Youn Signed-off-by: Felipe Balbi --- drivers/usb/dwc2/Kconfig | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/usb/dwc2/Kconfig b/drivers/usb/dwc2/Kconfig index 3db204f21ff9..1bcb36ae6505 100644 --- a/drivers/usb/dwc2/Kconfig +++ b/drivers/usb/dwc2/Kconfig @@ -62,6 +62,7 @@ config USB_DWC2_PCI depends on PCI default n select USB_DWC2_PLATFORM + select NOP_USB_XCEIV help The Designware USB2.0 PCI interface module for controllers connected to a PCI bus. -- cgit v1.2.3 From fdb51e3d97552413c851bf8426ef69508389df88 Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Fri, 13 Mar 2015 12:11:06 +0300 Subject: usb: gadget: printer: delete some dead code "num" is a u16 so it can't go higher than 65535. kstrtou16() has a range check built in so this is already handled. Signed-off-by: Dan Carpenter Signed-off-by: Felipe Balbi --- drivers/usb/gadget/function/f_printer.c | 5 ----- 1 file changed, 5 deletions(-) diff --git a/drivers/usb/gadget/function/f_printer.c b/drivers/usb/gadget/function/f_printer.c index 757fcf070013..caa56de3a3e8 100644 --- a/drivers/usb/gadget/function/f_printer.c +++ b/drivers/usb/gadget/function/f_printer.c @@ -1223,11 +1223,6 @@ static ssize_t f_printer_opts_q_len_store(struct f_printer_opts *opts, if (ret) goto end; - if (num > 65535) { - ret = -EINVAL; - goto end; - } - opts->q_len = (unsigned)num; ret = len; end: -- cgit v1.2.3 From 2bb2077ee607703771c35ed74837180760f9ce07 Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Fri, 13 Mar 2015 12:11:42 +0300 Subject: usb: gadget: printer: use after free in gprinter_alloc_inst() There was a missing goto so we free "opts" and then dereference it. Fixes: ee1cd515e889 ('usb: gadget: printer: add configfs support') Signed-off-by: Dan Carpenter Signed-off-by: Felipe Balbi --- drivers/usb/gadget/function/f_printer.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/usb/gadget/function/f_printer.c b/drivers/usb/gadget/function/f_printer.c index caa56de3a3e8..48882ecf1610 100644 --- a/drivers/usb/gadget/function/f_printer.c +++ b/drivers/usb/gadget/function/f_printer.c @@ -1307,6 +1307,7 @@ static struct usb_function_instance *gprinter_alloc_inst(void) kfree(opts); if (idr_is_empty(&printer_ida.idr)) gprinter_cleanup(); + goto unlock; } config_group_init_type_name(&opts->func_inst.group, "", &printer_func_type); -- cgit v1.2.3 From fbdecad99c3f37346ed868fec0c3a9c2809f78e9 Mon Sep 17 00:00:00 2001 From: Andrzej Pietrasiewicz Date: Fri, 13 Mar 2015 11:08:08 +0100 Subject: usb: gadget: f_printer: use non-zero flag for bitwise and USB_DIR_OUT happens to be zero, so the result of bitwise and is always 0. Consequently, break will never happen in the SOFT_RESET case. This patch uses a compatible condition with a non-zero USB_DIR_IN, which might or might not evaluate to zero. Reported-by: Dan Carpenter Signed-off-by: Andrzej Pietrasiewicz Signed-off-by: Felipe Balbi --- drivers/usb/gadget/function/f_printer.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/usb/gadget/function/f_printer.c b/drivers/usb/gadget/function/f_printer.c index 48882ecf1610..44173df27273 100644 --- a/drivers/usb/gadget/function/f_printer.c +++ b/drivers/usb/gadget/function/f_printer.c @@ -918,7 +918,7 @@ static bool gprinter_req_match(struct usb_function *f, return false; case SOFT_RESET: if (!w_value && !w_length && - (USB_DIR_OUT & ctrl->bRequestType)) + !(USB_DIR_IN & ctrl->bRequestType)) break; /* fall through */ default: -- cgit v1.2.3 From 7a96b78464bd8ba72c1c3095c543c1402db59e35 Mon Sep 17 00:00:00 2001 From: Yoshihiro Shimoda Date: Thu, 12 Mar 2015 15:35:18 +0900 Subject: usb: renesas_usbhs: add the channel number in dma-names To connect the channel of USB-DMAC to USBHS DnFIFO number, this patch adds this channel/FIFO number in dma-names. Otherwise, this driver needs to add analysis code for device tree. Signed-off-by: Yoshihiro Shimoda Signed-off-by: Felipe Balbi --- .../devicetree/bindings/usb/renesas_usbhs.txt | 5 ++++- drivers/usb/renesas_usbhs/fifo.c | 20 +++++++++++++------- 2 files changed, 17 insertions(+), 8 deletions(-) diff --git a/Documentation/devicetree/bindings/usb/renesas_usbhs.txt b/Documentation/devicetree/bindings/usb/renesas_usbhs.txt index 61b045b6d50e..dc2a18f0b3a1 100644 --- a/Documentation/devicetree/bindings/usb/renesas_usbhs.txt +++ b/Documentation/devicetree/bindings/usb/renesas_usbhs.txt @@ -15,7 +15,10 @@ Optional properties: - phys: phandle + phy specifier pair - phy-names: must be "usb" - dmas: Must contain a list of references to DMA specifiers. - - dma-names : Must contain a list of DMA names, "tx" or "rx". + - dma-names : Must contain a list of DMA names: + - tx0 ... tx + - rx0 ... rx + - This means DnFIFO in USBHS module. Example: usbhs: usb@e6590000 { diff --git a/drivers/usb/renesas_usbhs/fifo.c b/drivers/usb/renesas_usbhs/fifo.c index d891bff39d66..28d10eb432d5 100644 --- a/drivers/usb/renesas_usbhs/fifo.c +++ b/drivers/usb/renesas_usbhs/fifo.c @@ -1069,23 +1069,29 @@ static void usbhsf_dma_init_pdev(struct usbhs_fifo *fifo) &fifo->rx_slave); } -static void usbhsf_dma_init_dt(struct device *dev, struct usbhs_fifo *fifo) +static void usbhsf_dma_init_dt(struct device *dev, struct usbhs_fifo *fifo, + int channel) { - fifo->tx_chan = dma_request_slave_channel_reason(dev, "tx"); + char name[16]; + + snprintf(name, sizeof(name), "tx%d", channel); + fifo->tx_chan = dma_request_slave_channel_reason(dev, name); if (IS_ERR(fifo->tx_chan)) fifo->tx_chan = NULL; - fifo->rx_chan = dma_request_slave_channel_reason(dev, "rx"); + + snprintf(name, sizeof(name), "rx%d", channel); + fifo->rx_chan = dma_request_slave_channel_reason(dev, name); if (IS_ERR(fifo->rx_chan)) fifo->rx_chan = NULL; } -static void usbhsf_dma_init(struct usbhs_priv *priv, - struct usbhs_fifo *fifo) +static void usbhsf_dma_init(struct usbhs_priv *priv, struct usbhs_fifo *fifo, + int channel) { struct device *dev = usbhs_priv_to_dev(priv); if (dev->of_node) - usbhsf_dma_init_dt(dev, fifo); + usbhsf_dma_init_dt(dev, fifo, channel); else usbhsf_dma_init_pdev(fifo); @@ -1231,7 +1237,7 @@ do { \ usbhs_get_dparam(priv, d##channel##_tx_id); \ fifo->rx_slave.shdma_slave.slave_id = \ usbhs_get_dparam(priv, d##channel##_rx_id); \ - usbhsf_dma_init(priv, fifo); \ + usbhsf_dma_init(priv, fifo, channel); \ } while (0) #define USBHS_DFIFO_INIT(priv, fifo, channel) \ -- cgit v1.2.3 From 9b53d9af7aac09cf249d72bfbf15f08e47c4f7fe Mon Sep 17 00:00:00 2001 From: Yoshihiro Shimoda Date: Thu, 12 Mar 2015 15:35:19 +0900 Subject: usb: renesas_usbhs: fix the sequence in xfer_work() This patch fixes the setup sequence in xfer_work(). Otherwise, sometimes a usb transaction will get stuck. Signed-off-by: Yoshihiro Shimoda Signed-off-by: Felipe Balbi --- drivers/usb/renesas_usbhs/fifo.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/usb/renesas_usbhs/fifo.c b/drivers/usb/renesas_usbhs/fifo.c index 28d10eb432d5..b737661b10a2 100644 --- a/drivers/usb/renesas_usbhs/fifo.c +++ b/drivers/usb/renesas_usbhs/fifo.c @@ -822,10 +822,10 @@ static void xfer_work(struct work_struct *work) fifo->name, usbhs_pipe_number(pipe), pkt->length, pkt->zero); usbhs_pipe_running(pipe, 1); - usbhs_pipe_set_trans_count_if_bulk(pipe, pkt->trans); - usbhs_pipe_enable(pipe); usbhsf_dma_start(pipe, fifo); + usbhs_pipe_set_trans_count_if_bulk(pipe, pkt->trans); dma_async_issue_pending(chan); + usbhs_pipe_enable(pipe); } /* -- cgit v1.2.3 From ab330cf3888d8e0779fa05a243d53ba9f53a7ba9 Mon Sep 17 00:00:00 2001 From: Yoshihiro Shimoda Date: Thu, 12 Mar 2015 15:35:20 +0900 Subject: usb: renesas_usbhs: add support for USB-DMAC Some Renesas SoCs have the USB-DMAC. It is able to terminate transfers when a short packet is received, even if less bytes than the transfer counter size have been received. Also, it is able to send a short packet even if the packet size is not multiples of 8bytes. Since the previous code has used the interruption of USBHS controller when receiving packets even if this driver has used a dmac, a lot of interruptions has happened. This patch will reduce such interruptions. This patch allows to use the USB-DMAC on R-Car H2 and M2. Signed-off-by: Yoshihiro Shimoda Signed-off-by: Felipe Balbi --- drivers/usb/renesas_usbhs/common.c | 19 +++++ drivers/usb/renesas_usbhs/common.h | 7 ++ drivers/usb/renesas_usbhs/fifo.c | 165 +++++++++++++++++++++++++++++++++++-- drivers/usb/renesas_usbhs/fifo.h | 1 + drivers/usb/renesas_usbhs/pipe.c | 39 +++++++++ drivers/usb/renesas_usbhs/pipe.h | 1 + include/linux/usb/renesas_usbhs.h | 2 + 7 files changed, 228 insertions(+), 6 deletions(-) diff --git a/drivers/usb/renesas_usbhs/common.c b/drivers/usb/renesas_usbhs/common.c index 4cf77d3c3bd2..0f7e850fd4aa 100644 --- a/drivers/usb/renesas_usbhs/common.c +++ b/drivers/usb/renesas_usbhs/common.c @@ -275,6 +275,16 @@ int usbhs_set_device_config(struct usbhs_priv *priv, int devnum, return 0; } +/* + * interrupt functions + */ +void usbhs_xxxsts_clear(struct usbhs_priv *priv, u16 sts_reg, u16 bit) +{ + u16 pipe_mask = (u16)GENMASK(usbhs_get_dparam(priv, pipe_size), 0); + + usbhs_write(priv, sts_reg, ~(1 << bit) & pipe_mask); +} + /* * local functions */ @@ -487,6 +497,15 @@ static struct renesas_usbhs_platform_info *usbhs_parse_dt(struct device *dev) if (gpio > 0) dparam->enable_gpio = gpio; + switch (dparam->type) { + case USBHS_TYPE_R8A7790: + case USBHS_TYPE_R8A7791: + dparam->has_usb_dmac = 1; + break; + default: + break; + } + return info; } diff --git a/drivers/usb/renesas_usbhs/common.h b/drivers/usb/renesas_usbhs/common.h index fc96e924edc4..8c5fc12ad778 100644 --- a/drivers/usb/renesas_usbhs/common.h +++ b/drivers/usb/renesas_usbhs/common.h @@ -193,6 +193,7 @@ struct usbhs_priv; #define TYPE_BULK (1 << 14) #define TYPE_INT (2 << 14) #define TYPE_ISO (3 << 14) +#define BFRE (1 << 10) /* BRDY Interrupt Operation Spec. */ #define DBLB (1 << 9) /* Double Buffer Mode */ #define SHTNAK (1 << 7) /* Pipe Disable in Transfer End */ #define DIR_OUT (1 << 4) /* Transfer Direction */ @@ -216,6 +217,7 @@ struct usbhs_priv; #define ACLRM (1 << 9) /* Buffer Auto-Clear Mode */ #define SQCLR (1 << 8) /* Toggle Bit Clear */ #define SQSET (1 << 7) /* Toggle Bit Set */ +#define SQMON (1 << 6) /* Toggle Bit Check */ #define PBUSY (1 << 5) /* Pipe Busy */ #define PID_MASK (0x3) /* Response PID */ #define PID_NAK 0 @@ -323,6 +325,11 @@ int usbhs_frame_get_num(struct usbhs_priv *priv); int usbhs_set_device_config(struct usbhs_priv *priv, int devnum, u16 upphub, u16 hubport, u16 speed); +/* + * interrupt functions + */ +void usbhs_xxxsts_clear(struct usbhs_priv *priv, u16 sts_reg, u16 bit); + /* * data */ diff --git a/drivers/usb/renesas_usbhs/fifo.c b/drivers/usb/renesas_usbhs/fifo.c index b737661b10a2..8597cf9cfceb 100644 --- a/drivers/usb/renesas_usbhs/fifo.c +++ b/drivers/usb/renesas_usbhs/fifo.c @@ -813,7 +813,8 @@ static void xfer_work(struct work_struct *work) desc->callback = usbhsf_dma_complete; desc->callback_param = pipe; - if (dmaengine_submit(desc) < 0) { + pkt->cookie = dmaengine_submit(desc); + if (pkt->cookie < 0) { dev_err(dev, "Failed to submit dma descriptor\n"); return; } @@ -838,6 +839,7 @@ static int usbhsf_dma_prepare_push(struct usbhs_pkt *pkt, int *is_done) struct usbhs_fifo *fifo; int len = pkt->length - pkt->actual; int ret; + uintptr_t align_mask; if (usbhs_pipe_is_busy(pipe)) return 0; @@ -847,10 +849,14 @@ static int usbhsf_dma_prepare_push(struct usbhs_pkt *pkt, int *is_done) usbhs_pipe_is_dcp(pipe)) goto usbhsf_pio_prepare_push; - if (len & 0x7) /* 8byte alignment */ + /* check data length if this driver don't use USB-DMAC */ + if (!usbhs_get_dparam(priv, has_usb_dmac) && len & 0x7) goto usbhsf_pio_prepare_push; - if ((uintptr_t)(pkt->buf + pkt->actual) & 0x7) /* 8byte alignment */ + /* check buffer alignment */ + align_mask = usbhs_get_dparam(priv, has_usb_dmac) ? + USBHS_USB_DMAC_XFER_SIZE - 1 : 0x7; + if ((uintptr_t)(pkt->buf + pkt->actual) & align_mask) goto usbhsf_pio_prepare_push; /* return at this time if the pipe is running */ @@ -924,7 +930,85 @@ struct usbhs_pkt_handle usbhs_fifo_dma_push_handler = { /* * DMA pop handler */ -static int usbhsf_dma_try_pop(struct usbhs_pkt *pkt, int *is_done) + +static int usbhsf_dma_prepare_pop_with_rx_irq(struct usbhs_pkt *pkt, + int *is_done) +{ + return usbhsf_prepare_pop(pkt, is_done); +} + +static int usbhsf_dma_prepare_pop_with_usb_dmac(struct usbhs_pkt *pkt, + int *is_done) +{ + struct usbhs_pipe *pipe = pkt->pipe; + struct usbhs_priv *priv = usbhs_pipe_to_priv(pipe); + struct usbhs_fifo *fifo; + int ret; + + if (usbhs_pipe_is_busy(pipe)) + return 0; + + /* use PIO if packet is less than pio_dma_border or pipe is DCP */ + if ((pkt->length < usbhs_get_dparam(priv, pio_dma_border)) || + usbhs_pipe_is_dcp(pipe)) + goto usbhsf_pio_prepare_pop; + + fifo = usbhsf_get_dma_fifo(priv, pkt); + if (!fifo) + goto usbhsf_pio_prepare_pop; + + if ((uintptr_t)pkt->buf & (USBHS_USB_DMAC_XFER_SIZE - 1)) + goto usbhsf_pio_prepare_pop; + + usbhs_pipe_config_change_bfre(pipe, 1); + + ret = usbhsf_fifo_select(pipe, fifo, 0); + if (ret < 0) + goto usbhsf_pio_prepare_pop; + + if (usbhsf_dma_map(pkt) < 0) + goto usbhsf_pio_prepare_pop_unselect; + + /* DMA */ + + /* + * usbhs_fifo_dma_pop_handler :: prepare + * enabled irq to come here. + * but it is no longer needed for DMA. disable it. + */ + usbhsf_rx_irq_ctrl(pipe, 0); + + pkt->trans = pkt->length; + + INIT_WORK(&pkt->work, xfer_work); + schedule_work(&pkt->work); + + return 0; + +usbhsf_pio_prepare_pop_unselect: + usbhsf_fifo_unselect(pipe, fifo); +usbhsf_pio_prepare_pop: + + /* + * change handler to PIO + */ + pkt->handler = &usbhs_fifo_pio_pop_handler; + usbhs_pipe_config_change_bfre(pipe, 0); + + return pkt->handler->prepare(pkt, is_done); +} + +static int usbhsf_dma_prepare_pop(struct usbhs_pkt *pkt, int *is_done) +{ + struct usbhs_priv *priv = usbhs_pipe_to_priv(pkt->pipe); + + if (usbhs_get_dparam(priv, has_usb_dmac)) + return usbhsf_dma_prepare_pop_with_usb_dmac(pkt, is_done); + else + return usbhsf_dma_prepare_pop_with_rx_irq(pkt, is_done); +} + +static int usbhsf_dma_try_pop_with_rx_irq(struct usbhs_pkt *pkt, int *is_done) { struct usbhs_pipe *pipe = pkt->pipe; struct usbhs_priv *priv = usbhs_pipe_to_priv(pipe); @@ -993,7 +1077,16 @@ usbhsf_pio_prepare_pop: return pkt->handler->try_run(pkt, is_done); } -static int usbhsf_dma_pop_done(struct usbhs_pkt *pkt, int *is_done) +static int usbhsf_dma_try_pop(struct usbhs_pkt *pkt, int *is_done) +{ + struct usbhs_priv *priv = usbhs_pipe_to_priv(pkt->pipe); + + BUG_ON(usbhs_get_dparam(priv, has_usb_dmac)); + + return usbhsf_dma_try_pop_with_rx_irq(pkt, is_done); +} + +static int usbhsf_dma_pop_done_with_rx_irq(struct usbhs_pkt *pkt, int *is_done) { struct usbhs_pipe *pipe = pkt->pipe; int maxp = usbhs_pipe_get_maxpacket(pipe); @@ -1017,8 +1110,68 @@ static int usbhsf_dma_pop_done(struct usbhs_pkt *pkt, int *is_done) return 0; } +static size_t usbhs_dma_calc_received_size(struct usbhs_pkt *pkt, + struct dma_chan *chan, int dtln) +{ + struct usbhs_pipe *pipe = pkt->pipe; + struct dma_tx_state state; + size_t received_size; + int maxp = usbhs_pipe_get_maxpacket(pipe); + + dmaengine_tx_status(chan, pkt->cookie, &state); + received_size = pkt->length - state.residue; + + if (dtln) { + received_size -= USBHS_USB_DMAC_XFER_SIZE; + received_size &= ~(maxp - 1); + received_size += dtln; + } + + return received_size; +} + +static int usbhsf_dma_pop_done_with_usb_dmac(struct usbhs_pkt *pkt, + int *is_done) +{ + struct usbhs_pipe *pipe = pkt->pipe; + struct usbhs_priv *priv = usbhs_pipe_to_priv(pipe); + struct usbhs_fifo *fifo = usbhs_pipe_to_fifo(pipe); + struct dma_chan *chan = usbhsf_dma_chan_get(fifo, pkt); + int rcv_len; + + /* + * Since the driver disables rx_irq in DMA mode, the interrupt handler + * cannot the BRDYSTS. So, the function clears it here because the + * driver may use PIO mode next time. + */ + usbhs_xxxsts_clear(priv, BRDYSTS, usbhs_pipe_number(pipe)); + + rcv_len = usbhsf_fifo_rcv_len(priv, fifo); + usbhsf_fifo_clear(pipe, fifo); + pkt->actual = usbhs_dma_calc_received_size(pkt, chan, rcv_len); + + usbhsf_dma_stop(pipe, fifo); + usbhsf_dma_unmap(pkt); + usbhsf_fifo_unselect(pipe, pipe->fifo); + + /* The driver can assume the rx transaction is always "done" */ + *is_done = 1; + + return 0; +} + +static int usbhsf_dma_pop_done(struct usbhs_pkt *pkt, int *is_done) +{ + struct usbhs_priv *priv = usbhs_pipe_to_priv(pkt->pipe); + + if (usbhs_get_dparam(priv, has_usb_dmac)) + return usbhsf_dma_pop_done_with_usb_dmac(pkt, is_done); + else + return usbhsf_dma_pop_done_with_rx_irq(pkt, is_done); +} + struct usbhs_pkt_handle usbhs_fifo_dma_pop_handler = { - .prepare = usbhsf_prepare_pop, + .prepare = usbhsf_dma_prepare_pop, .try_run = usbhsf_dma_try_pop, .dma_done = usbhsf_dma_pop_done }; diff --git a/drivers/usb/renesas_usbhs/fifo.h b/drivers/usb/renesas_usbhs/fifo.h index f07037c1185f..04d3f8abad9e 100644 --- a/drivers/usb/renesas_usbhs/fifo.h +++ b/drivers/usb/renesas_usbhs/fifo.h @@ -58,6 +58,7 @@ struct usbhs_pkt { struct usbhs_pkt *pkt); struct work_struct work; dma_addr_t dma; + dma_cookie_t cookie; void *buf; int length; int trans; diff --git a/drivers/usb/renesas_usbhs/pipe.c b/drivers/usb/renesas_usbhs/pipe.c index 007f45abe96c..4f9c3356127a 100644 --- a/drivers/usb/renesas_usbhs/pipe.c +++ b/drivers/usb/renesas_usbhs/pipe.c @@ -84,6 +84,17 @@ static void __usbhsp_pipe_xxx_set(struct usbhs_pipe *pipe, usbhs_bset(priv, pipe_reg, mask, val); } +static u16 __usbhsp_pipe_xxx_get(struct usbhs_pipe *pipe, + u16 dcp_reg, u16 pipe_reg) +{ + struct usbhs_priv *priv = usbhs_pipe_to_priv(pipe); + + if (usbhs_pipe_is_dcp(pipe)) + return usbhs_read(priv, dcp_reg); + else + return usbhs_read(priv, pipe_reg); +} + /* * DCPCFG/PIPECFG functions */ @@ -92,6 +103,11 @@ static void usbhsp_pipe_cfg_set(struct usbhs_pipe *pipe, u16 mask, u16 val) __usbhsp_pipe_xxx_set(pipe, DCPCFG, PIPECFG, mask, val); } +static u16 usbhsp_pipe_cfg_get(struct usbhs_pipe *pipe) +{ + return __usbhsp_pipe_xxx_get(pipe, DCPCFG, PIPECFG); +} + /* * PIPEnTRN/PIPEnTRE functions */ @@ -616,6 +632,11 @@ void usbhs_pipe_data_sequence(struct usbhs_pipe *pipe, int sequence) usbhsp_pipectrl_set(pipe, mask, val); } +static int usbhs_pipe_get_data_sequence(struct usbhs_pipe *pipe) +{ + return !!(usbhsp_pipectrl_get(pipe) & SQMON); +} + void usbhs_pipe_clear(struct usbhs_pipe *pipe) { if (usbhs_pipe_is_dcp(pipe)) { @@ -626,6 +647,24 @@ void usbhs_pipe_clear(struct usbhs_pipe *pipe) } } +void usbhs_pipe_config_change_bfre(struct usbhs_pipe *pipe, int enable) +{ + int sequence; + + if (usbhs_pipe_is_dcp(pipe)) + return; + + usbhsp_pipe_select(pipe); + /* check if the driver needs to change the BFRE value */ + if (!(enable ^ !!(usbhsp_pipe_cfg_get(pipe) & BFRE))) + return; + + sequence = usbhs_pipe_get_data_sequence(pipe); + usbhsp_pipe_cfg_set(pipe, BFRE, enable ? BFRE : 0); + usbhs_pipe_clear(pipe); + usbhs_pipe_data_sequence(pipe, sequence); +} + static struct usbhs_pipe *usbhsp_get_pipe(struct usbhs_priv *priv, u32 type) { struct usbhs_pipe *pos, *pipe; diff --git a/drivers/usb/renesas_usbhs/pipe.h b/drivers/usb/renesas_usbhs/pipe.h index d24a05972370..b0bc7b603016 100644 --- a/drivers/usb/renesas_usbhs/pipe.h +++ b/drivers/usb/renesas_usbhs/pipe.h @@ -97,6 +97,7 @@ void usbhs_pipe_set_trans_count_if_bulk(struct usbhs_pipe *pipe, int len); void usbhs_pipe_select_fifo(struct usbhs_pipe *pipe, struct usbhs_fifo *fifo); void usbhs_pipe_config_update(struct usbhs_pipe *pipe, u16 devsel, u16 epnum, u16 maxp); +void usbhs_pipe_config_change_bfre(struct usbhs_pipe *pipe, int enable); #define usbhs_pipe_sequence_data0(pipe) usbhs_pipe_data_sequence(pipe, 0) #define usbhs_pipe_sequence_data1(pipe) usbhs_pipe_data_sequence(pipe, 1) diff --git a/include/linux/usb/renesas_usbhs.h b/include/linux/usb/renesas_usbhs.h index 9fd9e481ea98..f06529c14141 100644 --- a/include/linux/usb/renesas_usbhs.h +++ b/include/linux/usb/renesas_usbhs.h @@ -165,6 +165,8 @@ struct renesas_usbhs_driver_param { */ u32 has_otg:1; /* for controlling PWEN/EXTLP */ u32 has_sudmac:1; /* for SUDMAC */ + u32 has_usb_dmac:1; /* for USB-DMAC */ +#define USBHS_USB_DMAC_XFER_SIZE 32 /* hardcode the xfer size */ }; #define USBHS_TYPE_R8A7790 1 -- cgit v1.2.3 From 46f5cace1cd0559c335dfd4a5b8f57456d1bd6a1 Mon Sep 17 00:00:00 2001 From: Stephen Boyd Date: Fri, 13 Mar 2015 11:09:42 -0700 Subject: usb: phy: msm: Remove dead code This code is no longer used now that mach-msm has been removed. Delete it. Cc: Felipe Balbi Cc: Greg Kroah-Hartman Cc: linux-usb@vger.kernel.org Cc: David Brown Cc: Bryan Huntsman Cc: Daniel Walker Signed-off-by: Stephen Boyd Signed-off-by: Felipe Balbi --- drivers/usb/phy/Kconfig | 2 +- drivers/usb/phy/phy-msm-usb.c | 18 ++---------------- include/linux/usb/msm_hsusb.h | 4 ---- 3 files changed, 3 insertions(+), 21 deletions(-) diff --git a/drivers/usb/phy/Kconfig b/drivers/usb/phy/Kconfig index 52d3d58252e1..2fb3828b5089 100644 --- a/drivers/usb/phy/Kconfig +++ b/drivers/usb/phy/Kconfig @@ -139,7 +139,7 @@ config USB_ISP1301 config USB_MSM_OTG tristate "Qualcomm on-chip USB OTG controller support" - depends on (USB || USB_GADGET) && (ARCH_MSM || ARCH_QCOM || COMPILE_TEST) + depends on (USB || USB_GADGET) && (ARCH_QCOM || COMPILE_TEST) depends on RESET_CONTROLLER select USB_PHY help diff --git a/drivers/usb/phy/phy-msm-usb.c b/drivers/usb/phy/phy-msm-usb.c index 000fd892455f..b50c45c62da7 100644 --- a/drivers/usb/phy/phy-msm-usb.c +++ b/drivers/usb/phy/phy-msm-usb.c @@ -263,9 +263,7 @@ static int msm_otg_link_clk_reset(struct msm_otg *motg, bool assert) { int ret; - if (motg->pdata->link_clk_reset) - ret = motg->pdata->link_clk_reset(motg->clk, assert); - else if (assert) + if (assert) ret = reset_control_assert(motg->link_rst); else ret = reset_control_deassert(motg->link_rst); @@ -281,9 +279,7 @@ static int msm_otg_phy_clk_reset(struct msm_otg *motg) { int ret = 0; - if (motg->pdata->phy_clk_reset) - ret = motg->pdata->phy_clk_reset(motg->phy_reset_clk); - else if (motg->phy_rst) + if (motg->phy_rst) ret = reset_control_reset(motg->phy_rst); if (ret) @@ -1551,16 +1547,6 @@ static int msm_otg_probe(struct platform_device *pdev) phy = &motg->phy; phy->dev = &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"); - return PTR_ERR(motg->phy_reset_clk); - } - } - motg->clk = devm_clk_get(&pdev->dev, np ? "core" : "usb_hs_clk"); if (IS_ERR(motg->clk)) { dev_err(&pdev->dev, "failed to get usb_hs_clk\n"); diff --git a/include/linux/usb/msm_hsusb.h b/include/linux/usb/msm_hsusb.h index b0a39243295a..7dbecf9a4656 100644 --- a/include/linux/usb/msm_hsusb.h +++ b/include/linux/usb/msm_hsusb.h @@ -117,8 +117,6 @@ struct msm_otg_platform_data { enum otg_control_type otg_control; enum msm_usb_phy_type phy_type; void (*setup_gpio)(enum usb_otg_state state); - int (*link_clk_reset)(struct clk *link_clk, bool assert); - int (*phy_clk_reset)(struct clk *phy_clk); }; /** @@ -128,7 +126,6 @@ struct msm_otg_platform_data { * @irq: IRQ number assigned for HSUSB controller. * @clk: clock struct of usb_hs_clk. * @pclk: clock struct of usb_hs_pclk. - * @phy_reset_clk: clock struct of usb_phy_clk. * @core_clk: clock struct of usb_hs_core_clk. * @regs: ioremapped register base address. * @inputs: OTG state machine inputs(Id, SessValid etc). @@ -148,7 +145,6 @@ struct msm_otg { int irq; struct clk *clk; struct clk *pclk; - struct clk *phy_reset_clk; struct clk *core_clk; void __iomem *regs; #define ID 0 -- cgit v1.2.3 From 7252f1bfd4bc97ec4b5fa7adaf3a1a45c325635c Mon Sep 17 00:00:00 2001 From: Vincent Palatin Date: Sun, 15 Mar 2015 13:24:32 -0700 Subject: usb: dwc2: avoid leaking DMA channels on disconnection When the HCD is disconnected, the DMA transfers still in-flight were cleaned-up but the count of available DMA channels (e.g. available_host_channels) was not reset. The pool of DMA channels can be depleted when doing unclean disconnection of USB peripherals, and reaches the point where no transfer was possible until the next reboot/reload of the driver. Tested by putting a programmable USB mux on the port and randomly plugging/unpluging a USB HUB with USB mass-storage key, USB-audio and USB-ethernet dongle connected to its downstream ports, and also doing the disconnection early while the devices are still enumerating to get more URBs in-flight. After the patch, the devices are still enumerating after thousands of cycles, while the port was totally dead before. Signed-off-by: Vincent Palatin Acked-by: John Youn Signed-off-by: Felipe Balbi --- drivers/usb/dwc2/hcd.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/drivers/usb/dwc2/hcd.c b/drivers/usb/dwc2/hcd.c index c78c8740db1d..559b55e5debb 100644 --- a/drivers/usb/dwc2/hcd.c +++ b/drivers/usb/dwc2/hcd.c @@ -257,6 +257,14 @@ static void dwc2_hcd_cleanup_channels(struct dwc2_hsotg *hsotg) */ channel->qh = NULL; } + /* All channels have been freed, mark them available */ + if (hsotg->core_params->uframe_sched > 0) { + hsotg->available_host_channels = + hsotg->core_params->host_channels; + } else { + hsotg->non_periodic_channels = 0; + hsotg->periodic_channels = 0; + } } /** -- cgit v1.2.3 From dfea9c94837d27e38c8cc85a3c1c7c268973c3c2 Mon Sep 17 00:00:00 2001 From: Peter Chen Date: Fri, 6 Mar 2015 10:36:02 +0800 Subject: usb: udc: store usb_udc pointer in struct usb_gadget Instead of iterate to find usb_udc according to usb_gadget, this way is easier. Alan Stern suggests this way too: http://marc.info/?l=linux-usb&m=142168496528894&w=2 Acked-by: Alan Stern Signed-off-by: Peter Chen Signed-off-by: Felipe Balbi --- drivers/usb/gadget/udc/udc-core.c | 34 +++++++++------------------------- include/linux/usb/gadget.h | 3 +++ 2 files changed, 12 insertions(+), 25 deletions(-) diff --git a/drivers/usb/gadget/udc/udc-core.c b/drivers/usb/gadget/udc/udc-core.c index 5a81cb086b99..bf7ed777a87d 100644 --- a/drivers/usb/gadget/udc/udc-core.c +++ b/drivers/usb/gadget/udc/udc-core.c @@ -128,21 +128,11 @@ EXPORT_SYMBOL_GPL(usb_gadget_giveback_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); + struct usb_gadget *gadget = work_to_gadget(work); + struct usb_udc *udc = gadget->udc; - return; - -found: - mutex_unlock(&udc_lock); - - sysfs_notify(&udc->dev.kobj, NULL, "state"); + if (udc) + sysfs_notify(&udc->dev.kobj, NULL, "state"); } void usb_gadget_set_state(struct usb_gadget *gadget, @@ -278,6 +268,7 @@ int usb_add_gadget_udc_release(struct device *parent, struct usb_gadget *gadget, goto err3; udc->gadget = gadget; + gadget->udc = udc; mutex_lock(&udc_lock); list_add_tail(&udc->list, &udc_list); @@ -348,21 +339,14 @@ static void usb_gadget_remove_driver(struct usb_udc *udc) */ void usb_del_gadget_udc(struct usb_gadget *gadget) { - struct usb_udc *udc = NULL; + struct usb_udc *udc = gadget->udc; - mutex_lock(&udc_lock); - list_for_each_entry(udc, &udc_list, list) - if (udc->gadget == gadget) - goto found; - - dev_err(gadget->dev.parent, "gadget not registered.\n"); - mutex_unlock(&udc_lock); - - return; + if (!udc) + return; -found: dev_vdbg(gadget->dev.parent, "unregistering gadget\n"); + mutex_lock(&udc_lock); list_del(&udc->list); mutex_unlock(&udc_lock); diff --git a/include/linux/usb/gadget.h b/include/linux/usb/gadget.h index 02476b3a1109..242c694a5b85 100644 --- a/include/linux/usb/gadget.h +++ b/include/linux/usb/gadget.h @@ -474,6 +474,7 @@ struct usb_dcd_config_params { struct usb_gadget; struct usb_gadget_driver; +struct usb_udc; /* the rest of the api to the controller hardware: device operations, * which don't involve endpoints (or i/o). @@ -496,6 +497,7 @@ struct usb_gadget_ops { /** * struct usb_gadget - represents a usb slave device * @work: (internal use) Workqueue to be used for sysfs_notify() + * @udc: struct usb_udc pointer for this gadget * @ops: Function pointers used to access hardware-specific operations. * @ep0: Endpoint zero, used when reading or writing responses to * driver setup() requests @@ -545,6 +547,7 @@ struct usb_gadget_ops { */ struct usb_gadget { struct work_struct work; + struct usb_udc *udc; /* readonly to gadget driver */ const struct usb_gadget_ops *ops; struct usb_ep *ep0; -- cgit v1.2.3 From 628ef0d273a69d889669a459fb4675c678ae0418 Mon Sep 17 00:00:00 2001 From: Peter Chen Date: Fri, 6 Mar 2015 10:36:03 +0800 Subject: usb: udc: add usb_udc_vbus_handler This commit updates udc core vbus status, and try to connect or disconnect gadget. Signed-off-by: Peter Chen Acked-by: Alan Stern Signed-off-by: Felipe Balbi --- drivers/usb/gadget/udc/udc-core.c | 34 +++++++++++++++++++++++++++++++++- include/linux/usb/gadget.h | 4 ++++ 2 files changed, 37 insertions(+), 1 deletion(-) diff --git a/drivers/usb/gadget/udc/udc-core.c b/drivers/usb/gadget/udc/udc-core.c index bf7ed777a87d..d69c35558f68 100644 --- a/drivers/usb/gadget/udc/udc-core.c +++ b/drivers/usb/gadget/udc/udc-core.c @@ -35,6 +35,8 @@ * @dev - the child device to the actual controller * @gadget - the gadget. For use by the class code * @list - for use by the udc class driver + * @vbus - for udcs who care about vbus status, this value is real vbus status; + * for udcs who do not care about vbus status, this value is always true * * This represents the internal data structure which is used by the UDC-class * to hold information about udc driver and gadget together. @@ -44,6 +46,7 @@ struct usb_udc { struct usb_gadget *gadget; struct device dev; struct list_head list; + bool vbus; }; static struct class *udc_class; @@ -145,6 +148,34 @@ EXPORT_SYMBOL_GPL(usb_gadget_set_state); /* ------------------------------------------------------------------------- */ +static void usb_udc_connect_control(struct usb_udc *udc) +{ + if (udc->vbus) + usb_gadget_connect(udc->gadget); + else + usb_gadget_disconnect(udc->gadget); +} + +/** + * usb_udc_vbus_handler - updates the udc core vbus status, and try to + * connect or disconnect gadget + * @gadget: The gadget which vbus change occurs + * @status: The vbus status + * + * The udc driver calls it when it wants to connect or disconnect gadget + * according to vbus status. + */ +void usb_udc_vbus_handler(struct usb_gadget *gadget, bool status) +{ + struct usb_udc *udc = gadget->udc; + + if (udc) { + udc->vbus = status; + usb_udc_connect_control(udc); + } +} +EXPORT_SYMBOL_GPL(usb_udc_vbus_handler); + /** * usb_gadget_udc_reset - notifies the udc core that bus reset occurs * @gadget: The gadget which bus reset occurs @@ -278,6 +309,7 @@ int usb_add_gadget_udc_release(struct device *parent, struct usb_gadget *gadget, goto err4; usb_gadget_set_state(gadget, USB_STATE_NOTATTACHED); + udc->vbus = true; mutex_unlock(&udc_lock); @@ -381,7 +413,7 @@ static int udc_bind_to_driver(struct usb_udc *udc, struct usb_gadget_driver *dri driver->unbind(udc->gadget); goto err1; } - usb_gadget_connect(udc->gadget); + usb_udc_connect_control(udc); kobject_uevent(&udc->dev.kobj, KOBJ_CHANGE); return 0; diff --git a/include/linux/usb/gadget.h b/include/linux/usb/gadget.h index 242c694a5b85..4f3dfb7d0654 100644 --- a/include/linux/usb/gadget.h +++ b/include/linux/usb/gadget.h @@ -1032,6 +1032,10 @@ extern void usb_gadget_udc_reset(struct usb_gadget *gadget, extern void usb_gadget_giveback_request(struct usb_ep *ep, struct usb_request *req); +/*-------------------------------------------------------------------------*/ + +/* utility to update vbus status for udc core, it may be scheduled */ +extern void usb_udc_vbus_handler(struct usb_gadget *gadget, bool status); /*-------------------------------------------------------------------------*/ -- cgit v1.2.3 From 467a78c82ad5f9a3e3c80eb4ba17de6bb70e4f9b Mon Sep 17 00:00:00 2001 From: Peter Chen Date: Fri, 6 Mar 2015 10:36:04 +0800 Subject: usb: chipidea: udc: apply new usb_udc_vbus_handler interface It can move all pullup/pulldown operation control to udc-core through usb_gadget_connect/usb_gadget_disconnect according to vbus status. Signed-off-by: Peter Chen Signed-off-by: Felipe Balbi --- drivers/usb/chipidea/udc.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/drivers/usb/chipidea/udc.c b/drivers/usb/chipidea/udc.c index ff451048c1ac..55a36e046a1e 100644 --- a/drivers/usb/chipidea/udc.c +++ b/drivers/usb/chipidea/udc.c @@ -86,10 +86,8 @@ static int hw_device_state(struct ci_hdrc *ci, u32 dma) /* interrupt, error, port change, reset, sleep/suspend */ hw_write(ci, OP_USBINTR, ~0, USBi_UI|USBi_UEI|USBi_PCI|USBi_URI|USBi_SLI); - hw_write(ci, OP_USBCMD, USBCMD_RS, USBCMD_RS); } else { hw_write(ci, OP_USBINTR, ~0, 0); - hw_write(ci, OP_USBCMD, USBCMD_RS, 0); } return 0; } @@ -1474,7 +1472,9 @@ static int ci_udc_vbus_session(struct usb_gadget *_gadget, int is_active) hw_device_reset(ci); hw_device_state(ci, ci->ep0out->qh.dma); usb_gadget_set_state(_gadget, USB_STATE_POWERED); + usb_udc_vbus_handler(_gadget, true); } else { + usb_udc_vbus_handler(_gadget, false); if (ci->driver) ci->driver->disconnect(&ci->gadget); hw_device_state(ci, 0); @@ -1540,13 +1540,12 @@ static int ci_udc_pullup(struct usb_gadget *_gadget, int is_on) { struct ci_hdrc *ci = container_of(_gadget, struct ci_hdrc, gadget); - if (!ci->vbus_active) - return -EOPNOTSUPP; - + pm_runtime_get_sync(&ci->gadget.dev); if (is_on) hw_write(ci, OP_USBCMD, USBCMD_RS, USBCMD_RS); else hw_write(ci, OP_USBCMD, USBCMD_RS, 0); + pm_runtime_put_sync(&ci->gadget.dev); return 0; } @@ -1676,6 +1675,7 @@ static int ci_udc_start(struct usb_gadget *gadget, spin_lock_irqsave(&ci->lock, flags); hw_device_reset(ci); } else { + usb_udc_vbus_handler(&ci->gadget, false); pm_runtime_put_sync(&ci->gadget.dev); return retval; } -- cgit v1.2.3 From 869aee0f31429fa9d94d5aef539602b73ae0cf4b Mon Sep 17 00:00:00 2001 From: Axel Lin Date: Thu, 12 Mar 2015 09:15:28 +0800 Subject: usb: phy: Find the right match in devm_usb_phy_match The res parameter passed to devm_usb_phy_match() is the location where the pointer to the usb_phy is stored, hence it needs to be dereferenced before comparing to the match data in order to find the correct match. Fixes: 410219dcd2ba ("usb: otg: utils: devres: Add API's to associate a device with the phy") Signed-off-by: Axel Lin Cc: # v3.6+ Signed-off-by: Felipe Balbi --- drivers/usb/phy/phy.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/usb/phy/phy.c b/drivers/usb/phy/phy.c index 2f9735b35338..d1cd6b50f520 100644 --- a/drivers/usb/phy/phy.c +++ b/drivers/usb/phy/phy.c @@ -81,7 +81,9 @@ static void devm_usb_phy_release(struct device *dev, void *res) static int devm_usb_phy_match(struct device *dev, void *res, void *match_data) { - return res == match_data; + struct usb_phy **phy = res; + + return *phy == match_data; } /** -- cgit v1.2.3 From 990919cad55a8c38fc73b0a6a0c9e5e4e927a5f8 Mon Sep 17 00:00:00 2001 From: Peter Chen Date: Thu, 12 Mar 2015 09:47:53 +0800 Subject: usb: common: otg-fsm: only signal connect after switching to peripheral We should signal connect (pull up dp) after we have already at peripheral mode, otherwise, the dp may be toggled due to we reset controller or do disconnect during the initialization for peripheral, then, the host may be confused during the enumeration, eg, it finds the reset can't succeed, but the device is still there, see below error message. hub 1-0:1.0: USB hub found hub 1-0:1.0: 1 port detected hub 1-0:1.0: cannot reset port 1 (err = -32) hub 1-0:1.0: cannot reset port 1 (err = -32) hub 1-0:1.0: cannot reset port 1 (err = -32) hub 1-0:1.0: cannot reset port 1 (err = -32) hub 1-0:1.0: cannot reset port 1 (err = -32) hub 1-0:1.0: Cannot enable port 1. Maybe the USB cable is bad? hub 1-0:1.0: cannot reset port 1 (err = -32) hub 1-0:1.0: cannot reset port 1 (err = -32) hub 1-0:1.0: cannot reset port 1 (err = -32) hub 1-0:1.0: cannot reset port 1 (err = -32) hub 1-0:1.0: cannot reset port 1 (err = -32) hub 1-0:1.0: Cannot enable port 1. Maybe the USB cable is bad? hub 1-0:1.0: cannot reset port 1 (err = -32) hub 1-0:1.0: cannot reset port 1 (err = -32) hub 1-0:1.0: cannot reset port 1 (err = -32) hub 1-0:1.0: cannot reset port 1 (err = -32) hub 1-0:1.0: cannot reset port 1 (err = -32) hub 1-0:1.0: Cannot enable port 1. Maybe the USB cable is bad? hub 1-0:1.0: cannot reset port 1 (err = -32) hub 1-0:1.0: cannot reset port 1 (err = -32) hub 1-0:1.0: cannot reset port 1 (err = -32) hub 1-0:1.0: cannot reset port 1 (err = -32) hub 1-0:1.0: cannot reset port 1 (err = -32) hub 1-0:1.0: Cannot enable port 1. Maybe the USB cable is bad? hub 1-0:1.0: unable to enumerate USB device on port 1 Fixes: the issue existed when the otg fsm code was added. Cc: # v3.16+ Signed-off-by: Peter Chen Signed-off-by: Felipe Balbi --- drivers/usb/common/usb-otg-fsm.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/usb/common/usb-otg-fsm.c b/drivers/usb/common/usb-otg-fsm.c index c6b35b77dab7..61d538aa2346 100644 --- a/drivers/usb/common/usb-otg-fsm.c +++ b/drivers/usb/common/usb-otg-fsm.c @@ -150,9 +150,9 @@ static int otg_set_state(struct otg_fsm *fsm, enum usb_otg_state new_state) break; case OTG_STATE_B_PERIPHERAL: otg_chrg_vbus(fsm, 0); - otg_loc_conn(fsm, 1); otg_loc_sof(fsm, 0); otg_set_protocol(fsm, PROTO_GADGET); + otg_loc_conn(fsm, 1); break; case OTG_STATE_B_WAIT_ACON: otg_chrg_vbus(fsm, 0); @@ -213,10 +213,10 @@ static int otg_set_state(struct otg_fsm *fsm, enum usb_otg_state new_state) break; case OTG_STATE_A_PERIPHERAL: - otg_loc_conn(fsm, 1); otg_loc_sof(fsm, 0); otg_set_protocol(fsm, PROTO_GADGET); otg_drv_vbus(fsm, 1); + otg_loc_conn(fsm, 1); otg_add_timer(fsm, A_BIDL_ADIS); break; case OTG_STATE_A_WAIT_VFALL: -- cgit v1.2.3 From 00f30d29b497577954b20237b405e9d22b5286c2 Mon Sep 17 00:00:00 2001 From: Yoshihiro Shimoda Date: Mon, 16 Mar 2015 16:36:48 +0900 Subject: usb: renesas_usbhs: fix spinlock suspected in a gadget complete function According to the gadget.h, a "complete" function will always be called with interrupts disabled. However, sometimes usbhsg_queue_pop() function is called with interrupts enabled. So, this function should be held by usbhs_lock() to disable interruption. Also, this driver has to call spin_unlock() to avoid spinlock recursion by this driver before calling usb_gadget_giveback_request(). Otherwise, there is possible to cause a spinlock suspected in a gadget complete function. Signed-off-by: Yoshihiro Shimoda Signed-off-by: Felipe Balbi --- drivers/usb/renesas_usbhs/mod_gadget.c | 22 +++++++++++++++++++--- 1 file changed, 19 insertions(+), 3 deletions(-) diff --git a/drivers/usb/renesas_usbhs/mod_gadget.c b/drivers/usb/renesas_usbhs/mod_gadget.c index e0384af77e56..dc2aa3261202 100644 --- a/drivers/usb/renesas_usbhs/mod_gadget.c +++ b/drivers/usb/renesas_usbhs/mod_gadget.c @@ -119,18 +119,34 @@ struct usbhsg_recip_handle { /* * queue push/pop */ -static void usbhsg_queue_pop(struct usbhsg_uep *uep, - struct usbhsg_request *ureq, - int status) +static void __usbhsg_queue_pop(struct usbhsg_uep *uep, + struct usbhsg_request *ureq, + int status) { struct usbhsg_gpriv *gpriv = usbhsg_uep_to_gpriv(uep); struct usbhs_pipe *pipe = usbhsg_uep_to_pipe(uep); struct device *dev = usbhsg_gpriv_to_dev(gpriv); + struct usbhs_priv *priv = usbhsg_gpriv_to_priv(gpriv); dev_dbg(dev, "pipe %d : queue pop\n", usbhs_pipe_number(pipe)); ureq->req.status = status; + spin_unlock(usbhs_priv_to_lock(priv)); usb_gadget_giveback_request(&uep->ep, &ureq->req); + spin_lock(usbhs_priv_to_lock(priv)); +} + +static void usbhsg_queue_pop(struct usbhsg_uep *uep, + struct usbhsg_request *ureq, + int status) +{ + struct usbhsg_gpriv *gpriv = usbhsg_uep_to_gpriv(uep); + struct usbhs_priv *priv = usbhsg_gpriv_to_priv(gpriv); + unsigned long flags; + + usbhs_lock(priv, flags); + __usbhsg_queue_pop(uep, ureq, status); + usbhs_unlock(priv, flags); } static void usbhsg_queue_done(struct usbhs_priv *priv, struct usbhs_pkt *pkt) -- cgit v1.2.3 From 557f3586aaac0202010eb70e00d2694437f0cb37 Mon Sep 17 00:00:00 2001 From: Joe Perches Date: Mon, 16 Mar 2015 11:09:38 -0700 Subject: usb: gadget: goku_udc: Remove uses of seq_ return values The seq_printf/seq_puts/seq_putc return values, because they are frequently misused, will eventually be converted to void. See: commit 1f33c41c03da ("seq_file: Rename seq_overflow() to seq_has_overflowed() and make public") Miscellanea: o Coalesce formats, realign arguments o Create static functions for statement expression macros o Use c90 style comments instead of c99 Signed-off-by: Joe Perches Signed-off-by: Felipe Balbi --- drivers/usb/gadget/udc/goku_udc.c | 233 ++++++++++++++++++++------------------ 1 file changed, 122 insertions(+), 111 deletions(-) diff --git a/drivers/usb/gadget/udc/goku_udc.c b/drivers/usb/gadget/udc/goku_udc.c index 5b9176e7202a..9e8d842e8c08 100644 --- a/drivers/usb/gadget/udc/goku_udc.c +++ b/drivers/usb/gadget/udc/goku_udc.c @@ -1024,35 +1024,79 @@ static const char proc_node_name [] = "driver/udc"; static void dump_intmask(struct seq_file *m, const char *label, u32 mask) { /* int_status is the same format ... */ - seq_printf(m, - "%s %05X =" FOURBITS EIGHTBITS EIGHTBITS "\n", - label, mask, - (mask & INT_PWRDETECT) ? " power" : "", - (mask & INT_SYSERROR) ? " sys" : "", - (mask & INT_MSTRDEND) ? " in-dma" : "", - (mask & INT_MSTWRTMOUT) ? " wrtmo" : "", - - (mask & INT_MSTWREND) ? " out-dma" : "", - (mask & INT_MSTWRSET) ? " wrset" : "", - (mask & INT_ERR) ? " err" : "", - (mask & INT_SOF) ? " sof" : "", - - (mask & INT_EP3NAK) ? " ep3nak" : "", - (mask & INT_EP2NAK) ? " ep2nak" : "", - (mask & INT_EP1NAK) ? " ep1nak" : "", - (mask & INT_EP3DATASET) ? " ep3" : "", - - (mask & INT_EP2DATASET) ? " ep2" : "", - (mask & INT_EP1DATASET) ? " ep1" : "", - (mask & INT_STATUSNAK) ? " ep0snak" : "", - (mask & INT_STATUS) ? " ep0status" : "", - - (mask & INT_SETUP) ? " setup" : "", - (mask & INT_ENDPOINT0) ? " ep0" : "", - (mask & INT_USBRESET) ? " reset" : "", - (mask & INT_SUSPEND) ? " suspend" : ""); + seq_printf(m, "%s %05X =" FOURBITS EIGHTBITS EIGHTBITS "\n", + label, mask, + (mask & INT_PWRDETECT) ? " power" : "", + (mask & INT_SYSERROR) ? " sys" : "", + (mask & INT_MSTRDEND) ? " in-dma" : "", + (mask & INT_MSTWRTMOUT) ? " wrtmo" : "", + + (mask & INT_MSTWREND) ? " out-dma" : "", + (mask & INT_MSTWRSET) ? " wrset" : "", + (mask & INT_ERR) ? " err" : "", + (mask & INT_SOF) ? " sof" : "", + + (mask & INT_EP3NAK) ? " ep3nak" : "", + (mask & INT_EP2NAK) ? " ep2nak" : "", + (mask & INT_EP1NAK) ? " ep1nak" : "", + (mask & INT_EP3DATASET) ? " ep3" : "", + + (mask & INT_EP2DATASET) ? " ep2" : "", + (mask & INT_EP1DATASET) ? " ep1" : "", + (mask & INT_STATUSNAK) ? " ep0snak" : "", + (mask & INT_STATUS) ? " ep0status" : "", + + (mask & INT_SETUP) ? " setup" : "", + (mask & INT_ENDPOINT0) ? " ep0" : "", + (mask & INT_USBRESET) ? " reset" : "", + (mask & INT_SUSPEND) ? " suspend" : ""); +} + +static const char *udc_ep_state(enum ep0state state) +{ + switch (state) { + case EP0_DISCONNECT: + return "ep0_disconnect"; + case EP0_IDLE: + return "ep0_idle"; + case EP0_IN: + return "ep0_in"; + case EP0_OUT: + return "ep0_out"; + case EP0_STATUS: + return "ep0_status"; + case EP0_STALL: + return "ep0_stall"; + case EP0_SUSPEND: + return "ep0_suspend"; + } + + return "ep0_?"; } +static const char *udc_ep_status(u32 status) +{ + switch (status & EPxSTATUS_EP_MASK) { + case EPxSTATUS_EP_READY: + return "ready"; + case EPxSTATUS_EP_DATAIN: + return "packet"; + case EPxSTATUS_EP_FULL: + return "full"; + case EPxSTATUS_EP_TX_ERR: /* host will retry */ + return "tx_err"; + case EPxSTATUS_EP_RX_ERR: + return "rx_err"; + case EPxSTATUS_EP_BUSY: /* ep0 only */ + return "busy"; + case EPxSTATUS_EP_STALL: + return "stall"; + case EPxSTATUS_EP_INVALID: /* these "can't happen" */ + return "invalid"; + } + + return "?"; +} static int udc_proc_read(struct seq_file *m, void *v) { @@ -1068,29 +1112,18 @@ static int udc_proc_read(struct seq_file *m, void *v) tmp = readl(®s->power_detect); is_usb_connected = tmp & PW_DETECT; seq_printf(m, - "%s - %s\n" - "%s version: %s %s\n" - "Gadget driver: %s\n" - "Host %s, %s\n" - "\n", - pci_name(dev->pdev), driver_desc, - driver_name, DRIVER_VERSION, dmastr(), - dev->driver ? dev->driver->driver.name : "(none)", - is_usb_connected - ? ((tmp & PW_PULLUP) ? "full speed" : "powered") - : "disconnected", - ({const char *state; - switch(dev->ep0state){ - case EP0_DISCONNECT: state = "ep0_disconnect"; break; - case EP0_IDLE: state = "ep0_idle"; break; - case EP0_IN: state = "ep0_in"; break; - case EP0_OUT: state = "ep0_out"; break; - case EP0_STATUS: state = "ep0_status"; break; - case EP0_STALL: state = "ep0_stall"; break; - case EP0_SUSPEND: state = "ep0_suspend"; break; - default: state = "ep0_?"; break; - } state; }) - ); + "%s - %s\n" + "%s version: %s %s\n" + "Gadget driver: %s\n" + "Host %s, %s\n" + "\n", + pci_name(dev->pdev), driver_desc, + driver_name, DRIVER_VERSION, dmastr(), + dev->driver ? dev->driver->driver.name : "(none)", + is_usb_connected + ? ((tmp & PW_PULLUP) ? "full speed" : "powered") + : "disconnected", + udc_ep_state(dev->ep0state)); dump_intmask(m, "int_status", readl(®s->int_status)); dump_intmask(m, "int_enable", readl(®s->int_enable)); @@ -1099,31 +1132,30 @@ static int udc_proc_read(struct seq_file *m, void *v) goto done; /* registers for (active) device and ep0 */ - if (seq_printf(m, "\nirqs %lu\ndataset %02x " - "single.bcs %02x.%02x state %x addr %u\n", - dev->irqs, readl(®s->DataSet), - readl(®s->EPxSingle), readl(®s->EPxBCS), - readl(®s->UsbState), - readl(®s->address)) < 0) + seq_printf(m, "\nirqs %lu\ndataset %02x single.bcs %02x.%02x state %x addr %u\n", + dev->irqs, readl(®s->DataSet), + readl(®s->EPxSingle), readl(®s->EPxBCS), + readl(®s->UsbState), + readl(®s->address)); + if (seq_has_overflowed(m)) goto done; tmp = readl(®s->dma_master); - if (seq_printf(m, - "dma %03X =" EIGHTBITS "%s %s\n", tmp, - (tmp & MST_EOPB_DIS) ? " eopb-" : "", - (tmp & MST_EOPB_ENA) ? " eopb+" : "", - (tmp & MST_TIMEOUT_DIS) ? " tmo-" : "", - (tmp & MST_TIMEOUT_ENA) ? " tmo+" : "", - - (tmp & MST_RD_EOPB) ? " eopb" : "", - (tmp & MST_RD_RESET) ? " in_reset" : "", - (tmp & MST_WR_RESET) ? " out_reset" : "", - (tmp & MST_RD_ENA) ? " IN" : "", - - (tmp & MST_WR_ENA) ? " OUT" : "", - (tmp & MST_CONNECTION) - ? "ep1in/ep2out" - : "ep1out/ep2in") < 0) + seq_printf(m, "dma %03X =" EIGHTBITS "%s %s\n", + tmp, + (tmp & MST_EOPB_DIS) ? " eopb-" : "", + (tmp & MST_EOPB_ENA) ? " eopb+" : "", + (tmp & MST_TIMEOUT_DIS) ? " tmo-" : "", + (tmp & MST_TIMEOUT_ENA) ? " tmo+" : "", + + (tmp & MST_RD_EOPB) ? " eopb" : "", + (tmp & MST_RD_RESET) ? " in_reset" : "", + (tmp & MST_WR_RESET) ? " out_reset" : "", + (tmp & MST_RD_ENA) ? " IN" : "", + + (tmp & MST_WR_ENA) ? " OUT" : "", + (tmp & MST_CONNECTION) ? "ep1in/ep2out" : "ep1out/ep2in"); + if (seq_has_overflowed(m)) goto done; /* dump endpoint queues */ @@ -1135,44 +1167,23 @@ static int udc_proc_read(struct seq_file *m, void *v) continue; tmp = readl(ep->reg_status); - if (seq_printf(m, - "%s %s max %u %s, irqs %lu, " - "status %02x (%s) " FOURBITS "\n", - ep->ep.name, - ep->is_in ? "in" : "out", - ep->ep.maxpacket, - ep->dma ? "dma" : "pio", - ep->irqs, - tmp, ({ char *s; - switch (tmp & EPxSTATUS_EP_MASK) { - case EPxSTATUS_EP_READY: - s = "ready"; break; - case EPxSTATUS_EP_DATAIN: - s = "packet"; break; - case EPxSTATUS_EP_FULL: - s = "full"; break; - case EPxSTATUS_EP_TX_ERR: // host will retry - s = "tx_err"; break; - case EPxSTATUS_EP_RX_ERR: - s = "rx_err"; break; - case EPxSTATUS_EP_BUSY: /* ep0 only */ - s = "busy"; break; - case EPxSTATUS_EP_STALL: - s = "stall"; break; - case EPxSTATUS_EP_INVALID: // these "can't happen" - s = "invalid"; break; - default: - s = "?"; break; - } s; }), - (tmp & EPxSTATUS_TOGGLE) ? "data1" : "data0", - (tmp & EPxSTATUS_SUSPEND) ? " suspend" : "", - (tmp & EPxSTATUS_FIFO_DISABLE) ? " disable" : "", - (tmp & EPxSTATUS_STAGE_ERROR) ? " ep0stat" : "" - ) < 0) + seq_printf(m, "%s %s max %u %s, irqs %lu, status %02x (%s) " FOURBITS "\n", + ep->ep.name, + ep->is_in ? "in" : "out", + ep->ep.maxpacket, + ep->dma ? "dma" : "pio", + ep->irqs, + tmp, udc_ep_status(tmp), + (tmp & EPxSTATUS_TOGGLE) ? "data1" : "data0", + (tmp & EPxSTATUS_SUSPEND) ? " suspend" : "", + (tmp & EPxSTATUS_FIFO_DISABLE) ? " disable" : "", + (tmp & EPxSTATUS_STAGE_ERROR) ? " ep0stat" : ""); + if (seq_has_overflowed(m)) goto done; if (list_empty(&ep->queue)) { - if (seq_puts(m, "\t(nothing queued)\n") < 0) + seq_puts(m, "\t(nothing queued)\n"); + if (seq_has_overflowed(m)) goto done; continue; } @@ -1187,10 +1198,10 @@ static int udc_proc_read(struct seq_file *m, void *v) } else tmp = req->req.actual; - if (seq_printf(m, - "\treq %p len %u/%u buf %p\n", - &req->req, tmp, req->req.length, - req->req.buf) < 0) + seq_printf(m, "\treq %p len %u/%u buf %p\n", + &req->req, tmp, req->req.length, + req->req.buf); + if (seq_has_overflowed(m)) goto done; } } -- cgit v1.2.3 From 3463d795bf0f8d9b32d4deabaaffcb81a1abb92b Mon Sep 17 00:00:00 2001 From: Fabian Frederick Date: Mon, 16 Mar 2015 20:20:27 +0100 Subject: usb: gadget: constify of_device_id array of_device_id is always used as const. (See driver.of_match_table and open firmware functions) Signed-off-by: Fabian Frederick Signed-off-by: Felipe Balbi --- drivers/usb/gadget/udc/pxa27x_udc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/usb/gadget/udc/pxa27x_udc.c b/drivers/usb/gadget/udc/pxa27x_udc.c index 486f7546de8c..b51226abade6 100644 --- a/drivers/usb/gadget/udc/pxa27x_udc.c +++ b/drivers/usb/gadget/udc/pxa27x_udc.c @@ -2387,7 +2387,7 @@ static struct pxa_udc memory = { }; #if defined(CONFIG_OF) -static struct of_device_id udc_pxa_dt_ids[] = { +static const struct of_device_id udc_pxa_dt_ids[] = { { .compatible = "marvell,pxa270-udc" }, {} }; -- cgit v1.2.3 From b48cb02de37976ad2ebef201ad40aa40156f6daa Mon Sep 17 00:00:00 2001 From: John Youn Date: Mon, 16 Mar 2015 18:10:02 -0700 Subject: usb: dwc2: pci: Correctly compile dwc2-pci as a module or built-in The dwc2-pci driver should be compiled as a module when configured to do so. If the dwc2-pci is configured as a module but actually built-in, it can cause build errors due to the fact that the generic-phy will be allowed to compile as a module causing undefined references. Reported-by: Alan Cox Signed-off-by: John Youn Signed-off-by: Felipe Balbi --- drivers/usb/dwc2/Makefile | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/drivers/usb/dwc2/Makefile b/drivers/usb/dwc2/Makefile index 8f752679752a..f07b425eaff3 100644 --- a/drivers/usb/dwc2/Makefile +++ b/drivers/usb/dwc2/Makefile @@ -19,10 +19,8 @@ endif # mode. The PCI bus interface module will called dwc2_pci.ko and the platform # interface module will be called dwc2_platform.ko. -ifneq ($(CONFIG_USB_DWC2_PCI),) - obj-$(CONFIG_USB_DWC2) += dwc2_pci.o - dwc2_pci-y := pci.o -endif +obj-$(CONFIG_USB_DWC2_PCI) += dwc2_pci.o +dwc2_pci-y := pci.o obj-$(CONFIG_USB_DWC2_PLATFORM) += dwc2_platform.o dwc2_platform-y := platform.o -- cgit v1.2.3 From a3dd3befd7cb7d445fcb7c51cd922bf89afe0762 Mon Sep 17 00:00:00 2001 From: Ben Dooks Date: Wed, 18 Mar 2015 15:53:09 +0000 Subject: usb: gadget: atmel_usba: use endian agnostic IO on ARM Change from using the __raw IO accesors to the endian agnostic versions of readl/writel_relaxed when not on AVR32. This fixes issues with running big endian on ARMv7. Signed-off-by: Ben Dooks -- CC: Nicolas Ferre CC: Felipe Balbi CC: Greg Kroah-Hartman CC: linux-usb@vger.kernel.org Signed-off-by: Felipe Balbi --- drivers/usb/gadget/udc/atmel_usba_udc.c | 4 ++-- drivers/usb/gadget/udc/atmel_usba_udc.h | 22 ++++++++++++++++------ 2 files changed, 18 insertions(+), 8 deletions(-) diff --git a/drivers/usb/gadget/udc/atmel_usba_udc.c b/drivers/usb/gadget/udc/atmel_usba_udc.c index d019b6c9d74d..4c01953a0869 100644 --- a/drivers/usb/gadget/udc/atmel_usba_udc.c +++ b/drivers/usb/gadget/udc/atmel_usba_udc.c @@ -152,7 +152,7 @@ static int regs_dbg_open(struct inode *inode, struct file *file) spin_lock_irq(&udc->lock); for (i = 0; i < inode->i_size / 4; i++) - data[i] = __raw_readl(udc->regs + i * 4); + data[i] = usba_io_readl(udc->regs + i * 4); spin_unlock_irq(&udc->lock); file->private_data = data; @@ -1249,7 +1249,7 @@ static int handle_ep0_setup(struct usba_udc *udc, struct usba_ep *ep, if (crq->wLength != cpu_to_le16(sizeof(status))) goto stall; ep->state = DATA_STAGE_IN; - __raw_writew(status, ep->fifo); + usba_io_writew(status, ep->fifo); usba_ep_writel(ep, SET_STA, USBA_TX_PK_RDY); break; } diff --git a/drivers/usb/gadget/udc/atmel_usba_udc.h b/drivers/usb/gadget/udc/atmel_usba_udc.h index 085749a649e1..ea448a344767 100644 --- a/drivers/usb/gadget/udc/atmel_usba_udc.h +++ b/drivers/usb/gadget/udc/atmel_usba_udc.h @@ -191,18 +191,28 @@ | USBA_BF(name, value)) /* Register access macros */ +#ifdef CONFIG_AVR32 +#define usba_io_readl __raw_readl +#define usba_io_writel __raw_writel +#define usba_io_writew __raw_writew +#else +#define usba_io_readl readl_relaxed +#define usba_io_writel writel_relaxed +#define usba_io_writew writew_relaxed +#endif + #define usba_readl(udc, reg) \ - __raw_readl((udc)->regs + USBA_##reg) + usba_io_readl((udc)->regs + USBA_##reg) #define usba_writel(udc, reg, value) \ - __raw_writel((value), (udc)->regs + USBA_##reg) + usba_io_writel((value), (udc)->regs + USBA_##reg) #define usba_ep_readl(ep, reg) \ - __raw_readl((ep)->ep_regs + USBA_EPT_##reg) + usba_io_readl((ep)->ep_regs + USBA_EPT_##reg) #define usba_ep_writel(ep, reg, value) \ - __raw_writel((value), (ep)->ep_regs + USBA_EPT_##reg) + usba_io_writel((value), (ep)->ep_regs + USBA_EPT_##reg) #define usba_dma_readl(ep, reg) \ - __raw_readl((ep)->dma_regs + USBA_DMA_##reg) + usba_io_readl((ep)->dma_regs + USBA_DMA_##reg) #define usba_dma_writel(ep, reg, value) \ - __raw_writel((value), (ep)->dma_regs + USBA_DMA_##reg) + usba_io_writel((value), (ep)->dma_regs + USBA_DMA_##reg) /* Calculate base address for a given endpoint or DMA controller */ #define USBA_EPT_BASE(x) (0x100 + (x) * 0x20) -- cgit v1.2.3 From f4b4976b6e3171d408d0f6e08b12189bf60d1d22 Mon Sep 17 00:00:00 2001 From: Andrzej Pietrasiewicz Date: Tue, 24 Mar 2015 10:58:16 +0100 Subject: usb: gadget: f_printer: fix dependencies If f_printer is selected without legacy g_printer, it should depend on USB_CONFIGFS which pulls in libcomposite. Signed-off-by: Andrzej Pietrasiewicz Signed-off-by: Felipe Balbi --- drivers/usb/gadget/Kconfig | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/usb/gadget/Kconfig b/drivers/usb/gadget/Kconfig index 3bb0e67fded2..bcf83c0a6e62 100644 --- a/drivers/usb/gadget/Kconfig +++ b/drivers/usb/gadget/Kconfig @@ -440,6 +440,7 @@ config USB_CONFIGFS_F_UVC config USB_CONFIGFS_F_PRINTER bool "Printer function" select USB_F_PRINTER + depends on USB_CONFIGFS help The Printer function channels data between the USB host and a userspace program driving the print engine. The user space -- cgit v1.2.3 From 1ea276cbfc0201c9804f38474f3ec2c4488a2fc6 Mon Sep 17 00:00:00 2001 From: "Ivan T. Ivanov" Date: Sat, 21 Mar 2015 15:25:52 +0200 Subject: usb: phy: Fix USB_ULPI_VIEWPORT Kconfig dependency USB_ULPI_VIEWPORT didn't depend on USB_ULPI, while USB_ULPI is using non user selectable USB_ULPI_VIEWPORT. Fix this. Signed-off-by: Ivan T. Ivanov Signed-off-by: Felipe Balbi --- drivers/usb/phy/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/usb/phy/Kconfig b/drivers/usb/phy/Kconfig index 2fb3828b5089..2175678e674e 100644 --- a/drivers/usb/phy/Kconfig +++ b/drivers/usb/phy/Kconfig @@ -202,13 +202,13 @@ config USB_RCAR_GEN2_PHY config USB_ULPI bool "Generic ULPI Transceiver Driver" depends on ARM || ARM64 + select USB_ULPI_VIEWPORT help Enable this to support ULPI connected USB OTG transceivers which are likely found on embedded boards. config USB_ULPI_VIEWPORT bool - depends on USB_ULPI help Provides read/write operations to the ULPI phy register set for controllers with a viewport register (e.g. Chipidea/ARC controllers). -- cgit v1.2.3 From 3e457371f436e89ce9239674828f9729a36b2595 Mon Sep 17 00:00:00 2001 From: Tony Lindgren Date: Thu, 19 Mar 2015 17:10:16 -0700 Subject: usb: musb: Fix fifo reads for dm816x with musb_dsps Looks like dm81xx can only do 32-bit fifo reads like am35x. Let's set up musb-dsps with a custom read_fifo function based on the compatible flag. Otherwise we can get the following errors when starting dhclient on a asix USB Ethernet adapter: asix 2-1:1.0 eth2: asix_rx_fixup() Bad Header Length 0xffff003c, offset 4 While at it, let's also remove pointless cast of the driver data. Cc: Bin Liu Cc: Brian Hutchinson Cc: George Cherian Cc: Sergei Shtylyov Signed-off-by: Tony Lindgren Signed-off-by: Felipe Balbi --- drivers/usb/musb/musb_dsps.c | 25 ++++++++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) diff --git a/drivers/usb/musb/musb_dsps.c b/drivers/usb/musb/musb_dsps.c index 09648b4e40c2..b23ad150a165 100644 --- a/drivers/usb/musb/musb_dsps.c +++ b/drivers/usb/musb/musb_dsps.c @@ -615,6 +615,24 @@ static int dsps_musb_recover(struct musb *musb) return session_restart ? 0 : -EPIPE; } +/* Similar to am35x, dm81xx support only 32-bit read operation */ +static void dsps_read_fifo32(struct musb_hw_ep *hw_ep, u16 len, u8 *dst) +{ + void __iomem *fifo = hw_ep->fifo; + + if (len >= 4) { + readsl(fifo, dst, len >> 2); + dst += len & ~0x03; + len &= 0x03; + } + + /* Read any remaining 1 to 3 bytes */ + if (len > 0) { + u32 val = musb_readl(fifo, 0); + memcpy(dst, &val, len); + } +} + static struct musb_platform_ops dsps_ops = { .quirks = MUSB_INDEXED_EP, .init = dsps_musb_init, @@ -761,6 +779,9 @@ static int dsps_probe(struct platform_device *pdev) } wrp = match->data; + if (of_device_is_compatible(pdev->dev.of_node, "ti,musb-dm816")) + dsps_ops.read_fifo = dsps_read_fifo32; + /* allocate glue */ glue = devm_kzalloc(&pdev->dev, sizeof(*glue), GFP_KERNEL); if (!glue) @@ -837,7 +858,9 @@ static const struct dsps_musb_wrapper am33xx_driver_data = { static const struct of_device_id musb_dsps_of_match[] = { { .compatible = "ti,musb-am33xx", - .data = (void *) &am33xx_driver_data, }, + .data = &am33xx_driver_data, }, + { .compatible = "ti,musb-dm816", + .data = &am33xx_driver_data, }, { }, }; MODULE_DEVICE_TABLE(of, musb_dsps_of_match); -- cgit v1.2.3