From c688419141ad6134d7973fcf182e3719e98d7491 Mon Sep 17 00:00:00 2001 From: Sebastian Andrzej Siewior Date: Mon, 9 Jan 2012 13:14:56 +0100 Subject: usb: gadget: dummy_hcd: initialize max_streams early While playing with uasp I noticed that it does not work with dummy_hcd. The problem is that uasp requires a stream capable endpoint which it is requesting at bind time like every other gadget. dummy_hcd however initializes the max_stream value after connect once it knows if it runs at SS or not. I don't think that it is might be wrong to initialize the stream capability even at HS speed. The gadget may not use this descriptor at HS speed so it should not cause any damage. Signed-off-by: Sebastian Andrzej Siewior Signed-off-by: Felipe Balbi diff --git a/drivers/usb/gadget/dummy_hcd.c b/drivers/usb/gadget/dummy_hcd.c index db815c2..5a21bf5c 100644 --- a/drivers/usb/gadget/dummy_hcd.c +++ b/drivers/usb/gadget/dummy_hcd.c @@ -800,17 +800,10 @@ static int dummy_set_selfpowered (struct usb_gadget *_gadget, int value) static void dummy_udc_udpate_ep0(struct dummy *dum) { - u32 i; - - if (dum->gadget.speed == USB_SPEED_SUPER) { - for (i = 0; i < DUMMY_ENDPOINTS; i++) - dum->ep[i].ep.max_streams = 0x10; + if (dum->gadget.speed == USB_SPEED_SUPER) dum->ep[0].ep.maxpacket = 9; - } else { - for (i = 0; i < DUMMY_ENDPOINTS; i++) - dum->ep[i].ep.max_streams = 0; + else dum->ep[0].ep.maxpacket = 64; - } } static int dummy_pullup (struct usb_gadget *_gadget, int value) @@ -954,6 +947,7 @@ static void init_dummy_udc_hw(struct dummy *dum) ep->halted = ep->wedged = ep->already_seen = ep->setup_stage = 0; ep->ep.maxpacket = ~0; + ep->ep.max_streams = 16; ep->last_io = jiffies; ep->gadget = &dum->gadget; ep->desc = NULL; -- cgit v0.10.2 From a04ce20d9f5e9484ed3879e1290bd05933cbbe2a Mon Sep 17 00:00:00 2001 From: Sebastian Andrzej Siewior Date: Mon, 9 Jan 2012 13:14:57 +0100 Subject: usb: gadget: dummy_hcd: move the transfer part into its own function This patch moves the part of the code which does the bare transfer into its function. It is a preparion for the implementation of sg support. Signed-off-by: Sebastian Andrzej Siewior Signed-off-by: Felipe Balbi diff --git a/drivers/usb/gadget/dummy_hcd.c b/drivers/usb/gadget/dummy_hcd.c index 5a21bf5c..61e893a 100644 --- a/drivers/usb/gadget/dummy_hcd.c +++ b/drivers/usb/gadget/dummy_hcd.c @@ -1133,6 +1133,23 @@ static int dummy_urb_dequeue(struct usb_hcd *hcd, struct urb *urb, int status) return rc; } +static int dummy_perform_transfer(struct urb *urb, struct dummy_request *req, + u32 len) +{ + void *ubuf, *rbuf; + int to_host; + + to_host = usb_pipein(urb->pipe); + rbuf = req->req.buf + req->req.actual; + ubuf = urb->transfer_buffer + urb->actual_length; + + if (to_host) + memcpy(ubuf, rbuf, len); + else + memcpy(rbuf, ubuf, len); + return len; +} + /* transfer up to a frame's worth; caller must own lock */ static int transfer(struct dummy *dum, struct urb *urb, struct dummy_ep *ep, int limit, @@ -1164,8 +1181,6 @@ top: if (unlikely (len == 0)) is_short = 1; else { - char *ubuf, *rbuf; - /* not enough bandwidth left? */ if (limit < ep->ep.maxpacket && limit < len) break; @@ -1180,13 +1195,8 @@ top: } is_short = (len % ep->ep.maxpacket) != 0; - /* else transfer packet(s) */ - ubuf = urb->transfer_buffer + urb->actual_length; - rbuf = req->req.buf + req->req.actual; - if (to_host) - memcpy (ubuf, rbuf, len); - else - memcpy (rbuf, ubuf, len); + len = dummy_perform_transfer(urb, req, len); + ep->last_io = jiffies; limit -= len; -- cgit v0.10.2 From 14fce33a960afbcf91ef97135903092c33f6d076 Mon Sep 17 00:00:00 2001 From: Sebastian Andrzej Siewior Date: Mon, 9 Jan 2012 19:30:50 +0100 Subject: usb: gadget: dummy_hcd: add sg support This patch adds sg support to dummy_hcd. It seems that uas is not able to work with a hcd which does not support sg only based transfers. Signed-off-by: Sebastian Andrzej Siewior Acked-by: Alan Stern Signed-off-by: Felipe Balbi diff --git a/drivers/usb/gadget/dummy_hcd.c b/drivers/usb/gadget/dummy_hcd.c index 61e893a..4973778 100644 --- a/drivers/usb/gadget/dummy_hcd.c +++ b/drivers/usb/gadget/dummy_hcd.c @@ -39,6 +39,7 @@ #include #include #include +#include #include #include @@ -147,6 +148,8 @@ static const char *const ep_name [] = { struct urbp { struct urb *urb; struct list_head urbp_list; + struct sg_mapping_iter miter; + u32 miter_started; }; @@ -1077,13 +1080,11 @@ static int dummy_urb_enqueue ( unsigned long flags; int rc; - if (!urb->transfer_buffer && urb->transfer_buffer_length) - return -EINVAL; - urbp = kmalloc (sizeof *urbp, mem_flags); if (!urbp) return -ENOMEM; urbp->urb = urb; + urbp->miter_started = 0; dum_hcd = hcd_to_dummy_hcd(hcd); spin_lock_irqsave(&dum_hcd->dum->lock, flags); @@ -1137,17 +1138,66 @@ static int dummy_perform_transfer(struct urb *urb, struct dummy_request *req, u32 len) { void *ubuf, *rbuf; + struct urbp *urbp = urb->hcpriv; int to_host; + struct sg_mapping_iter *miter = &urbp->miter; + u32 trans = 0; + u32 this_sg; + bool next_sg; to_host = usb_pipein(urb->pipe); rbuf = req->req.buf + req->req.actual; - ubuf = urb->transfer_buffer + urb->actual_length; - if (to_host) - memcpy(ubuf, rbuf, len); - else - memcpy(rbuf, ubuf, len); - return len; + if (!urb->num_sgs) { + ubuf = urb->transfer_buffer + urb->actual_length; + if (to_host) + memcpy(ubuf, rbuf, len); + else + memcpy(rbuf, ubuf, len); + return len; + } + + if (!urbp->miter_started) { + u32 flags = SG_MITER_ATOMIC; + + if (to_host) + flags |= SG_MITER_TO_SG; + else + flags |= SG_MITER_FROM_SG; + + sg_miter_start(miter, urb->sg, urb->num_sgs, flags); + urbp->miter_started = 1; + } + next_sg = sg_miter_next(miter); + if (next_sg == false) { + WARN_ON_ONCE(1); + return -EINVAL; + } + do { + ubuf = miter->addr; + this_sg = min_t(u32, len, miter->length); + miter->consumed = this_sg; + trans += this_sg; + + if (to_host) + memcpy(ubuf, rbuf, this_sg); + else + memcpy(rbuf, ubuf, this_sg); + len -= this_sg; + + if (!len) + break; + next_sg = sg_miter_next(miter); + if (next_sg == false) { + WARN_ON_ONCE(1); + return -EINVAL; + } + + rbuf += this_sg; + } while (1); + + sg_miter_stop(miter); + return trans; } /* transfer up to a frame's worth; caller must own lock */ @@ -1198,10 +1248,13 @@ top: len = dummy_perform_transfer(urb, req, len); ep->last_io = jiffies; - - limit -= len; - urb->actual_length += len; - req->req.actual += len; + if (len < 0) { + req->req.status = len; + } else { + limit -= len; + urb->actual_length += len; + req->req.actual += len; + } } /* short packets terminate, maybe with overflow/underflow. @@ -2212,6 +2265,7 @@ static int dummy_h_get_frame (struct usb_hcd *hcd) static int dummy_setup(struct usb_hcd *hcd) { + hcd->self.sg_tablesize = ~0; if (usb_hcd_is_primary_hcd(hcd)) { the_controller.hs_hcd = hcd_to_dummy_hcd(hcd); the_controller.hs_hcd->dum = &the_controller; -- cgit v0.10.2 From d262127c330b852ce4b210a0b1b06e69d4d87704 Mon Sep 17 00:00:00 2001 From: Sebastian Andrzej Siewior Date: Mon, 9 Jan 2012 13:14:59 +0100 Subject: usb: gadget: dummy_hcd: rename dummy_udc_udpate_ep0() to dummy_udc_update_ep0() This renames a function so "update" is spelled properly. Signed-off-by: Sebastian Andrzej Siewior Signed-off-by: Felipe Balbi diff --git a/drivers/usb/gadget/dummy_hcd.c b/drivers/usb/gadget/dummy_hcd.c index 4973778..a40dc6a 100644 --- a/drivers/usb/gadget/dummy_hcd.c +++ b/drivers/usb/gadget/dummy_hcd.c @@ -801,7 +801,7 @@ static int dummy_set_selfpowered (struct usb_gadget *_gadget, int value) return 0; } -static void dummy_udc_udpate_ep0(struct dummy *dum) +static void dummy_udc_update_ep0(struct dummy *dum) { if (dum->gadget.speed == USB_SPEED_SUPER) dum->ep[0].ep.maxpacket = 9; @@ -825,7 +825,7 @@ static int dummy_pullup (struct usb_gadget *_gadget, int value) dum->driver->max_speed); else dum->gadget.speed = USB_SPEED_FULL; - dummy_udc_udpate_ep0(dum); + dummy_udc_update_ep0(dum); if (dum->gadget.speed < dum->driver->max_speed) dev_dbg(udc_dev(dum), "This device can perform faster" -- cgit v0.10.2 From d81f3e4f5792acab5929ef99aad6ca5e21a31a0e Mon Sep 17 00:00:00 2001 From: Sebastian Andrzej Siewior Date: Mon, 9 Jan 2012 13:15:00 +0100 Subject: usb: gadget: dummy_hcd: make alloc/free streams static There is no reason why dummy_alloc_streams() and dummy_free_streams() are global. Make them static instead. Signed-off-by: Sebastian Andrzej Siewior Signed-off-by: Felipe Balbi diff --git a/drivers/usb/gadget/dummy_hcd.c b/drivers/usb/gadget/dummy_hcd.c index a40dc6a..6264732 100644 --- a/drivers/usb/gadget/dummy_hcd.c +++ b/drivers/usb/gadget/dummy_hcd.c @@ -2286,7 +2286,7 @@ static int dummy_setup(struct usb_hcd *hcd) } /* Change a group of bulk endpoints to support multiple stream IDs */ -int dummy_alloc_streams(struct usb_hcd *hcd, struct usb_device *udev, +static int dummy_alloc_streams(struct usb_hcd *hcd, struct usb_device *udev, struct usb_host_endpoint **eps, unsigned int num_eps, unsigned int num_streams, gfp_t mem_flags) { @@ -2298,7 +2298,7 @@ int dummy_alloc_streams(struct usb_hcd *hcd, struct usb_device *udev, } /* Reverts a group of bulk endpoints back to not using stream IDs. */ -int dummy_free_streams(struct usb_hcd *hcd, struct usb_device *udev, +static int dummy_free_streams(struct usb_hcd *hcd, struct usb_device *udev, struct usb_host_endpoint **eps, unsigned int num_eps, gfp_t mem_flags) { -- cgit v0.10.2 From 10800f2ca1a78e24cf92dc5e16a68a9b78f91bbe Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Wed, 4 Jan 2012 10:20:09 +0300 Subject: usb: gadget: mv_udc: remove unneeded NULL check We've dereferenced req already, and we checked for bogus parameters at the start of the function. Signed-off-by: Dan Carpenter Signed-off-by: Felipe Balbi diff --git a/drivers/usb/gadget/mv_udc_core.c b/drivers/usb/gadget/mv_udc_core.c index f97e737..8dd398b 100644 --- a/drivers/usb/gadget/mv_udc_core.c +++ b/drivers/usb/gadget/mv_udc_core.c @@ -771,8 +771,7 @@ mv_ep_queue(struct usb_ep *_ep, struct usb_request *_req, gfp_t gfp_flags) udc->ep0_state = DATA_STATE_XMIT; /* irq handler advances the queue */ - if (req != NULL) - list_add_tail(&req->queue, &ep->queue); + list_add_tail(&req->queue, &ep->queue); spin_unlock_irqrestore(&udc->lock, flags); return 0; -- cgit v0.10.2 From 53069af3fa8ba2849cd4785160690873995d4f39 Mon Sep 17 00:00:00 2001 From: "Shimoda, Yoshihiro" Date: Thu, 5 Jan 2012 15:37:18 +0900 Subject: usb: renesas_usbhs: add IRQ resource decoding for IRQF_SHARED In case of the SH7757, the irq number of USB module and SUDMAC are the same. So, we have to set the IRQF_SHARED in such a case. Signed-off-by: Yoshihiro Shimoda Signed-off-by: Felipe Balbi diff --git a/drivers/usb/renesas_usbhs/common.c b/drivers/usb/renesas_usbhs/common.c index e9a5b1d..a165490 100644 --- a/drivers/usb/renesas_usbhs/common.c +++ b/drivers/usb/renesas_usbhs/common.c @@ -413,8 +413,7 @@ static int usbhs_probe(struct platform_device *pdev) struct renesas_usbhs_platform_info *info = pdev->dev.platform_data; struct renesas_usbhs_driver_callback *dfunc; struct usbhs_priv *priv; - struct resource *res; - unsigned int irq; + struct resource *res, *irq_res; int ret; /* check platform information */ @@ -426,8 +425,8 @@ static int usbhs_probe(struct platform_device *pdev) /* platform data */ res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - irq = platform_get_irq(pdev, 0); - if (!res || (int)irq <= 0) { + irq_res = platform_get_resource(pdev, IORESOURCE_IRQ, 0); + if (!res || !irq_res) { dev_err(&pdev->dev, "Not enough Renesas USB platform resources.\n"); return -ENODEV; } @@ -476,7 +475,9 @@ static int usbhs_probe(struct platform_device *pdev) /* * priv settings */ - priv->irq = irq; + priv->irq = irq_res->start; + if (irq_res->flags & IORESOURCE_IRQ_SHAREABLE) + priv->irqflags = IRQF_SHARED; priv->pdev = pdev; INIT_DELAYED_WORK(&priv->notify_hotplug_work, usbhsc_notify_hotplug); spin_lock_init(usbhs_priv_to_lock(priv)); diff --git a/drivers/usb/renesas_usbhs/common.h b/drivers/usb/renesas_usbhs/common.h index d79b3e2..3f3ccd3 100644 --- a/drivers/usb/renesas_usbhs/common.h +++ b/drivers/usb/renesas_usbhs/common.h @@ -242,6 +242,7 @@ struct usbhs_priv { void __iomem *base; unsigned int irq; + unsigned long irqflags; struct renesas_usbhs_platform_callback pfunc; struct renesas_usbhs_driver_param dparam; diff --git a/drivers/usb/renesas_usbhs/mod.c b/drivers/usb/renesas_usbhs/mod.c index 1b97fb1..0871e81 100644 --- a/drivers/usb/renesas_usbhs/mod.c +++ b/drivers/usb/renesas_usbhs/mod.c @@ -152,7 +152,7 @@ int usbhs_mod_probe(struct usbhs_priv *priv) /* irq settings */ ret = request_irq(priv->irq, usbhs_interrupt, - 0, dev_name(dev), priv); + priv->irqflags, dev_name(dev), priv); if (ret) { dev_err(dev, "irq request err\n"); goto mod_init_gadget_err; -- cgit v0.10.2 From 5ea4399457f730b4614d0632ceefd3e8f53fb1e6 Mon Sep 17 00:00:00 2001 From: "Shimoda, Yoshihiro" Date: Thu, 5 Jan 2012 15:37:22 +0900 Subject: usb: renesas_usbhs: add support for SUDMAC The SUDMAC uses 8-bit width only. So, when the driver uses SUDMAC, we have to clear the MBW_32. Signed-off-by: Yoshihiro Shimoda Signed-off-by: Felipe Balbi diff --git a/drivers/usb/renesas_usbhs/fifo.c b/drivers/usb/renesas_usbhs/fifo.c index 72339bd..03a9cc5 100644 --- a/drivers/usb/renesas_usbhs/fifo.c +++ b/drivers/usb/renesas_usbhs/fifo.c @@ -23,6 +23,7 @@ #define usbhsf_get_cfifo(p) (&((p)->fifo_info.cfifo)) #define usbhsf_get_d0fifo(p) (&((p)->fifo_info.d0fifo)) #define usbhsf_get_d1fifo(p) (&((p)->fifo_info.d1fifo)) +#define usbhsf_is_cfifo(p, f) (usbhsf_get_cfifo(p) == f) #define usbhsf_fifo_is_busy(f) ((f)->pipe) /* see usbhs_pipe_select_fifo */ @@ -305,7 +306,10 @@ static int usbhsf_fifo_select(struct usbhs_pipe *pipe, } /* "base" will be used below */ - usbhs_write(priv, fifo->sel, base | MBW_32); + if (usbhs_get_dparam(priv, has_sudmac) && !usbhsf_is_cfifo(priv, fifo)) + usbhs_write(priv, fifo->sel, base); + else + usbhs_write(priv, fifo->sel, base | MBW_32); /* check ISEL and CURPIPE value */ while (timeout--) { diff --git a/include/linux/usb/renesas_usbhs.h b/include/linux/usb/renesas_usbhs.h index 0d3f988..547e59c 100644 --- a/include/linux/usb/renesas_usbhs.h +++ b/include/linux/usb/renesas_usbhs.h @@ -149,6 +149,7 @@ struct renesas_usbhs_driver_param { * option: */ u32 has_otg:1; /* for controlling PWEN/EXTLP */ + u32 has_sudmac:1; /* for SUDMAC */ }; /* -- cgit v0.10.2 From 2d4172c938745cfa51c4a91406efb584549c1024 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Heiko=20St=C3=BCbner?= Date: Sun, 8 Jan 2012 21:57:55 +0100 Subject: usb: s3c-hsudc: Use helper functions instead of generic container_of The helper functions were definied but never used until now. Signed-off-by: Heiko Stuebner Signed-off-by: Felipe Balbi diff --git a/drivers/usb/gadget/s3c-hsudc.c b/drivers/usb/gadget/s3c-hsudc.c index df8661d..41b3a58 100644 --- a/drivers/usb/gadget/s3c-hsudc.c +++ b/drivers/usb/gadget/s3c-hsudc.c @@ -759,7 +759,7 @@ static int s3c_hsudc_ep_enable(struct usb_ep *_ep, unsigned long flags; u32 ecr = 0; - hsep = container_of(_ep, struct s3c_hsudc_ep, ep); + hsep = our_ep(_ep); if (!_ep || !desc || hsep->desc || _ep->name == ep0name || desc->bDescriptorType != USB_DT_ENDPOINT || hsep->bEndpointAddress != desc->bEndpointAddress @@ -853,7 +853,7 @@ static void s3c_hsudc_free_request(struct usb_ep *ep, struct usb_request *_req) { struct s3c_hsudc_req *hsreq; - hsreq = container_of(_req, struct s3c_hsudc_req, req); + hsreq = our_req(_req); WARN_ON(!list_empty(&hsreq->queue)); kfree(hsreq); } @@ -876,12 +876,12 @@ static int s3c_hsudc_queue(struct usb_ep *_ep, struct usb_request *_req, u32 offset; u32 csr; - hsreq = container_of(_req, struct s3c_hsudc_req, req); + hsreq = our_req(_req); if ((!_req || !_req->complete || !_req->buf || !list_empty(&hsreq->queue))) return -EINVAL; - hsep = container_of(_ep, struct s3c_hsudc_ep, ep); + hsep = our_ep(_ep); hsudc = hsep->dev; if (!hsudc->driver || hsudc->gadget.speed == USB_SPEED_UNKNOWN) return -ESHUTDOWN; @@ -935,7 +935,7 @@ static int s3c_hsudc_dequeue(struct usb_ep *_ep, struct usb_request *_req) struct s3c_hsudc_req *hsreq; unsigned long flags; - hsep = container_of(_ep, struct s3c_hsudc_ep, ep); + hsep = our_ep(_ep); if (!_ep || hsep->ep.name == ep0name) return -EINVAL; -- cgit v0.10.2 From 294f78ec493e69b55bcba566d4fe264d041aba8d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Heiko=20St=C3=BCbner?= Date: Sun, 8 Jan 2012 21:58:28 +0100 Subject: usb: s3c-hsudc: add basic runtime_pm calls This will enable the system to check for activity of the usb gadget and also in a later patch to control the usbphy power-domain. When handling the power domain there, it will be possible to remove another reference to architecture code. Signed-off-by: Heiko Stuebner Signed-off-by: Felipe Balbi diff --git a/drivers/usb/gadget/s3c-hsudc.c b/drivers/usb/gadget/s3c-hsudc.c index 41b3a58..5e87293 100644 --- a/drivers/usb/gadget/s3c-hsudc.c +++ b/drivers/usb/gadget/s3c-hsudc.c @@ -30,6 +30,7 @@ #include #include #include +#include #include @@ -1178,6 +1179,9 @@ static int s3c_hsudc_start(struct usb_gadget *gadget, dev_info(hsudc->dev, "bound driver %s\n", driver->driver.name); s3c_hsudc_reconfig(hsudc); + + pm_runtime_get_sync(hsudc->dev); + s3c_hsudc_init_phy(); if (hsudc->pd->gpio_init) hsudc->pd->gpio_init(); @@ -1208,6 +1212,9 @@ static int s3c_hsudc_stop(struct usb_gadget *gadget, hsudc->gadget.dev.driver = NULL; hsudc->gadget.speed = USB_SPEED_UNKNOWN; s3c_hsudc_uninit_phy(); + + pm_runtime_put(hsudc->dev); + if (hsudc->pd->gpio_uninit) hsudc->pd->gpio_uninit(); s3c_hsudc_stop_activity(hsudc); @@ -1362,6 +1369,8 @@ static int __devinit s3c_hsudc_probe(struct platform_device *pdev) if (ret) goto err_add_udc; + pm_runtime_enable(dev); + return 0; err_add_udc: device_unregister(&hsudc->gadget.dev); -- cgit v0.10.2 From c50a3bff0edb0acd49d8033a12ea4668e09a31ad Mon Sep 17 00:00:00 2001 From: Tomoya MORINAGA Date: Thu, 12 Jan 2012 11:27:05 +0900 Subject: usb: gadget: pch_udc: Fix disconnect issue ISSUE: When the driver notifies a gadget of a disconnect event, a system rarely freezes. CAUSE: When the driver calls dev->driver->disconnect(), it is not calling spin_unlock(). Signed-off-by: Tomoya MORINAGA Signed-off-by: Felipe Balbi diff --git a/drivers/usb/gadget/pch_udc.c b/drivers/usb/gadget/pch_udc.c index a3fcaae..6a44c92 100644 --- a/drivers/usb/gadget/pch_udc.c +++ b/drivers/usb/gadget/pch_udc.c @@ -2335,8 +2335,11 @@ static void pch_udc_svc_ur_interrupt(struct pch_udc_dev *dev) /* Complete request queue */ empty_req_queue(ep); } - if (dev->driver && dev->driver->disconnect) + if (dev->driver && dev->driver->disconnect) { + spin_unlock(&dev->lock); dev->driver->disconnect(&dev->gadget); + spin_lock(&dev->lock); + } } /** -- cgit v0.10.2 From c802672cd36cd063bfd54d54c8c34825ab5b2357 Mon Sep 17 00:00:00 2001 From: Tomoya MORINAGA Date: Thu, 12 Jan 2012 11:27:06 +0900 Subject: usb: gadget: pch_udc: Fix wrong return value ISSUE: If the return value of pch_udc_pcd_init() is False, the return value of this function is unsettled. Since pch_udc_pcd_init() always returns 0, there is not actually the issue. CAUSE: If pch_udc_pcd_init() is True, the variable, retval, is not set for an appropriate value. Signed-off-by: Tomoya MORINAGA Signed-off-by: Felipe Balbi diff --git a/drivers/usb/gadget/pch_udc.c b/drivers/usb/gadget/pch_udc.c index 6a44c92..96606b6 100644 --- a/drivers/usb/gadget/pch_udc.c +++ b/drivers/usb/gadget/pch_udc.c @@ -2915,8 +2915,10 @@ static int pch_udc_probe(struct pci_dev *pdev, } pch_udc = dev; /* initialize the hardware */ - if (pch_udc_pcd_init(dev)) + if (pch_udc_pcd_init(dev)) { + retval = -ENODEV; goto finished; + } if (request_irq(pdev->irq, pch_udc_isr, IRQF_SHARED, KBUILD_MODNAME, dev)) { dev_err(&pdev->dev, "%s: request_irq(%d) fail\n", __func__, -- cgit v0.10.2 From 84566abba058b2aae8d603dfa90b5a3778a6714f Mon Sep 17 00:00:00 2001 From: Tomoya MORINAGA Date: Thu, 12 Jan 2012 11:27:07 +0900 Subject: usb: gadget: pch_udc: Fix USB suspend issue ISSUE: After USB Suspend, a system rarely freezes. CAUSE: When USB Suspend occurred, the driver is not notifying a gadget of the event. Signed-off-by: Tomoya MORINAGA Signed-off-by: Felipe Balbi diff --git a/drivers/usb/gadget/pch_udc.c b/drivers/usb/gadget/pch_udc.c index 96606b6..842ca63 100644 --- a/drivers/usb/gadget/pch_udc.c +++ b/drivers/usb/gadget/pch_udc.c @@ -2475,8 +2475,15 @@ static void pch_udc_dev_isr(struct pch_udc_dev *dev, u32 dev_intr) if (dev_intr & UDC_DEVINT_SC) pch_udc_svc_cfg_interrupt(dev); /* USB Suspend interrupt */ - if (dev_intr & UDC_DEVINT_US) + if (dev_intr & UDC_DEVINT_US) { + if (dev->driver + && dev->driver->suspend) { + spin_unlock(&dev->lock); + dev->driver->suspend(&dev->gadget); + spin_lock(&dev->lock); + } dev_dbg(&dev->pdev->dev, "USB_SUSPEND\n"); + } /* Clear the SOF interrupt, if enabled */ if (dev_intr & UDC_DEVINT_SOF) dev_dbg(&dev->pdev->dev, "SOF\n"); -- cgit v0.10.2 From 1c575d2d2e3ff2a7cb3c2e2165064199cfd8ad32 Mon Sep 17 00:00:00 2001 From: Tomoya MORINAGA Date: Thu, 12 Jan 2012 11:27:08 +0900 Subject: usb: gadget: pch_udc: Fix usb/gadget/pch_udc: Fix ether gadget connect/disconnect issue ISSUE: After a USB cable is connect/disconnected, the system rarely freezes. CAUSE: Since the USB device controller cannot know to disconnect the USB cable, when it is used without detecting VBUS by GPIO, the UDC driver does not notify to USB Gadget. Since USB Gadget cannot know to disconnect, a false setting occurred when the USB cable is connected/disconnect repeatedly. Signed-off-by: Tomoya MORINAGA Signed-off-by: Felipe Balbi diff --git a/drivers/usb/gadget/pch_udc.c b/drivers/usb/gadget/pch_udc.c index 842ca63..c2f1975 100644 --- a/drivers/usb/gadget/pch_udc.c +++ b/drivers/usb/gadget/pch_udc.c @@ -311,6 +311,7 @@ struct pch_udc_ep { * @registered: driver regsitered with system * @suspended: driver in suspended state * @connected: gadget driver associated + * @vbus_session: required vbus_session state * @set_cfg_not_acked: pending acknowledgement 4 setup * @waiting_zlp_ack: pending acknowledgement 4 ZLP * @data_requests: DMA pool for data requests @@ -337,6 +338,7 @@ struct pch_udc_dev { registered:1, suspended:1, connected:1, + vbus_session:1, set_cfg_not_acked:1, waiting_zlp_ack:1; struct pci_pool *data_requests; @@ -554,6 +556,31 @@ static void pch_udc_clear_disconnect(struct pch_udc_dev *dev) } /** + * pch_udc_reconnect() - This API initializes usb device controller, + * and clear the disconnect status. + * @dev: Reference to pch_udc_regs structure + */ +static void pch_udc_init(struct pch_udc_dev *dev); +static void pch_udc_reconnect(struct pch_udc_dev *dev) +{ + pch_udc_init(dev); + + /* enable device interrupts */ + /* pch_udc_enable_interrupts() */ + pch_udc_bit_clr(dev, UDC_DEVIRQMSK_ADDR, + UDC_DEVINT_UR | UDC_DEVINT_US | + UDC_DEVINT_ENUM | + UDC_DEVINT_SI | UDC_DEVINT_SC); + + /* Clear the disconnect */ + pch_udc_bit_set(dev, UDC_DEVCTL_ADDR, UDC_DEVCTL_RES); + pch_udc_bit_clr(dev, UDC_DEVCTL_ADDR, UDC_DEVCTL_SD); + mdelay(1); + /* Resume USB signalling */ + pch_udc_bit_clr(dev, UDC_DEVCTL_ADDR, UDC_DEVCTL_RES); +} + +/** * pch_udc_vbus_session() - set or clearr the disconnect status. * @dev: Reference to pch_udc_regs structure * @is_active: Parameter specifying the action @@ -563,10 +590,18 @@ static void pch_udc_clear_disconnect(struct pch_udc_dev *dev) static inline void pch_udc_vbus_session(struct pch_udc_dev *dev, int is_active) { - if (is_active) - pch_udc_clear_disconnect(dev); - else + if (is_active) { + pch_udc_reconnect(dev); + dev->vbus_session = 1; + } else { + if (dev->driver && dev->driver->disconnect) { + spin_unlock(&dev->lock); + dev->driver->disconnect(&dev->gadget); + spin_lock(&dev->lock); + } pch_udc_set_disconnect(dev); + dev->vbus_session = 0; + } } /** @@ -1126,7 +1161,17 @@ static int pch_udc_pcd_pullup(struct usb_gadget *gadget, int is_on) if (!gadget) return -EINVAL; dev = container_of(gadget, struct pch_udc_dev, gadget); - pch_udc_vbus_session(dev, is_on); + if (is_on) { + pch_udc_reconnect(dev); + } else { + if (dev->driver && dev->driver->disconnect) { + spin_unlock(&dev->lock); + dev->driver->disconnect(&dev->gadget); + spin_lock(&dev->lock); + } + pch_udc_set_disconnect(dev); + } + return 0; } @@ -2482,6 +2527,15 @@ static void pch_udc_dev_isr(struct pch_udc_dev *dev, u32 dev_intr) dev->driver->suspend(&dev->gadget); spin_lock(&dev->lock); } + + if (dev->vbus_session == 0) { + if (dev->driver && dev->driver->disconnect) { + spin_unlock(&dev->lock); + dev->driver->disconnect(&dev->gadget); + spin_lock(&dev->lock); + } + pch_udc_reconnect(dev); + } dev_dbg(&dev->pdev->dev, "USB_SUSPEND\n"); } /* Clear the SOF interrupt, if enabled */ @@ -2509,6 +2563,14 @@ static irqreturn_t pch_udc_isr(int irq, void *pdev) dev_intr = pch_udc_read_device_interrupts(dev); ep_intr = pch_udc_read_ep_interrupts(dev); + /* For a hot plug, this find that the controller is hung up. */ + if (dev_intr == ep_intr) + if (dev_intr == pch_udc_readl(dev, UDC_DEVCFG_ADDR)) { + dev_dbg(&dev->pdev->dev, "UDC: Hung up\n"); + /* The controller is reset */ + pch_udc_writel(dev, UDC_SRST, UDC_SRST_ADDR); + return IRQ_HANDLED; + } if (dev_intr) /* Clear device interrupts */ pch_udc_write_device_interrupts(dev, dev_intr); -- cgit v0.10.2 From 833310402c54ad9b676b465fc53ad276b13d36be Mon Sep 17 00:00:00 2001 From: Tomoya MORINAGA Date: Thu, 12 Jan 2012 11:27:09 +0900 Subject: usb: gadget: pch_udc: Reduce redundant interrupt ISSUE: USB Suspend interrupts occur frequently. CAUSE: When it is called pch_udc_reconnect() in USB Suspend, it repeats reset and Suspend. SOLUTION: pch_udc_reconnect() does not enable all interrupts. When an enumeration event occurred the driver enables all interrupts. Signed-off-by: Tomoya MORINAGA Signed-off-by: Felipe Balbi diff --git a/drivers/usb/gadget/pch_udc.c b/drivers/usb/gadget/pch_udc.c index c2f1975..4da9814 100644 --- a/drivers/usb/gadget/pch_udc.c +++ b/drivers/usb/gadget/pch_udc.c @@ -568,9 +568,7 @@ static void pch_udc_reconnect(struct pch_udc_dev *dev) /* enable device interrupts */ /* pch_udc_enable_interrupts() */ pch_udc_bit_clr(dev, UDC_DEVIRQMSK_ADDR, - UDC_DEVINT_UR | UDC_DEVINT_US | - UDC_DEVINT_ENUM | - UDC_DEVINT_SI | UDC_DEVINT_SC); + UDC_DEVINT_UR | UDC_DEVINT_ENUM); /* Clear the disconnect */ pch_udc_bit_set(dev, UDC_DEVCTL_ADDR, UDC_DEVCTL_RES); @@ -2419,6 +2417,11 @@ static void pch_udc_svc_enum_interrupt(struct pch_udc_dev *dev) pch_udc_set_dma(dev, DMA_DIR_TX); pch_udc_set_dma(dev, DMA_DIR_RX); pch_udc_ep_set_rrdy(&(dev->ep[UDC_EP0OUT_IDX])); + + /* enable device interrupts */ + pch_udc_enable_interrupts(dev, UDC_DEVINT_UR | UDC_DEVINT_US | + UDC_DEVINT_ES | UDC_DEVINT_ENUM | + UDC_DEVINT_SI | UDC_DEVINT_SC); } /** -- cgit v0.10.2 From 9645f7d3dab346a9d442dca1688740551b8c55f2 Mon Sep 17 00:00:00 2001 From: Tomoya MORINAGA Date: Thu, 12 Jan 2012 11:27:10 +0900 Subject: usb: gadget: pch_udc: Add debug message ISSUE: Adding debugging messages. CAUSE: The debugging messages are added to make sure of that major interrupt events are occurring. Signed-off-by: Tomoya MORINAGA Signed-off-by: Felipe Balbi diff --git a/drivers/usb/gadget/pch_udc.c b/drivers/usb/gadget/pch_udc.c index 4da9814..82416aa 100644 --- a/drivers/usb/gadget/pch_udc.c +++ b/drivers/usb/gadget/pch_udc.c @@ -2511,11 +2511,15 @@ static void pch_udc_svc_cfg_interrupt(struct pch_udc_dev *dev) static void pch_udc_dev_isr(struct pch_udc_dev *dev, u32 dev_intr) { /* USB Reset Interrupt */ - if (dev_intr & UDC_DEVINT_UR) + if (dev_intr & UDC_DEVINT_UR) { pch_udc_svc_ur_interrupt(dev); + dev_dbg(&dev->pdev->dev, "USB_RESET\n"); + } /* Enumeration Done Interrupt */ - if (dev_intr & UDC_DEVINT_ENUM) + if (dev_intr & UDC_DEVINT_ENUM) { pch_udc_svc_enum_interrupt(dev); + dev_dbg(&dev->pdev->dev, "USB_ENUM\n"); + } /* Set Interface Interrupt */ if (dev_intr & UDC_DEVINT_SI) pch_udc_svc_intf_interrupt(dev); -- cgit v0.10.2 From a54c979fed31b4230b2e63132f7167206e4e5d69 Mon Sep 17 00:00:00 2001 From: Sebastian Andrzej Siewior Date: Fri, 13 Jan 2012 21:51:47 +0100 Subject: usb: gadget: dummy_hcd: complete stream support dummy_hcd provides (alloc|free)_stream() callbacks but there are not doing anything. The transfer side also lacks matching of streams. This patch changes this and implements stream allocation / de-allocation support and proper urb <=> req matching. The UDC side exposes a limit of 16 streams. DWC3, the only USB3 UDC has no limitations in this regard except that it _needs_ to know that streams will be used at the ep_enable time. At the host side, there is no real limit either: XHCI can allocate any number of streams as long as it does not run out of memory. The UAS gadget currently requests 16 streams and the UAS host side fallbacks from the requested 256 down to 16 which is fine. From the UASP point of view (the only specified user), the number of used streams does not really matter. The only limitation is that the host may not use a higher stream than the gadget requested and can deal with. The dummy stream support has been modelled after current UAS + XHCI + DWC3 + UASP usage which helps me testing: - the device announces that each ep supports 16 streams (even it could more than that). - the device side looks into Companion descriptor at ep_enable time and enables them according to it. - the host side tries to enable the requested number of streams but the upper limit is the Comanion descriptor. None (zero streams) is an error condition, less is okay. Signed-off-by: Sebastian Andrzej Siewior Signed-off-by: Felipe Balbi diff --git a/drivers/usb/gadget/dummy_hcd.c b/drivers/usb/gadget/dummy_hcd.c index 6264732..e57989c 100644 --- a/drivers/usb/gadget/dummy_hcd.c +++ b/drivers/usb/gadget/dummy_hcd.c @@ -88,6 +88,7 @@ struct dummy_ep { unsigned wedged : 1; unsigned already_seen : 1; unsigned setup_stage : 1; + unsigned stream_en:1; }; struct dummy_request { @@ -169,6 +170,8 @@ struct dummy_hcd { struct usb_device *udev; struct list_head urbp_list; + u32 stream_en_ep; + u8 num_stream[30 / 2]; unsigned active:1; unsigned old_active:1; @@ -514,8 +517,16 @@ dummy_enable (struct usb_ep *_ep, const struct usb_endpoint_descriptor *desc) _ep->maxpacket = max; ep->desc = desc; + if (usb_ss_max_streams(_ep->comp_desc)) { + if (!usb_endpoint_xfer_bulk(desc)) { + dev_err(udc_dev(dum), "Can't enable stream support on " + "non-bulk ep %s\n", _ep->name); + return -EINVAL; + } + ep->stream_en = 1; + } - dev_dbg (udc_dev(dum), "enabled %s (ep%d%s-%s) maxpacket %d\n", + dev_dbg(udc_dev(dum), "enabled %s (ep%d%s-%s) maxpacket %d stream %s\n", _ep->name, desc->bEndpointAddress & 0x0f, (desc->bEndpointAddress & USB_DIR_IN) ? "in" : "out", @@ -534,7 +545,7 @@ dummy_enable (struct usb_ep *_ep, const struct usb_endpoint_descriptor *desc) val = "ctrl"; break; }; val; }), - max); + max, ep->stream_en ? "enabled" : "disabled"); /* at this point real hardware should be NAKing transfers * to that endpoint, until a buffer is queued to it. @@ -559,6 +570,7 @@ static int dummy_disable (struct usb_ep *_ep) spin_lock_irqsave (&dum->lock, flags); ep->desc = NULL; + ep->stream_en = 0; retval = 0; nuke (dum, ep); spin_unlock_irqrestore (&dum->lock, flags); @@ -1058,6 +1070,16 @@ static struct platform_driver dummy_udc_driver = { /*-------------------------------------------------------------------------*/ +static unsigned int dummy_get_ep_idx(const struct usb_endpoint_descriptor *desc) +{ + unsigned int index; + + index = usb_endpoint_num(desc) << 1; + if (usb_endpoint_dir_in(desc)) + index |= 1; + return index; +} + /* MASTER/HOST SIDE DRIVER * * this uses the hcd framework to hook up to host side drivers. @@ -1070,6 +1092,81 @@ static struct platform_driver dummy_udc_driver = { * usb 2.0 rules. */ +static int dummy_ep_stream_en(struct dummy_hcd *dum_hcd, struct urb *urb) +{ + const struct usb_endpoint_descriptor *desc = &urb->ep->desc; + u32 index; + + if (!usb_endpoint_xfer_bulk(desc)) + return 0; + + index = dummy_get_ep_idx(desc); + return (1 << index) & dum_hcd->stream_en_ep; +} + +/* + * The max stream number is saved as a nibble so for the 30 possible endpoints + * we only 15 bytes of memory. Therefore we are limited to max 16 streams (0 + * means we use only 1 stream). The maximum according to the spec is 16bit so + * if the 16 stream limit is about to go, the array size should be incremented + * to 30 elements of type u16. + */ +static int get_max_streams_for_pipe(struct dummy_hcd *dum_hcd, + unsigned int pipe) +{ + int max_streams; + + max_streams = dum_hcd->num_stream[usb_pipeendpoint(pipe)]; + if (usb_pipeout(pipe)) + max_streams >>= 4; + else + max_streams &= 0xf; + max_streams++; + return max_streams; +} + +static void set_max_streams_for_pipe(struct dummy_hcd *dum_hcd, + unsigned int pipe, unsigned int streams) +{ + int max_streams; + + streams--; + max_streams = dum_hcd->num_stream[usb_pipeendpoint(pipe)]; + if (usb_pipeout(pipe)) { + streams <<= 4; + max_streams &= 0xf; + } else { + max_streams &= 0xf0; + } + max_streams |= streams; + dum_hcd->num_stream[usb_pipeendpoint(pipe)] = max_streams; +} + +static int dummy_validate_stream(struct dummy_hcd *dum_hcd, struct urb *urb) +{ + unsigned int max_streams; + int enabled; + + enabled = dummy_ep_stream_en(dum_hcd, urb); + if (!urb->stream_id) { + if (enabled) + return -EINVAL; + return 0; + } + if (!enabled) + return -EINVAL; + + max_streams = get_max_streams_for_pipe(dum_hcd, + usb_pipeendpoint(urb->pipe)); + if (urb->stream_id > max_streams) { + dev_err(dummy_dev(dum_hcd), "Stream id %d is out of range.\n", + urb->stream_id); + BUG(); + return -EINVAL; + } + return 0; +} + static int dummy_urb_enqueue ( struct usb_hcd *hcd, struct urb *urb, @@ -1088,6 +1185,13 @@ static int dummy_urb_enqueue ( dum_hcd = hcd_to_dummy_hcd(hcd); spin_lock_irqsave(&dum_hcd->dum->lock, flags); + + rc = dummy_validate_stream(dum_hcd, urb); + if (rc) { + kfree(urbp); + goto done; + } + rc = usb_hcd_link_urb_to_ep(hcd, urb); if (rc) { kfree(urbp); @@ -1201,10 +1305,10 @@ static int dummy_perform_transfer(struct urb *urb, struct dummy_request *req, } /* transfer up to a frame's worth; caller must own lock */ -static int -transfer(struct dummy *dum, struct urb *urb, struct dummy_ep *ep, int limit, - int *status) +static int transfer(struct dummy_hcd *dum_hcd, struct urb *urb, + struct dummy_ep *ep, int limit, int *status) { + struct dummy *dum = dum_hcd->dum; struct dummy_request *req; top: @@ -1214,6 +1318,11 @@ top: int is_short, to_host; int rescan = 0; + if (dummy_ep_stream_en(dum_hcd, urb)) { + if ((urb->stream_id != req->req.stream_id)) + continue; + } + /* 1..N packets of ep->ep.maxpacket each ... the last one * may be short (including zero length). * @@ -1744,7 +1853,7 @@ restart: default: treat_control_like_bulk: ep->last_io = jiffies; - total = transfer(dum, urb, ep, limit, &status); + total = transfer(dum_hcd, urb, ep, limit, &status); break; } @@ -2201,6 +2310,7 @@ static int dummy_start_ss(struct dummy_hcd *dum_hcd) dum_hcd->timer.function = dummy_timer; dum_hcd->timer.data = (unsigned long)dum_hcd; dum_hcd->rh_state = DUMMY_RH_RUNNING; + dum_hcd->stream_en_ep = 0; INIT_LIST_HEAD(&dum_hcd->urbp_list); dummy_hcd_to_hcd(dum_hcd)->power_budget = POWER_BUDGET; dummy_hcd_to_hcd(dum_hcd)->state = HC_STATE_RUNNING; @@ -2290,11 +2400,46 @@ static int dummy_alloc_streams(struct usb_hcd *hcd, struct usb_device *udev, struct usb_host_endpoint **eps, unsigned int num_eps, unsigned int num_streams, gfp_t mem_flags) { - if (hcd->speed != HCD_USB3) - dev_dbg(dummy_dev(hcd_to_dummy_hcd(hcd)), - "%s() - ERROR! Not supported for USB2.0 roothub\n", - __func__); - return 0; + struct dummy_hcd *dum_hcd = hcd_to_dummy_hcd(hcd); + unsigned long flags; + int max_stream; + int ret_streams = num_streams; + unsigned int index; + unsigned int i; + + if (!num_eps) + return -EINVAL; + + spin_lock_irqsave(&dum_hcd->dum->lock, flags); + for (i = 0; i < num_eps; i++) { + index = dummy_get_ep_idx(&eps[i]->desc); + if ((1 << index) & dum_hcd->stream_en_ep) { + ret_streams = -EINVAL; + goto out; + } + max_stream = usb_ss_max_streams(&eps[i]->ss_ep_comp); + if (!max_stream) { + ret_streams = -EINVAL; + goto out; + } + if (max_stream < ret_streams) { + dev_dbg(dummy_dev(dum_hcd), "Ep 0x%x only supports %u " + "stream IDs.\n", + eps[i]->desc.bEndpointAddress, + max_stream); + ret_streams = max_stream; + } + } + + for (i = 0; i < num_eps; i++) { + index = dummy_get_ep_idx(&eps[i]->desc); + dum_hcd->stream_en_ep |= 1 << index; + set_max_streams_for_pipe(dum_hcd, + usb_endpoint_num(&eps[i]->desc), ret_streams); + } +out: + spin_unlock_irqrestore(&dum_hcd->dum->lock, flags); + return ret_streams; } /* Reverts a group of bulk endpoints back to not using stream IDs. */ @@ -2302,11 +2447,31 @@ static int dummy_free_streams(struct usb_hcd *hcd, struct usb_device *udev, struct usb_host_endpoint **eps, unsigned int num_eps, gfp_t mem_flags) { - if (hcd->speed != HCD_USB3) - dev_dbg(dummy_dev(hcd_to_dummy_hcd(hcd)), - "%s() - ERROR! Not supported for USB2.0 roothub\n", - __func__); - return 0; + struct dummy_hcd *dum_hcd = hcd_to_dummy_hcd(hcd); + unsigned long flags; + int ret; + unsigned int index; + unsigned int i; + + spin_lock_irqsave(&dum_hcd->dum->lock, flags); + for (i = 0; i < num_eps; i++) { + index = dummy_get_ep_idx(&eps[i]->desc); + if (!((1 << index) & dum_hcd->stream_en_ep)) { + ret = -EINVAL; + goto out; + } + } + + for (i = 0; i < num_eps; i++) { + index = dummy_get_ep_idx(&eps[i]->desc); + dum_hcd->stream_en_ep &= ~(1 << index); + set_max_streams_for_pipe(dum_hcd, + usb_endpoint_num(&eps[i]->desc), 0); + } + ret = 0; +out: + spin_unlock_irqrestore(&dum_hcd->dum->lock, flags); + return ret; } static struct hc_driver dummy_hcd = { -- cgit v0.10.2 From 59f08e6d2015a16fb4856f910ef0660d13a0c767 Mon Sep 17 00:00:00 2001 From: Sebastian Andrzej Siewior Date: Thu, 12 Jan 2012 12:53:14 +0100 Subject: usb: dummy_hcd: use usb_endpoint_type() This patch makes use of usb_endpoint_type() instead of the open coding. Signed-off-by: Sebastian Andrzej Siewior Signed-off-by: Felipe Balbi diff --git a/drivers/usb/gadget/dummy_hcd.c b/drivers/usb/gadget/dummy_hcd.c index e57989c..fd4d86a 100644 --- a/drivers/usb/gadget/dummy_hcd.c +++ b/drivers/usb/gadget/dummy_hcd.c @@ -447,7 +447,7 @@ dummy_enable (struct usb_ep *_ep, const struct usb_endpoint_descriptor *desc) * especially for "ep9out" style fixed function ones.) */ retval = -EINVAL; - switch (desc->bmAttributes & 0x03) { + switch (usb_endpoint_type(desc)) { case USB_ENDPOINT_XFER_BULK: if (strstr (ep->ep.name, "-iso") || strstr (ep->ep.name, "-int")) { @@ -531,7 +531,7 @@ dummy_enable (struct usb_ep *_ep, const struct usb_endpoint_descriptor *desc) desc->bEndpointAddress & 0x0f, (desc->bEndpointAddress & USB_DIR_IN) ? "in" : "out", ({ char *val; - switch (desc->bmAttributes & 0x03) { + switch (usb_endpoint_type(desc)) { case USB_ENDPOINT_XFER_BULK: val = "bulk"; break; @@ -1439,7 +1439,7 @@ static int periodic_bytes (struct dummy *dum, struct dummy_ep *ep) limit += limit * tmp; } if (dum->gadget.speed == USB_SPEED_SUPER) { - switch (ep->desc->bmAttributes & 0x03) { + switch (usb_endpoint_type(ep->desc)) { case USB_ENDPOINT_XFER_ISOC: /* Sec. 4.4.8.2 USB3.0 Spec */ limit = 3 * 16 * 1024 * 8; -- cgit v0.10.2 From 18f2cbaa2b9617eed20789ce40878920a8ea6beb Mon Sep 17 00:00:00 2001 From: Sebastian Andrzej Siewior Date: Thu, 12 Jan 2012 12:53:15 +0100 Subject: usb: gadget: dummy_hcd: clean up checkpatch This patch converts checkpatch output of |./scripts/checkpatch.pl drivers/usb/gadget/dummy_hcd.c -file from |total: 22 errors, 174 warnings, 2642 lines checked to |total: 0 errors, 0 warnings, 2632 lines checked Signed-off-by: Sebastian Andrzej Siewior Signed-off-by: Felipe Balbi diff --git a/drivers/usb/gadget/dummy_hcd.c b/drivers/usb/gadget/dummy_hcd.c index fd4d86a..67573e5 100644 --- a/drivers/usb/gadget/dummy_hcd.c +++ b/drivers/usb/gadget/dummy_hcd.c @@ -42,25 +42,24 @@ #include #include -#include +#include #include #include #include - #define DRIVER_DESC "USB Host+Gadget Emulator" #define DRIVER_VERSION "02 May 2005" #define POWER_BUDGET 500 /* in mA; use 8 for low-power port testing */ -static const char driver_name [] = "dummy_hcd"; -static const char driver_desc [] = "USB Host+Gadget Emulator"; +static const char driver_name[] = "dummy_hcd"; +static const char driver_desc[] = "USB Host+Gadget Emulator"; -static const char gadget_name [] = "dummy_udc"; +static const char gadget_name[] = "dummy_udc"; -MODULE_DESCRIPTION (DRIVER_DESC); -MODULE_AUTHOR ("David Brownell"); -MODULE_LICENSE ("GPL"); +MODULE_DESCRIPTION(DRIVER_DESC); +MODULE_AUTHOR("David Brownell"); +MODULE_LICENSE("GPL"); struct dummy_hcd_module_parameters { bool is_super_speed; @@ -84,10 +83,10 @@ struct dummy_ep { struct usb_gadget *gadget; const struct usb_endpoint_descriptor *desc; struct usb_ep ep; - unsigned halted : 1; - unsigned wedged : 1; - unsigned already_seen : 1; - unsigned setup_stage : 1; + unsigned halted:1; + unsigned wedged:1; + unsigned already_seen:1; + unsigned setup_stage:1; unsigned stream_en:1; }; @@ -96,15 +95,15 @@ struct dummy_request { struct usb_request req; }; -static inline struct dummy_ep *usb_ep_to_dummy_ep (struct usb_ep *_ep) +static inline struct dummy_ep *usb_ep_to_dummy_ep(struct usb_ep *_ep) { - return container_of (_ep, struct dummy_ep, ep); + return container_of(_ep, struct dummy_ep, ep); } static inline struct dummy_request *usb_request_to_dummy_request (struct usb_request *_req) { - return container_of (_req, struct dummy_request, req); + return container_of(_req, struct dummy_request, req); } /*-------------------------------------------------------------------------*/ @@ -123,9 +122,9 @@ static inline struct dummy_request *usb_request_to_dummy_request * configurations, illegal or unsupported packet lengths, and so on. */ -static const char ep0name [] = "ep0"; +static const char ep0name[] = "ep0"; -static const char *const ep_name [] = { +static const char *const ep_name[] = { ep0name, /* everyone has ep0 */ /* act like a net2280: high speed, six configurable endpoints */ @@ -184,12 +183,12 @@ struct dummy { /* * SLAVE/GADGET side support */ - struct dummy_ep ep [DUMMY_ENDPOINTS]; + struct dummy_ep ep[DUMMY_ENDPOINTS]; int address; struct usb_gadget gadget; struct usb_gadget_driver *driver; struct dummy_request fifo_req; - u8 fifo_buf [FIFO_SIZE]; + u8 fifo_buf[FIFO_SIZE]; u16 devstatus; unsigned udc_suspended:1; unsigned pullup:1; @@ -216,14 +215,14 @@ static inline struct device *dummy_dev(struct dummy_hcd *dum) return dummy_hcd_to_hcd(dum)->self.controller; } -static inline struct device *udc_dev (struct dummy *dum) +static inline struct device *udc_dev(struct dummy *dum) { return dum->gadget.dev.parent; } -static inline struct dummy *ep_to_dummy (struct dummy_ep *ep) +static inline struct dummy *ep_to_dummy(struct dummy_ep *ep) { - return container_of (ep->gadget, struct dummy, gadget); + return container_of(ep->gadget, struct dummy, gadget); } static inline struct dummy_hcd *gadget_to_dummy_hcd(struct usb_gadget *gadget) @@ -235,9 +234,9 @@ static inline struct dummy_hcd *gadget_to_dummy_hcd(struct usb_gadget *gadget) return dum->hs_hcd; } -static inline struct dummy *gadget_dev_to_dummy (struct device *dev) +static inline struct dummy *gadget_dev_to_dummy(struct device *dev) { - return container_of (dev, struct dummy, gadget.dev); + return container_of(dev, struct dummy, gadget.dev); } static struct dummy the_controller; @@ -247,24 +246,23 @@ static struct dummy the_controller; /* SLAVE/GADGET SIDE UTILITY ROUTINES */ /* called with spinlock held */ -static void nuke (struct dummy *dum, struct dummy_ep *ep) +static void nuke(struct dummy *dum, struct dummy_ep *ep) { - while (!list_empty (&ep->queue)) { + while (!list_empty(&ep->queue)) { struct dummy_request *req; - req = list_entry (ep->queue.next, struct dummy_request, queue); - list_del_init (&req->queue); + req = list_entry(ep->queue.next, struct dummy_request, queue); + list_del_init(&req->queue); req->req.status = -ESHUTDOWN; - spin_unlock (&dum->lock); - req->req.complete (&ep->ep, &req->req); - spin_lock (&dum->lock); + spin_unlock(&dum->lock); + req->req.complete(&ep->ep, &req->req); + spin_lock(&dum->lock); } } /* caller must hold lock */ -static void -stop_activity (struct dummy *dum) +static void stop_activity(struct dummy *dum) { struct dummy_ep *ep; @@ -274,8 +272,8 @@ stop_activity (struct dummy *dum) /* The timer is left running so that outstanding URBs can fail */ /* nuke any pending requests first, so driver i/o is quiesced */ - list_for_each_entry (ep, &dum->gadget.ep_list, ep.ep_list) - nuke (dum, ep); + list_for_each_entry(ep, &dum->gadget.ep_list, ep.ep_list) + nuke(dum, ep); /* driver now does any non-usb quiescing necessary */ } @@ -410,8 +408,8 @@ static void set_link_state(struct dummy_hcd *dum_hcd) #define is_enabled(dum) \ (dum->port_status & USB_PORT_STAT_ENABLE) -static int -dummy_enable (struct usb_ep *_ep, const struct usb_endpoint_descriptor *desc) +static int dummy_enable(struct usb_ep *_ep, + const struct usb_endpoint_descriptor *desc) { struct dummy *dum; struct dummy_hcd *dum_hcd; @@ -419,11 +417,11 @@ dummy_enable (struct usb_ep *_ep, const struct usb_endpoint_descriptor *desc) unsigned max; int retval; - ep = usb_ep_to_dummy_ep (_ep); + ep = usb_ep_to_dummy_ep(_ep); if (!_ep || !desc || ep->desc || _ep->name == ep0name || desc->bDescriptorType != USB_DT_ENDPOINT) return -EINVAL; - dum = ep_to_dummy (ep); + dum = ep_to_dummy(ep); if (!dum->driver) return -ESHUTDOWN; @@ -449,8 +447,8 @@ dummy_enable (struct usb_ep *_ep, const struct usb_endpoint_descriptor *desc) retval = -EINVAL; switch (usb_endpoint_type(desc)) { case USB_ENDPOINT_XFER_BULK: - if (strstr (ep->ep.name, "-iso") - || strstr (ep->ep.name, "-int")) { + if (strstr(ep->ep.name, "-iso") + || strstr(ep->ep.name, "-int")) { goto done; } switch (dum->gadget.speed) { @@ -472,7 +470,7 @@ dummy_enable (struct usb_ep *_ep, const struct usb_endpoint_descriptor *desc) } break; case USB_ENDPOINT_XFER_INT: - if (strstr (ep->ep.name, "-iso")) /* bulk is ok */ + if (strstr(ep->ep.name, "-iso")) /* bulk is ok */ goto done; /* real hardware might not handle all packet sizes */ switch (dum->gadget.speed) { @@ -492,8 +490,8 @@ dummy_enable (struct usb_ep *_ep, const struct usb_endpoint_descriptor *desc) } break; case USB_ENDPOINT_XFER_ISOC: - if (strstr (ep->ep.name, "-bulk") - || strstr (ep->ep.name, "-int")) + if (strstr(ep->ep.name, "-bulk") + || strstr(ep->ep.name, "-int")) goto done; /* real hardware might not handle all packet sizes */ switch (dum->gadget.speed) { @@ -556,68 +554,65 @@ done: return retval; } -static int dummy_disable (struct usb_ep *_ep) +static int dummy_disable(struct usb_ep *_ep) { struct dummy_ep *ep; struct dummy *dum; unsigned long flags; int retval; - ep = usb_ep_to_dummy_ep (_ep); + ep = usb_ep_to_dummy_ep(_ep); if (!_ep || !ep->desc || _ep->name == ep0name) return -EINVAL; - dum = ep_to_dummy (ep); + dum = ep_to_dummy(ep); - spin_lock_irqsave (&dum->lock, flags); + spin_lock_irqsave(&dum->lock, flags); ep->desc = NULL; ep->stream_en = 0; retval = 0; - nuke (dum, ep); - spin_unlock_irqrestore (&dum->lock, flags); + nuke(dum, ep); + spin_unlock_irqrestore(&dum->lock, flags); - dev_dbg (udc_dev(dum), "disabled %s\n", _ep->name); + dev_dbg(udc_dev(dum), "disabled %s\n", _ep->name); return retval; } -static struct usb_request * -dummy_alloc_request (struct usb_ep *_ep, gfp_t mem_flags) +static struct usb_request *dummy_alloc_request(struct usb_ep *_ep, + gfp_t mem_flags) { struct dummy_ep *ep; struct dummy_request *req; if (!_ep) return NULL; - ep = usb_ep_to_dummy_ep (_ep); + ep = usb_ep_to_dummy_ep(_ep); req = kzalloc(sizeof(*req), mem_flags); if (!req) return NULL; - INIT_LIST_HEAD (&req->queue); + INIT_LIST_HEAD(&req->queue); return &req->req; } -static void -dummy_free_request (struct usb_ep *_ep, struct usb_request *_req) +static void dummy_free_request(struct usb_ep *_ep, struct usb_request *_req) { struct dummy_ep *ep; struct dummy_request *req; - ep = usb_ep_to_dummy_ep (_ep); + ep = usb_ep_to_dummy_ep(_ep); if (!ep || !_req || (!ep->desc && _ep->name != ep0name)) return; - req = usb_request_to_dummy_request (_req); - WARN_ON (!list_empty (&req->queue)); - kfree (req); + req = usb_request_to_dummy_request(_req); + WARN_ON(!list_empty(&req->queue)); + kfree(req); } -static void -fifo_complete (struct usb_ep *ep, struct usb_request *req) +static void fifo_complete(struct usb_ep *ep, struct usb_request *req) { } -static int -dummy_queue (struct usb_ep *_ep, struct usb_request *_req, +static int dummy_queue(struct usb_ep *_ep, struct usb_request *_req, gfp_t mem_flags) { struct dummy_ep *ep; @@ -626,49 +621,48 @@ dummy_queue (struct usb_ep *_ep, struct usb_request *_req, struct dummy_hcd *dum_hcd; unsigned long flags; - req = usb_request_to_dummy_request (_req); - if (!_req || !list_empty (&req->queue) || !_req->complete) + req = usb_request_to_dummy_request(_req); + if (!_req || !list_empty(&req->queue) || !_req->complete) return -EINVAL; - ep = usb_ep_to_dummy_ep (_ep); + ep = usb_ep_to_dummy_ep(_ep); if (!_ep || (!ep->desc && _ep->name != ep0name)) return -EINVAL; - dum = ep_to_dummy (ep); + dum = ep_to_dummy(ep); dum_hcd = gadget_to_dummy_hcd(&dum->gadget); if (!dum->driver || !is_enabled(dum_hcd)) return -ESHUTDOWN; #if 0 - dev_dbg (udc_dev(dum), "ep %p queue req %p to %s, len %d buf %p\n", + dev_dbg(udc_dev(dum), "ep %p queue req %p to %s, len %d buf %p\n", ep, _req, _ep->name, _req->length, _req->buf); #endif - _req->status = -EINPROGRESS; _req->actual = 0; - spin_lock_irqsave (&dum->lock, flags); + spin_lock_irqsave(&dum->lock, flags); /* implement an emulated single-request FIFO */ if (ep->desc && (ep->desc->bEndpointAddress & USB_DIR_IN) && - list_empty (&dum->fifo_req.queue) && - list_empty (&ep->queue) && + list_empty(&dum->fifo_req.queue) && + list_empty(&ep->queue) && _req->length <= FIFO_SIZE) { req = &dum->fifo_req; req->req = *_req; req->req.buf = dum->fifo_buf; - memcpy (dum->fifo_buf, _req->buf, _req->length); + memcpy(dum->fifo_buf, _req->buf, _req->length); req->req.context = dum; req->req.complete = fifo_complete; list_add_tail(&req->queue, &ep->queue); - spin_unlock (&dum->lock); + spin_unlock(&dum->lock); _req->actual = _req->length; _req->status = 0; - _req->complete (_ep, _req); - spin_lock (&dum->lock); + _req->complete(_ep, _req); + spin_lock(&dum->lock); } else list_add_tail(&req->queue, &ep->queue); - spin_unlock_irqrestore (&dum->lock, flags); + spin_unlock_irqrestore(&dum->lock, flags); /* real hardware would likely enable transfers here, in case * it'd been left NAKing. @@ -676,7 +670,7 @@ dummy_queue (struct usb_ep *_ep, struct usb_request *_req, return 0; } -static int dummy_dequeue (struct usb_ep *_ep, struct usb_request *_req) +static int dummy_dequeue(struct usb_ep *_ep, struct usb_request *_req) { struct dummy_ep *ep; struct dummy *dum; @@ -686,31 +680,31 @@ static int dummy_dequeue (struct usb_ep *_ep, struct usb_request *_req) if (!_ep || !_req) return retval; - ep = usb_ep_to_dummy_ep (_ep); - dum = ep_to_dummy (ep); + ep = usb_ep_to_dummy_ep(_ep); + dum = ep_to_dummy(ep); if (!dum->driver) return -ESHUTDOWN; - local_irq_save (flags); - spin_lock (&dum->lock); - list_for_each_entry (req, &ep->queue, queue) { + local_irq_save(flags); + spin_lock(&dum->lock); + list_for_each_entry(req, &ep->queue, queue) { if (&req->req == _req) { - list_del_init (&req->queue); + list_del_init(&req->queue); _req->status = -ECONNRESET; retval = 0; break; } } - spin_unlock (&dum->lock); + spin_unlock(&dum->lock); if (retval == 0) { - dev_dbg (udc_dev(dum), + dev_dbg(udc_dev(dum), "dequeued req %p from %s, len %d buf %p\n", req, _ep->name, _req->length, _req->buf); - _req->complete (_ep, _req); + _req->complete(_ep, _req); } - local_irq_restore (flags); + local_irq_restore(flags); return retval; } @@ -722,14 +716,14 @@ dummy_set_halt_and_wedge(struct usb_ep *_ep, int value, int wedged) if (!_ep) return -EINVAL; - ep = usb_ep_to_dummy_ep (_ep); - dum = ep_to_dummy (ep); + ep = usb_ep_to_dummy_ep(_ep); + dum = ep_to_dummy(ep); if (!dum->driver) return -ESHUTDOWN; if (!value) ep->halted = ep->wedged = 0; else if (ep->desc && (ep->desc->bEndpointAddress & USB_DIR_IN) && - !list_empty (&ep->queue)) + !list_empty(&ep->queue)) return -EAGAIN; else { ep->halted = 1; @@ -770,15 +764,15 @@ static const struct usb_ep_ops dummy_ep_ops = { /*-------------------------------------------------------------------------*/ /* there are both host and device side versions of this call ... */ -static int dummy_g_get_frame (struct usb_gadget *_gadget) +static int dummy_g_get_frame(struct usb_gadget *_gadget) { struct timeval tv; - do_gettimeofday (&tv); + do_gettimeofday(&tv); return tv.tv_usec / 1000; } -static int dummy_wakeup (struct usb_gadget *_gadget) +static int dummy_wakeup(struct usb_gadget *_gadget) { struct dummy_hcd *dum_hcd; @@ -801,11 +795,11 @@ static int dummy_wakeup (struct usb_gadget *_gadget) return 0; } -static int dummy_set_selfpowered (struct usb_gadget *_gadget, int value) +static int dummy_set_selfpowered(struct usb_gadget *_gadget, int value) { struct dummy *dum; - dum = (gadget_to_dummy_hcd(_gadget))->dum; + dum = gadget_to_dummy_hcd(_gadget)->dum; if (value) dum->devstatus |= (1 << USB_DEVICE_SELF_POWERED); else @@ -821,7 +815,7 @@ static void dummy_udc_update_ep0(struct dummy *dum) dum->ep[0].ep.maxpacket = 64; } -static int dummy_pullup (struct usb_gadget *_gadget, int value) +static int dummy_pullup(struct usb_gadget *_gadget, int value) { struct dummy_hcd *dum_hcd; struct dummy *dum; @@ -846,10 +840,10 @@ static int dummy_pullup (struct usb_gadget *_gadget, int value) } dum_hcd = gadget_to_dummy_hcd(_gadget); - spin_lock_irqsave (&dum->lock, flags); + spin_lock_irqsave(&dum->lock, flags); dum->pullup = (value != 0); set_link_state(dum_hcd); - spin_unlock_irqrestore (&dum->lock, flags); + spin_unlock_irqrestore(&dum->lock, flags); usb_hcd_poll_rh_status(dummy_hcd_to_hcd(dum_hcd)); return 0; @@ -872,16 +866,16 @@ static const struct usb_gadget_ops dummy_ops = { /*-------------------------------------------------------------------------*/ /* "function" sysfs attribute */ -static ssize_t -show_function (struct device *dev, struct device_attribute *attr, char *buf) +static ssize_t show_function(struct device *dev, struct device_attribute *attr, + char *buf) { - struct dummy *dum = gadget_dev_to_dummy (dev); + struct dummy *dum = gadget_dev_to_dummy(dev); if (!dum->driver || !dum->driver->function) return 0; - return scnprintf (buf, PAGE_SIZE, "%s\n", dum->driver->function); + return scnprintf(buf, PAGE_SIZE, "%s\n", dum->driver->function); } -static DEVICE_ATTR (function, S_IRUGO, show_function, NULL); +static DEVICE_ATTR(function, S_IRUGO, show_function, NULL); /*-------------------------------------------------------------------------*/ @@ -916,7 +910,7 @@ static int dummy_udc_start(struct usb_gadget *g, dum->devstatus = 0; dum->driver = driver; - dev_dbg (udc_dev(dum), "binding gadget driver '%s'\n", + dev_dbg(udc_dev(dum), "binding gadget driver '%s'\n", driver->driver.name); return 0; } @@ -927,7 +921,7 @@ static int dummy_udc_stop(struct usb_gadget *g, struct dummy_hcd *dum_hcd = gadget_to_dummy_hcd(g); struct dummy *dum = dum_hcd->dum; - dev_dbg (udc_dev(dum), "unregister gadget driver '%s'\n", + dev_dbg(udc_dev(dum), "unregister gadget driver '%s'\n", driver->driver.name); dum->driver = NULL; @@ -940,8 +934,7 @@ static int dummy_udc_stop(struct usb_gadget *g, /* The gadget structure is stored inside the hcd structure and will be * released along with it. */ -static void -dummy_gadget_release (struct device *dev) +static void dummy_gadget_release(struct device *dev) { return; } @@ -978,7 +971,7 @@ static void init_dummy_udc_hw(struct dummy *dum) #endif } -static int dummy_udc_probe (struct platform_device *pdev) +static int dummy_udc_probe(struct platform_device *pdev) { struct dummy *dum = &the_controller; int rc; @@ -990,7 +983,7 @@ static int dummy_udc_probe (struct platform_device *pdev) dev_set_name(&dum->gadget.dev, "gadget"); dum->gadget.dev.parent = &pdev->dev; dum->gadget.dev.release = dummy_gadget_release; - rc = device_register (&dum->gadget.dev); + rc = device_register(&dum->gadget.dev); if (rc < 0) { put_device(&dum->gadget.dev); return rc; @@ -1002,7 +995,7 @@ static int dummy_udc_probe (struct platform_device *pdev) if (rc < 0) goto err_udc; - rc = device_create_file (&dum->gadget.dev, &dev_attr_function); + rc = device_create_file(&dum->gadget.dev, &dev_attr_function); if (rc < 0) goto err_dev; platform_set_drvdata(pdev, dum); @@ -1015,14 +1008,14 @@ err_udc: return rc; } -static int dummy_udc_remove (struct platform_device *pdev) +static int dummy_udc_remove(struct platform_device *pdev) { - struct dummy *dum = platform_get_drvdata (pdev); + struct dummy *dum = platform_get_drvdata(pdev); usb_del_gadget_udc(&dum->gadget); - platform_set_drvdata (pdev, NULL); - device_remove_file (&dum->gadget.dev, &dev_attr_function); - device_unregister (&dum->gadget.dev); + platform_set_drvdata(pdev, NULL); + device_remove_file(&dum->gadget.dev, &dev_attr_function); + device_unregister(&dum->gadget.dev); return 0; } @@ -1167,7 +1160,7 @@ static int dummy_validate_stream(struct dummy_hcd *dum_hcd, struct urb *urb) return 0; } -static int dummy_urb_enqueue ( +static int dummy_urb_enqueue( struct usb_hcd *hcd, struct urb *urb, gfp_t mem_flags @@ -1177,7 +1170,7 @@ static int dummy_urb_enqueue ( unsigned long flags; int rc; - urbp = kmalloc (sizeof *urbp, mem_flags); + urbp = kmalloc(sizeof *urbp, mem_flags); if (!urbp) return -ENOMEM; urbp->urb = urb; @@ -1206,7 +1199,7 @@ static int dummy_urb_enqueue ( list_add_tail(&urbp->urbp_list, &dum_hcd->urbp_list); urb->hcpriv = urbp; - if (usb_pipetype (urb->pipe) == PIPE_CONTROL) + if (usb_pipetype(urb->pipe) == PIPE_CONTROL) urb->error_count = 1; /* mark as a new urb */ /* kick the scheduler, it'll do the rest */ @@ -1313,7 +1306,7 @@ static int transfer(struct dummy_hcd *dum_hcd, struct urb *urb, top: /* if there's no request queued, the device is NAKing; return */ - list_for_each_entry (req, &ep->queue, queue) { + list_for_each_entry(req, &ep->queue, queue) { unsigned host_len, dev_len, len; int is_short, to_host; int rescan = 0; @@ -1332,18 +1325,18 @@ top: */ host_len = urb->transfer_buffer_length - urb->actual_length; dev_len = req->req.length - req->req.actual; - len = min (host_len, dev_len); + len = min(host_len, dev_len); /* FIXME update emulated data toggle too */ - to_host = usb_pipein (urb->pipe); - if (unlikely (len == 0)) + to_host = usb_pipein(urb->pipe); + if (unlikely(len == 0)) is_short = 1; else { /* not enough bandwidth left? */ if (limit < ep->ep.maxpacket && limit < len) break; - len = min (len, (unsigned) limit); + len = min_t(unsigned, len, limit); if (len == 0) break; @@ -1404,11 +1397,11 @@ top: /* device side completion --> continuable */ if (req->req.status != -EINPROGRESS) { - list_del_init (&req->queue); + list_del_init(&req->queue); - spin_unlock (&dum->lock); - req->req.complete (&ep->ep, &req->req); - spin_lock (&dum->lock); + spin_unlock(&dum->lock); + req->req.complete(&ep->ep, &req->req); + spin_lock(&dum->lock); /* requests might have been unlinked... */ rescan = 1; @@ -1425,7 +1418,7 @@ top: return limit; } -static int periodic_bytes (struct dummy *dum, struct dummy_ep *ep) +static int periodic_bytes(struct dummy *dum, struct dummy_ep *ep) { int limit = ep->ep.maxpacket; @@ -1461,7 +1454,7 @@ static int periodic_bytes (struct dummy *dum, struct dummy_ep *ep) USB_PORT_STAT_SUSPEND)) \ == (USB_PORT_STAT_CONNECTION | USB_PORT_STAT_ENABLE)) -static struct dummy_ep *find_endpoint (struct dummy *dum, u8 address) +static struct dummy_ep *find_endpoint(struct dummy *dum, u8 address) { int i; @@ -1469,9 +1462,9 @@ static struct dummy_ep *find_endpoint (struct dummy *dum, u8 address) dum->ss_hcd : dum->hs_hcd))) return NULL; if ((address & ~USB_DIR_IN) == 0) - return &dum->ep [0]; + return &dum->ep[0]; for (i = 1; i < DUMMY_ENDPOINTS; i++) { - struct dummy_ep *ep = &dum->ep [i]; + struct dummy_ep *ep = &dum->ep[i]; if (!ep->desc) continue; @@ -1701,19 +1694,19 @@ static void dummy_timer(unsigned long _dum_hcd) /* FIXME if HZ != 1000 this will probably misbehave ... */ /* look at each urb queued by the host side driver */ - spin_lock_irqsave (&dum->lock, flags); + spin_lock_irqsave(&dum->lock, flags); if (!dum_hcd->udev) { dev_err(dummy_dev(dum_hcd), "timer fired with no URBs pending?\n"); - spin_unlock_irqrestore (&dum->lock, flags); + spin_unlock_irqrestore(&dum->lock, flags); return; } for (i = 0; i < DUMMY_ENDPOINTS; i++) { - if (!ep_name [i]) + if (!ep_name[i]) break; - dum->ep [i].already_seen = 0; + dum->ep[i].already_seen = 0; } restart: @@ -1730,7 +1723,7 @@ restart: goto return_urb; else if (dum_hcd->rh_state != DUMMY_RH_RUNNING) continue; - type = usb_pipetype (urb->pipe); + type = usb_pipetype(urb->pipe); /* used up this frame's non-periodic bandwidth? * FIXME there's infinite bandwidth for control and @@ -1741,7 +1734,7 @@ restart: /* find the gadget's ep for this request (if configured) */ address = usb_pipeendpoint (urb->pipe); - if (usb_pipein (urb->pipe)) + if (usb_pipein(urb->pipe)) address |= USB_DIR_IN; ep = find_endpoint(dum, address); if (!ep) { @@ -1756,7 +1749,7 @@ restart: if (ep->already_seen) continue; ep->already_seen = 1; - if (ep == &dum->ep [0] && urb->error_count) { + if (ep == &dum->ep[0] && urb->error_count) { ep->setup_stage = 1; /* a new urb */ urb->error_count = 0; } @@ -1770,21 +1763,21 @@ restart: /* FIXME make sure both ends agree on maxpacket */ /* handle control requests */ - if (ep == &dum->ep [0] && ep->setup_stage) { + if (ep == &dum->ep[0] && ep->setup_stage) { struct usb_ctrlrequest setup; int value = 1; - setup = *(struct usb_ctrlrequest*) urb->setup_packet; + setup = *(struct usb_ctrlrequest *) urb->setup_packet; /* paranoia, in case of stale queued data */ - list_for_each_entry (req, &ep->queue, queue) { - list_del_init (&req->queue); + list_for_each_entry(req, &ep->queue, queue) { + list_del_init(&req->queue); req->req.status = -EOVERFLOW; - dev_dbg (udc_dev(dum), "stale req = %p\n", + dev_dbg(udc_dev(dum), "stale req = %p\n", req); - spin_unlock (&dum->lock); - req->req.complete (&ep->ep, &req->req); - spin_lock (&dum->lock); + spin_unlock(&dum->lock); + req->req.complete(&ep->ep, &req->req); + spin_lock(&dum->lock); ep->already_seen = 0; goto restart; } @@ -1804,10 +1797,10 @@ restart: * until setup() returns; no reentrancy issues etc. */ if (value > 0) { - spin_unlock (&dum->lock); - value = dum->driver->setup (&dum->gadget, + spin_unlock(&dum->lock); + value = dum->driver->setup(&dum->gadget, &setup); - spin_lock (&dum->lock); + spin_lock(&dum->lock); if (value >= 0) { /* no delays (max 64KB data stage) */ @@ -1819,7 +1812,7 @@ restart: if (value < 0) { if (value != -EOPNOTSUPP) - dev_dbg (udc_dev(dum), + dev_dbg(udc_dev(dum), "setup --> %d\n", value); status = -EPIPE; @@ -1831,14 +1824,14 @@ restart: /* non-control requests */ limit = total; - switch (usb_pipetype (urb->pipe)) { + switch (usb_pipetype(urb->pipe)) { case PIPE_ISOCHRONOUS: /* FIXME is it urb->interval since the last xfer? * use urb->iso_frame_desc[i]. * complete whether or not ep has requests queued. * report random errors, to debug drivers. */ - limit = max (limit, periodic_bytes (dum, ep)); + limit = max(limit, periodic_bytes(dum, ep)); status = -ENOSYS; break; @@ -1846,12 +1839,11 @@ restart: /* FIXME is it urb->interval since the last xfer? * this almost certainly polls too fast. */ - limit = max (limit, periodic_bytes (dum, ep)); + limit = max(limit, periodic_bytes(dum, ep)); /* FALLTHROUGH */ - // case PIPE_BULK: case PIPE_CONTROL: default: - treat_control_like_bulk: +treat_control_like_bulk: ep->last_io = jiffies; total = transfer(dum_hcd, urb, ep, limit, &status); break; @@ -1862,15 +1854,15 @@ restart: continue; return_urb: - list_del (&urbp->urbp_list); - kfree (urbp); + list_del(&urbp->urbp_list); + kfree(urbp); if (ep) ep->already_seen = ep->setup_stage = 0; usb_hcd_unlink_urb_from_ep(dummy_hcd_to_hcd(dum_hcd), urb); - spin_unlock (&dum->lock); + spin_unlock(&dum->lock); usb_hcd_giveback_urb(dummy_hcd_to_hcd(dum_hcd), urb, status); - spin_lock (&dum->lock); + spin_lock(&dum->lock); goto restart; } @@ -1883,7 +1875,7 @@ return_urb: mod_timer(&dum_hcd->timer, jiffies + msecs_to_jiffies(1)); } - spin_unlock_irqrestore (&dum->lock, flags); + spin_unlock_irqrestore(&dum->lock, flags); } /*-------------------------------------------------------------------------*/ @@ -1895,7 +1887,7 @@ return_urb: | USB_PORT_STAT_C_OVERCURRENT \ | USB_PORT_STAT_C_RESET) << 16) -static int dummy_hub_status (struct usb_hcd *hcd, char *buf) +static int dummy_hub_status(struct usb_hcd *hcd, char *buf) { struct dummy_hcd *dum_hcd; unsigned long flags; @@ -1919,7 +1911,7 @@ static int dummy_hub_status (struct usb_hcd *hcd, char *buf) dum_hcd->port_status); retval = 1; if (dum_hcd->rh_state == DUMMY_RH_SUSPENDED) - usb_hcd_resume_root_hub (hcd); + usb_hcd_resume_root_hub(hcd); } done: spin_unlock_irqrestore(&dum_hcd->dum->lock, flags); @@ -1938,10 +1930,9 @@ ss_hub_descriptor(struct usb_hub_descriptor *desc) desc->u.ss.DeviceRemovable = 0xffff; } -static inline void -hub_descriptor (struct usb_hub_descriptor *desc) +static inline void hub_descriptor(struct usb_hub_descriptor *desc) { - memset (desc, 0, sizeof *desc); + memset(desc, 0, sizeof *desc); desc->bDescriptorType = 0x29; desc->bDescLength = 9; desc->wHubCharacteristics = cpu_to_le16(0x0001); @@ -1950,7 +1941,7 @@ hub_descriptor (struct usb_hub_descriptor *desc) desc->u.hs.DeviceRemovable[1] = 0xff; } -static int dummy_hub_control ( +static int dummy_hub_control( struct usb_hcd *hcd, u16 typeReq, u16 wValue, @@ -2018,7 +2009,7 @@ static int dummy_hub_control ( hub_descriptor((struct usb_hub_descriptor *) buf); break; case GetHubStatus: - *(__le32 *) buf = cpu_to_le32 (0); + *(__le32 *) buf = cpu_to_le32(0); break; case GetPortStatus: if (wIndex != 1) @@ -2060,8 +2051,8 @@ static int dummy_hub_control ( } } set_link_state(dum_hcd); - ((__le16 *) buf)[0] = cpu_to_le16 (dum_hcd->port_status); - ((__le16 *) buf)[1] = cpu_to_le16 (dum_hcd->port_status >> 16); + ((__le16 *) buf)[0] = cpu_to_le16(dum_hcd->port_status); + ((__le16 *) buf)[1] = cpu_to_le16(dum_hcd->port_status >> 16); break; case SetHubFeature: retval = -EPIPE; @@ -2195,15 +2186,15 @@ error: spin_unlock_irqrestore(&dum_hcd->dum->lock, flags); if ((dum_hcd->port_status & PORT_C_MASK) != 0) - usb_hcd_poll_rh_status (hcd); + usb_hcd_poll_rh_status(hcd); return retval; } -static int dummy_bus_suspend (struct usb_hcd *hcd) +static int dummy_bus_suspend(struct usb_hcd *hcd) { struct dummy_hcd *dum_hcd = hcd_to_dummy_hcd(hcd); - dev_dbg (&hcd->self.root_hub->dev, "%s\n", __func__); + dev_dbg(&hcd->self.root_hub->dev, "%s\n", __func__); spin_lock_irq(&dum_hcd->dum->lock); dum_hcd->rh_state = DUMMY_RH_SUSPENDED; @@ -2213,12 +2204,12 @@ static int dummy_bus_suspend (struct usb_hcd *hcd) return 0; } -static int dummy_bus_resume (struct usb_hcd *hcd) +static int dummy_bus_resume(struct usb_hcd *hcd) { struct dummy_hcd *dum_hcd = hcd_to_dummy_hcd(hcd); int rc = 0; - dev_dbg (&hcd->self.root_hub->dev, "%s\n", __func__); + dev_dbg(&hcd->self.root_hub->dev, "%s\n", __func__); spin_lock_irq(&dum_hcd->dum->lock); if (!HCD_HW_ACCESSIBLE(hcd)) { @@ -2236,55 +2227,54 @@ static int dummy_bus_resume (struct usb_hcd *hcd) /*-------------------------------------------------------------------------*/ -static inline ssize_t -show_urb (char *buf, size_t size, struct urb *urb) +static inline ssize_t show_urb(char *buf, size_t size, struct urb *urb) { - int ep = usb_pipeendpoint (urb->pipe); + int ep = usb_pipeendpoint(urb->pipe); - return snprintf (buf, size, + return snprintf(buf, size, "urb/%p %s ep%d%s%s len %d/%d\n", urb, ({ char *s; - switch (urb->dev->speed) { - case USB_SPEED_LOW: + switch (urb->dev->speed) { + case USB_SPEED_LOW: s = "ls"; break; - case USB_SPEED_FULL: + case USB_SPEED_FULL: s = "fs"; break; - case USB_SPEED_HIGH: + case USB_SPEED_HIGH: s = "hs"; break; - case USB_SPEED_SUPER: + case USB_SPEED_SUPER: s = "ss"; break; - default: + default: s = "?"; break; }; s; }), - ep, ep ? (usb_pipein (urb->pipe) ? "in" : "out") : "", + ep, ep ? (usb_pipein(urb->pipe) ? "in" : "out") : "", ({ char *s; \ - switch (usb_pipetype (urb->pipe)) { \ - case PIPE_CONTROL: \ + switch (usb_pipetype(urb->pipe)) { \ + case PIPE_CONTROL: \ s = ""; \ break; \ - case PIPE_BULK: \ + case PIPE_BULK: \ s = "-bulk"; \ break; \ - case PIPE_INTERRUPT: \ + case PIPE_INTERRUPT: \ s = "-int"; \ break; \ - default: \ + default: \ s = "-iso"; \ break; \ - }; s;}), + }; s; }), urb->actual_length, urb->transfer_buffer_length); } -static ssize_t -show_urbs (struct device *dev, struct device_attribute *attr, char *buf) +static ssize_t show_urbs(struct device *dev, struct device_attribute *attr, + char *buf) { - struct usb_hcd *hcd = dev_get_drvdata (dev); + struct usb_hcd *hcd = dev_get_drvdata(dev); struct dummy_hcd *dum_hcd = hcd_to_dummy_hcd(hcd); struct urbp *urbp; size_t size = 0; @@ -2294,7 +2284,7 @@ show_urbs (struct device *dev, struct device_attribute *attr, char *buf) list_for_each_entry(urbp, &dum_hcd->urbp_list, urbp_list) { size_t temp; - temp = show_urb (buf, PAGE_SIZE - size, urbp->urb); + temp = show_urb(buf, PAGE_SIZE - size, urbp->urb); buf += temp; size += temp; } @@ -2302,7 +2292,7 @@ show_urbs (struct device *dev, struct device_attribute *attr, char *buf) return size; } -static DEVICE_ATTR (urbs, S_IRUGO, show_urbs, NULL); +static DEVICE_ATTR(urbs, S_IRUGO, show_urbs, NULL); static int dummy_start_ss(struct dummy_hcd *dum_hcd) { @@ -2356,11 +2346,11 @@ static int dummy_start(struct usb_hcd *hcd) return device_create_file(dummy_dev(dum_hcd), &dev_attr_urbs); } -static void dummy_stop (struct usb_hcd *hcd) +static void dummy_stop(struct usb_hcd *hcd) { struct dummy *dum; - dum = (hcd_to_dummy_hcd(hcd))->dum; + dum = hcd_to_dummy_hcd(hcd)->dum; device_remove_file(dummy_dev(hcd_to_dummy_hcd(hcd)), &dev_attr_urbs); usb_gadget_unregister_driver(dum->driver); dev_info(dummy_dev(hcd_to_dummy_hcd(hcd)), "stopped\n"); @@ -2368,9 +2358,9 @@ static void dummy_stop (struct usb_hcd *hcd) /*-------------------------------------------------------------------------*/ -static int dummy_h_get_frame (struct usb_hcd *hcd) +static int dummy_h_get_frame(struct usb_hcd *hcd) { - return dummy_g_get_frame (NULL); + return dummy_g_get_frame(NULL); } static int dummy_setup(struct usb_hcd *hcd) @@ -2485,13 +2475,13 @@ static struct hc_driver dummy_hcd = { .start = dummy_start, .stop = dummy_stop, - .urb_enqueue = dummy_urb_enqueue, - .urb_dequeue = dummy_urb_dequeue, + .urb_enqueue = dummy_urb_enqueue, + .urb_dequeue = dummy_urb_dequeue, - .get_frame_number = dummy_h_get_frame, + .get_frame_number = dummy_h_get_frame, - .hub_status_data = dummy_hub_status, - .hub_control = dummy_hub_control, + .hub_status_data = dummy_hub_status, + .hub_control = dummy_hub_control, .bus_suspend = dummy_bus_suspend, .bus_resume = dummy_bus_resume, @@ -2546,7 +2536,7 @@ static int dummy_hcd_remove(struct platform_device *pdev) { struct dummy *dum; - dum = (hcd_to_dummy_hcd(platform_get_drvdata(pdev)))->dum; + dum = hcd_to_dummy_hcd(platform_get_drvdata(pdev))->dum; if (dum->ss_hcd) { usb_remove_hcd(dummy_hcd_to_hcd(dum->ss_hcd)); @@ -2562,15 +2552,15 @@ static int dummy_hcd_remove(struct platform_device *pdev) return 0; } -static int dummy_hcd_suspend (struct platform_device *pdev, pm_message_t state) +static int dummy_hcd_suspend(struct platform_device *pdev, pm_message_t state) { struct usb_hcd *hcd; struct dummy_hcd *dum_hcd; int rc = 0; - dev_dbg (&pdev->dev, "%s\n", __func__); + dev_dbg(&pdev->dev, "%s\n", __func__); - hcd = platform_get_drvdata (pdev); + hcd = platform_get_drvdata(pdev); dum_hcd = hcd_to_dummy_hcd(hcd); if (dum_hcd->rh_state == DUMMY_RH_RUNNING) { dev_warn(&pdev->dev, "Root hub isn't suspended!\n"); @@ -2580,15 +2570,15 @@ static int dummy_hcd_suspend (struct platform_device *pdev, pm_message_t state) return rc; } -static int dummy_hcd_resume (struct platform_device *pdev) +static int dummy_hcd_resume(struct platform_device *pdev) { struct usb_hcd *hcd; - dev_dbg (&pdev->dev, "%s\n", __func__); + dev_dbg(&pdev->dev, "%s\n", __func__); - hcd = platform_get_drvdata (pdev); + hcd = platform_get_drvdata(pdev); set_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags); - usb_hcd_poll_rh_status (hcd); + usb_hcd_poll_rh_status(hcd); return 0; } @@ -2608,11 +2598,11 @@ static struct platform_driver dummy_hcd_driver = { static struct platform_device *the_udc_pdev; static struct platform_device *the_hcd_pdev; -static int __init init (void) +static int __init init(void) { int retval = -ENOMEM; - if (usb_disabled ()) + if (usb_disabled()) return -ENODEV; if (!mod_data.is_high_speed && mod_data.is_super_speed) @@ -2671,13 +2661,13 @@ err_alloc_udc: platform_device_put(the_hcd_pdev); return retval; } -module_init (init); +module_init(init); -static void __exit cleanup (void) +static void __exit cleanup(void) { platform_device_unregister(the_udc_pdev); platform_device_unregister(the_hcd_pdev); platform_driver_unregister(&dummy_udc_driver); platform_driver_unregister(&dummy_hcd_driver); } -module_exit (cleanup); +module_exit(cleanup); -- cgit v0.10.2 From 54b8360ffd4c8b2c128ca25233b8c6876fb92d30 Mon Sep 17 00:00:00 2001 From: Michal Nazarewicz Date: Fri, 13 Jan 2012 15:05:16 +0100 Subject: usb: gadget: update Michal Nazarewicz's email address The m.nazarewicz@samsung.com email address is no longer valid, so this commit replaces it with mina86@mina86.com which is employer-agnostic and thus should be valid for foreseeable feature. Signed-off-by: Michal Nazarewicz Signed-off-by: Felipe Balbi diff --git a/drivers/usb/gadget/f_acm.c b/drivers/usb/gadget/f_acm.c index 3f88493..e482121 100644 --- a/drivers/usb/gadget/f_acm.c +++ b/drivers/usb/gadget/f_acm.c @@ -5,7 +5,7 @@ * Copyright (C) 2008 by David Brownell * Copyright (C) 2008 by Nokia Corporation * Copyright (C) 2009 by Samsung Electronics - * Author: Michal Nazarewicz (m.nazarewicz@samsung.com) + * Author: Michal Nazarewicz (mina86@mina86.com) * * This software is distributed under the terms of the GNU General * Public License ("GPL") as published by the Free Software Foundation, diff --git a/drivers/usb/gadget/f_fs.c b/drivers/usb/gadget/f_fs.c index f63dc6c..7e2216f 100644 --- a/drivers/usb/gadget/f_fs.c +++ b/drivers/usb/gadget/f_fs.c @@ -2,7 +2,7 @@ * f_fs.c -- user mode file system API for USB composite function controllers * * Copyright (C) 2010 Samsung Electronics - * Author: Michal Nazarewicz + * Author: Michal Nazarewicz * * Based on inode.c (GadgetFS) which was: * Copyright (C) 2003-2004 David Brownell diff --git a/drivers/usb/gadget/f_mass_storage.c b/drivers/usb/gadget/f_mass_storage.c index 6353eca..6cde021 100644 --- a/drivers/usb/gadget/f_mass_storage.c +++ b/drivers/usb/gadget/f_mass_storage.c @@ -3,7 +3,7 @@ * * Copyright (C) 2003-2008 Alan Stern * Copyright (C) 2009 Samsung Electronics - * Author: Michal Nazarewicz + * Author: Michal Nazarewicz * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/usb/gadget/f_rndis.c b/drivers/usb/gadget/f_rndis.c index 704d1d9..7b1cf18 100644 --- a/drivers/usb/gadget/f_rndis.c +++ b/drivers/usb/gadget/f_rndis.c @@ -5,7 +5,7 @@ * Copyright (C) 2003-2004 Robert Schwebel, Benedikt Spranger * Copyright (C) 2008 Nokia Corporation * Copyright (C) 2009 Samsung Electronics - * Author: Michal Nazarewicz (m.nazarewicz@samsung.com) + * Author: Michal Nazarewicz (mina86@mina86.com) * * 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 diff --git a/drivers/usb/gadget/g_ffs.c b/drivers/usb/gadget/g_ffs.c index 0519d77..331cd67 100644 --- a/drivers/usb/gadget/g_ffs.c +++ b/drivers/usb/gadget/g_ffs.c @@ -2,7 +2,7 @@ * g_ffs.c -- user mode file system API for USB composite function controllers * * Copyright (C) 2010 Samsung Electronics - * Author: Michal Nazarewicz + * Author: Michal Nazarewicz * * 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 diff --git a/drivers/usb/gadget/mass_storage.c b/drivers/usb/gadget/mass_storage.c index e24f72f..1f376eb 100644 --- a/drivers/usb/gadget/mass_storage.c +++ b/drivers/usb/gadget/mass_storage.c @@ -3,7 +3,7 @@ * * Copyright (C) 2003-2008 Alan Stern * Copyright (C) 2009 Samsung Electronics - * Author: Michal Nazarewicz + * Author: Michal Nazarewicz * All rights reserved. * * This program is free software; you can redistribute it and/or modify diff --git a/drivers/usb/gadget/multi.c b/drivers/usb/gadget/multi.c index 7e7f515..c37fb33 100644 --- a/drivers/usb/gadget/multi.c +++ b/drivers/usb/gadget/multi.c @@ -4,7 +4,7 @@ * Copyright (C) 2008 David Brownell * Copyright (C) 2008 Nokia Corporation * Copyright (C) 2009 Samsung Electronics - * Author: Michal Nazarewicz (m.nazarewicz@samsung.com) + * Author: Michal Nazarewicz (mina86@mina86.com) * * 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 diff --git a/drivers/usb/gadget/storage_common.c b/drivers/usb/gadget/storage_common.c index c7f291a..9d450fe 100644 --- a/drivers/usb/gadget/storage_common.c +++ b/drivers/usb/gadget/storage_common.c @@ -3,7 +3,7 @@ * * Copyright (C) 2003-2008 Alan Stern * Copyeight (C) 2009 Samsung Electronics - * Author: Michal Nazarewicz (m.nazarewicz@samsung.com) + * Author: Michal Nazarewicz (mina86@mina86.com) * * 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 diff --git a/tools/usb/ffs-test.c b/tools/usb/ffs-test.c index b9c7986..53452c3 100644 --- a/tools/usb/ffs-test.c +++ b/tools/usb/ffs-test.c @@ -2,7 +2,7 @@ * ffs-test.c.c -- user mode filesystem api for usb composite function * * Copyright (C) 2010 Samsung Electronics - * Author: Michal Nazarewicz + * Author: Michal Nazarewicz * * 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 diff --git a/tools/usb/testusb.c b/tools/usb/testusb.c index f08e894..6e0f567 100644 --- a/tools/usb/testusb.c +++ b/tools/usb/testusb.c @@ -3,7 +3,7 @@ /* * Copyright (c) 2002 by David Brownell * Copyright (c) 2010 by Samsung Electronics - * Author: Michal Nazarewicz + * Author: Michal Nazarewicz * * 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 -- cgit v0.10.2 From fcc0bb5ace11e6d0a27bdbdbf2e5e6d361a4e701 Mon Sep 17 00:00:00 2001 From: Sebastian Andrzej Siewior Date: Fri, 13 Jan 2012 21:54:46 +0100 Subject: usb: gadget: f_mass_storage: remove one FSG_NO_INTR_EP Remove one define of FSG_NO_INTR_EP and we still have that we can use. Signed-off-by: Sebastian Andrzej Siewior Acked-by: Michal Nazarewicz Signed-off-by: Felipe Balbi diff --git a/drivers/usb/gadget/f_mass_storage.c b/drivers/usb/gadget/f_mass_storage.c index 6cde021..8246271 100644 --- a/drivers/usb/gadget/f_mass_storage.c +++ b/drivers/usb/gadget/f_mass_storage.c @@ -304,7 +304,6 @@ static const char fsg_string_interface[] = "Mass Storage"; -#define FSG_NO_INTR_EP 1 #define FSG_NO_DEVICE_STRINGS 1 #define FSG_NO_OTG 1 #define FSG_NO_INTR_EP 1 -- cgit v0.10.2 From d11519adc214c243ec606f186316f2667b677694 Mon Sep 17 00:00:00 2001 From: Praveena Nadahally Date: Wed, 25 Jan 2012 11:02:03 +0100 Subject: usb: gadget: Add Interface Association Descriptor to ECM Add IAD to bind the two interfaces of ECM so that it works properly in composite gadget mode. Signed-off-by: Thirupathi Signed-off-by: Praveena Nadahally Signed-off-by: Linus Walleij Signed-off-by: Felipe Balbi diff --git a/drivers/usb/gadget/f_ecm.c b/drivers/usb/gadget/f_ecm.c index 11c07cb..30b908f 100644 --- a/drivers/usb/gadget/f_ecm.c +++ b/drivers/usb/gadget/f_ecm.c @@ -97,6 +97,20 @@ static inline unsigned ecm_bitrate(struct usb_gadget *g) /* interface descriptor: */ +static struct usb_interface_assoc_descriptor +ecm_iad_descriptor = { + .bLength = sizeof ecm_iad_descriptor, + .bDescriptorType = USB_DT_INTERFACE_ASSOCIATION, + + /* .bFirstInterface = DYNAMIC, */ + .bInterfaceCount = 2, /* control + data */ + .bFunctionClass = USB_CLASS_COMM, + .bFunctionSubClass = USB_CDC_SUBCLASS_ETHERNET, + .bFunctionProtocol = USB_CDC_PROTO_NONE, + /* .iFunction = DYNAMIC */ +}; + + static struct usb_interface_descriptor ecm_control_intf = { .bLength = sizeof ecm_control_intf, .bDescriptorType = USB_DT_INTERFACE, @@ -199,6 +213,7 @@ static struct usb_endpoint_descriptor fs_ecm_out_desc = { static struct usb_descriptor_header *ecm_fs_function[] = { /* CDC ECM control descriptors */ + (struct usb_descriptor_header *) &ecm_iad_descriptor, (struct usb_descriptor_header *) &ecm_control_intf, (struct usb_descriptor_header *) &ecm_header_desc, (struct usb_descriptor_header *) &ecm_union_desc, @@ -247,6 +262,7 @@ static struct usb_endpoint_descriptor hs_ecm_out_desc = { static struct usb_descriptor_header *ecm_hs_function[] = { /* CDC ECM control descriptors */ + (struct usb_descriptor_header *) &ecm_iad_descriptor, (struct usb_descriptor_header *) &ecm_control_intf, (struct usb_descriptor_header *) &ecm_header_desc, (struct usb_descriptor_header *) &ecm_union_desc, @@ -339,6 +355,7 @@ static struct usb_string ecm_string_defs[] = { [0].s = "CDC Ethernet Control Model (ECM)", [1].s = NULL /* DYNAMIC */, [2].s = "CDC Ethernet Data", + [3].s = "CDC ECM", { } /* end of list */ }; @@ -674,6 +691,7 @@ ecm_bind(struct usb_configuration *c, struct usb_function *f) if (status < 0) goto fail; ecm->ctrl_id = status; + ecm_iad_descriptor.bFirstInterface = status; ecm_control_intf.bInterfaceNumber = status; ecm_union_desc.bMasterInterface0 = status; @@ -864,6 +882,13 @@ ecm_bind_config(struct usb_configuration *c, u8 ethaddr[ETH_ALEN]) return status; ecm_string_defs[1].id = status; ecm_desc.iMACAddress = status; + + /* IAD label */ + status = usb_string_id(c->cdev); + if (status < 0) + return status; + ecm_string_defs[3].id = status; + ecm_iad_descriptor.iFunction = status; } /* allocate and initialize one new instance */ -- cgit v0.10.2 From 3cf0ad02e42a91e85ffe9bd67422dd266531d3ec Mon Sep 17 00:00:00 2001 From: Sebastian Andrzej Siewior Date: Wed, 25 Jan 2012 11:51:19 +0100 Subject: usb: gadget: dummy_hcd: don't assign ->desc on error case If the stream check fails then we leave ep->desc assigend but we return with an error code. The caller assumes the endpoint is not enabled (which is the case) but it can not enable it again due to this assigment. Signed-off-by: Sebastian Andrzej Siewior Acked-by: Alan Stern Signed-off-by: Felipe Balbi diff --git a/drivers/usb/gadget/dummy_hcd.c b/drivers/usb/gadget/dummy_hcd.c index 67573e5..9170a4c 100644 --- a/drivers/usb/gadget/dummy_hcd.c +++ b/drivers/usb/gadget/dummy_hcd.c @@ -514,7 +514,6 @@ static int dummy_enable(struct usb_ep *_ep, } _ep->maxpacket = max; - ep->desc = desc; if (usb_ss_max_streams(_ep->comp_desc)) { if (!usb_endpoint_xfer_bulk(desc)) { dev_err(udc_dev(dum), "Can't enable stream support on " @@ -523,6 +522,7 @@ static int dummy_enable(struct usb_ep *_ep, } ep->stream_en = 1; } + ep->desc = desc; dev_dbg(udc_dev(dum), "enabled %s (ep%d%s-%s) maxpacket %d stream %s\n", _ep->name, -- cgit v0.10.2 From 20edfbb6a17f3007c1905e9849d8d306e318883b Mon Sep 17 00:00:00 2001 From: Sebastian Andrzej Siewior Date: Wed, 25 Jan 2012 15:18:58 +0100 Subject: usb: gadget: dummy_hcd: fix null-deref free req _ep to ep is a pointer substraction so ep won't be zero unless _ep was 8. This was not intendent by the author, it was probably a typo while checking for NULL of the argument. Signed-off-by: Sebastian Andrzej Siewior Acked-by: Alan Stern Signed-off-by: Felipe Balbi diff --git a/drivers/usb/gadget/dummy_hcd.c b/drivers/usb/gadget/dummy_hcd.c index 9170a4c..8cc1a88 100644 --- a/drivers/usb/gadget/dummy_hcd.c +++ b/drivers/usb/gadget/dummy_hcd.c @@ -599,8 +599,10 @@ static void dummy_free_request(struct usb_ep *_ep, struct usb_request *_req) struct dummy_ep *ep; struct dummy_request *req; + if (!_ep || !_req) + return; ep = usb_ep_to_dummy_ep(_ep); - if (!ep || !_req || (!ep->desc && _ep->name != ep0name)) + if (!ep->desc && _ep->name != ep0name) return; req = usb_request_to_dummy_request(_req); -- cgit v0.10.2 From dd63180b758d5972fc90621af0741d5bfae1a684 Mon Sep 17 00:00:00 2001 From: Tomoya MORINAGA Date: Fri, 3 Feb 2012 16:35:26 +0900 Subject: usb: gadget: pch_udc: Detecting VBUS through GPIO Problem: In USB Suspend, pch_udc handles 'disconnect'. Root cause: The current pch_udc is not monitoring VBUS. When USB cable is disconnected, USB Device Controller generates an interrupt of USB Suspend. pch_udc cannot distinguish it is USB Suspend or disconnect. Therefore, pch_udc handles 'disconnect' after an interrupt of USB Suspend happend. Solution: VBUS is detected through GPIO. After an interrupt produced USB Suspend, if VBUS is Low, pch_udc handles 'disconnect'. If VBUS is High, pch_udc handles 'suspend'. Signed-off-by: Tomoya MORINAGA Signed-off-by: Felipe Balbi diff --git a/drivers/usb/gadget/pch_udc.c b/drivers/usb/gadget/pch_udc.c index 82416aa..9c5ae2c 100644 --- a/drivers/usb/gadget/pch_udc.c +++ b/drivers/usb/gadget/pch_udc.c @@ -15,6 +15,13 @@ #include #include #include +#include + +/* GPIO port for VBUS detecting */ +static int vbus_gpio_port = -1; /* GPIO port number (-1:Not used) */ + +#define PCH_VBUS_PERIOD 3000 /* VBUS polling period (msec) */ +#define PCH_VBUS_INTERVAL 10 /* VBUS polling interval (msec) */ /* Address offset of Registers */ #define UDC_EP_REG_SHIFT 0x20 /* Offset to next EP */ @@ -296,6 +303,17 @@ struct pch_udc_ep { }; /** + * struct pch_vbus_gpio_data - Structure holding GPIO informaton + * for detecting VBUS + * @port: gpio port number + * @irq_work_fall Structure for WorkQueue + */ +struct pch_vbus_gpio_data { + int port; + struct work_struct irq_work_fall; +}; + +/** * struct pch_udc_dev - Structure holding complete information * of the PCH USB device * @gadget: gadget driver data @@ -323,6 +341,7 @@ struct pch_udc_ep { * @base_addr: for mapped device memory * @irq: IRQ line for the device * @cfg_data: current cfg, intf, and alt in use + * @vbus_gpio: GPIO informaton for detecting VBUS */ struct pch_udc_dev { struct usb_gadget gadget; @@ -349,7 +368,8 @@ struct pch_udc_dev { unsigned long phys_addr; void __iomem *base_addr; unsigned irq; - struct pch_udc_cfg_data cfg_data; + struct pch_udc_cfg_data cfg_data; + struct pch_vbus_gpio_data vbus_gpio; }; #define PCH_UDC_PCI_BAR 1 @@ -1226,6 +1246,115 @@ static const struct usb_gadget_ops pch_udc_ops = { }; /** + * pch_vbus_gpio_get_value() - This API gets value of GPIO port as VBUS status. + * @dev: Reference to the driver structure + * + * Return value: + * 1: VBUS is high + * 0: VBUS is low + * -1: It is not enable to detect VBUS using GPIO + */ +static int pch_vbus_gpio_get_value(struct pch_udc_dev *dev) +{ + int vbus = 0; + + if (dev->vbus_gpio.port) + vbus = gpio_get_value(dev->vbus_gpio.port) ? 1 : 0; + else + vbus = -1; + + return vbus; +} + +/** + * pch_vbus_gpio_work_fall() - This API keeps watch on VBUS becoming Low. + * If VBUS is Low, disconnect is processed + * @irq_work: Structure for WorkQueue + * + */ +static void pch_vbus_gpio_work_fall(struct work_struct *irq_work) +{ + struct pch_vbus_gpio_data *vbus_gpio = container_of(irq_work, + struct pch_vbus_gpio_data, irq_work_fall); + struct pch_udc_dev *dev = + container_of(vbus_gpio, struct pch_udc_dev, vbus_gpio); + int vbus_saved = -1; + int vbus; + int count; + + if (!dev->vbus_gpio.port) + return; + + for (count = 0; count < (PCH_VBUS_PERIOD / PCH_VBUS_INTERVAL); + count++) { + vbus = pch_vbus_gpio_get_value(dev); + + if ((vbus_saved == vbus) && (vbus == 0)) { + dev_dbg(&dev->pdev->dev, "VBUS fell"); + if (dev->driver + && dev->driver->disconnect) { + dev->driver->disconnect( + &dev->gadget); + } + pch_udc_reconnect(dev); + dev_dbg(&dev->pdev->dev, "VBUS fell"); + return; + } + vbus_saved = vbus; + mdelay(PCH_VBUS_INTERVAL); + } +} + +/** + * pch_vbus_gpio_init() - This API initializes GPIO port detecting VBUS. + * @dev: Reference to the driver structure + * @vbus_gpio Number of GPIO port to detect gpio + * + * Return codes: + * 0: Success + * -EINVAL: GPIO port is invalid or can't be initialized. + */ +static int pch_vbus_gpio_init(struct pch_udc_dev *dev, int vbus_gpio_port) +{ + int err; + + dev->vbus_gpio.port = 0; + + if (vbus_gpio_port <= -1) + return -EINVAL; + + err = gpio_is_valid(vbus_gpio_port); + if (!err) { + pr_err("%s: gpio port %d is invalid\n", + __func__, vbus_gpio_port); + return -EINVAL; + } + + err = gpio_request(vbus_gpio_port, "pch_vbus"); + if (err) { + pr_err("%s: can't request gpio port %d, err: %d\n", + __func__, vbus_gpio_port, err); + return -EINVAL; + } + + dev->vbus_gpio.port = vbus_gpio_port; + gpio_direction_input(vbus_gpio_port); + INIT_WORK(&dev->vbus_gpio.irq_work_fall, pch_vbus_gpio_work_fall); + + return 0; +} + +/** + * pch_vbus_gpio_free() - This API frees resources of GPIO port + * @dev: Reference to the driver structure + */ +static void pch_vbus_gpio_free(struct pch_udc_dev *dev) +{ + if (dev->vbus_gpio.port) + gpio_free(dev->vbus_gpio.port); +} + +/** * complete_req() - This API is invoked from the driver when processing * of a request is complete * @ep: Reference to the endpoint structure @@ -2510,6 +2639,8 @@ static void pch_udc_svc_cfg_interrupt(struct pch_udc_dev *dev) */ static void pch_udc_dev_isr(struct pch_udc_dev *dev, u32 dev_intr) { + int vbus; + /* USB Reset Interrupt */ if (dev_intr & UDC_DEVINT_UR) { pch_udc_svc_ur_interrupt(dev); @@ -2535,14 +2666,19 @@ static void pch_udc_dev_isr(struct pch_udc_dev *dev, u32 dev_intr) spin_lock(&dev->lock); } - if (dev->vbus_session == 0) { + vbus = pch_vbus_gpio_get_value(dev); + if ((dev->vbus_session == 0) + && (vbus != 1)) { if (dev->driver && dev->driver->disconnect) { spin_unlock(&dev->lock); dev->driver->disconnect(&dev->gadget); spin_lock(&dev->lock); } pch_udc_reconnect(dev); - } + } else if ((dev->vbus_session == 0) + && (vbus == 1)) + schedule_work(&dev->vbus_gpio.irq_work_fall); + dev_dbg(&dev->pdev->dev, "USB_SUSPEND\n"); } /* Clear the SOF interrupt, if enabled */ @@ -2704,6 +2840,7 @@ static int pch_udc_pcd_init(struct pch_udc_dev *dev) { pch_udc_init(dev); pch_udc_pcd_reinit(dev); + pch_vbus_gpio_init(dev, vbus_gpio_port); return 0; } @@ -2882,6 +3019,8 @@ static void pch_udc_remove(struct pci_dev *pdev) UDC_EP0OUT_BUFF_SIZE * 4, DMA_FROM_DEVICE); kfree(dev->ep0out_buf); + pch_vbus_gpio_free(dev); + pch_udc_exit(dev); if (dev->irq_registered) -- cgit v0.10.2 From 637b78eb31e0b167ed913f1750bb645dfeda38f0 Mon Sep 17 00:00:00 2001 From: Tomoya MORINAGA Date: Fri, 3 Feb 2012 16:14:18 +0900 Subject: usb: gadget: pch_udc: Detecting VBUS through GPIO with interrupt Problem: pch_udc continues operation even if VBUS becomes Low. pch_udc performs D+ pulling up before VBUS becomes High. USB device should be controlled according to VBUS state. Root cause: The current pch_udc is not always monitoring VBUS. Solution: The change of VBUS is detected using an interrupt of GPIO. If VBUS became Low, pch_udc handles 'disconnect'. After VBUS became High, a pull improves D+, and pch_udc handles 'connect'. [ balbi@ti.com : make it actually compile ] Signed-off-by: Tomoya MORINAGA Signed-off-by: Felipe Balbi diff --git a/drivers/usb/gadget/pch_udc.c b/drivers/usb/gadget/pch_udc.c index 9c5ae2c..a992084 100644 --- a/drivers/usb/gadget/pch_udc.c +++ b/drivers/usb/gadget/pch_udc.c @@ -306,11 +306,15 @@ struct pch_udc_ep { * struct pch_vbus_gpio_data - Structure holding GPIO informaton * for detecting VBUS * @port: gpio port number + * @intr: gpio interrupt number * @irq_work_fall Structure for WorkQueue + * @irq_work_rise Structure for WorkQueue */ struct pch_vbus_gpio_data { int port; + int intr; struct work_struct irq_work_fall; + struct work_struct irq_work_rise; }; /** @@ -1296,8 +1300,10 @@ static void pch_vbus_gpio_work_fall(struct work_struct *irq_work) dev->driver->disconnect( &dev->gadget); } - pch_udc_reconnect(dev); - dev_dbg(&dev->pdev->dev, "VBUS fell"); + if (dev->vbus_gpio.intr) + pch_udc_init(dev); + else + pch_udc_reconnect(dev); return; } vbus_saved = vbus; @@ -1306,6 +1312,57 @@ static void pch_vbus_gpio_work_fall(struct work_struct *irq_work) } /** + * pch_vbus_gpio_work_rise() - This API checks VBUS is High. + * If VBUS is High, connect is processed + * @irq_work: Structure for WorkQueue + * + */ +static void pch_vbus_gpio_work_rise(struct work_struct *irq_work) +{ + struct pch_vbus_gpio_data *vbus_gpio = container_of(irq_work, + struct pch_vbus_gpio_data, irq_work_rise); + struct pch_udc_dev *dev = + container_of(vbus_gpio, struct pch_udc_dev, vbus_gpio); + int vbus; + + if (!dev->vbus_gpio.port) + return; + + mdelay(PCH_VBUS_INTERVAL); + vbus = pch_vbus_gpio_get_value(dev); + + if (vbus == 1) { + dev_dbg(&dev->pdev->dev, "VBUS rose"); + pch_udc_reconnect(dev); + return; + } +} + +/** + * pch_vbus_gpio_irq() - IRQ handler for GPIO intrerrupt for changing VBUS + * @irq: Interrupt request number + * @dev: Reference to the device structure + * + * Return codes: + * 0: Success + * -EINVAL: GPIO port is invalid or can't be initialized. + */ +static irqreturn_t pch_vbus_gpio_irq(int irq, void *data) +{ + struct pch_udc_dev *dev = (struct pch_udc_dev *)data; + + if (!dev->vbus_gpio.port || !dev->vbus_gpio.intr) + return IRQ_NONE; + + if (pch_vbus_gpio_get_value(dev)) + schedule_work(&dev->vbus_gpio.irq_work_rise); + else + schedule_work(&dev->vbus_gpio.irq_work_fall); + + return IRQ_HANDLED; +} + +/** * pch_vbus_gpio_init() - This API initializes GPIO port detecting VBUS. * @dev: Reference to the driver structure * @vbus_gpio Number of GPIO port to detect gpio @@ -1317,8 +1374,10 @@ static void pch_vbus_gpio_work_fall(struct work_struct *irq_work) static int pch_vbus_gpio_init(struct pch_udc_dev *dev, int vbus_gpio_port) { int err; + int irq_num = 0; dev->vbus_gpio.port = 0; + dev->vbus_gpio.intr = 0; if (vbus_gpio_port <= -1) return -EINVAL; @@ -1341,6 +1400,21 @@ static int pch_vbus_gpio_init(struct pch_udc_dev *dev, int vbus_gpio_port) gpio_direction_input(vbus_gpio_port); INIT_WORK(&dev->vbus_gpio.irq_work_fall, pch_vbus_gpio_work_fall); + irq_num = gpio_to_irq(vbus_gpio_port); + if (irq_num > 0) { + irq_set_irq_type(irq_num, IRQ_TYPE_EDGE_BOTH); + err = request_irq(irq_num, pch_vbus_gpio_irq, 0, + "vbus_detect", dev); + if (!err) { + dev->vbus_gpio.intr = irq_num; + INIT_WORK(&dev->vbus_gpio.irq_work_rise, + pch_vbus_gpio_work_rise); + } else { + pr_err("%s: can't request irq %d, err: %d\n", + __func__, irq_num, err); + } + } + return 0; } @@ -1350,6 +1424,9 @@ static int pch_vbus_gpio_init(struct pch_udc_dev *dev, int vbus_gpio_port) */ static void pch_vbus_gpio_free(struct pch_udc_dev *dev) { + if (dev->vbus_gpio.intr) + free_irq(dev->vbus_gpio.intr, dev); + if (dev->vbus_gpio.port) gpio_free(dev->vbus_gpio.port); } @@ -2676,7 +2753,8 @@ static void pch_udc_dev_isr(struct pch_udc_dev *dev, u32 dev_intr) } pch_udc_reconnect(dev); } else if ((dev->vbus_session == 0) - && (vbus == 1)) + && (vbus == 1) + && !dev->vbus_gpio.intr) schedule_work(&dev->vbus_gpio.irq_work_fall); dev_dbg(&dev->pdev->dev, "USB_SUSPEND\n"); @@ -2941,7 +3019,8 @@ static int pch_udc_start(struct usb_gadget_driver *driver, pch_udc_setup_ep0(dev); /* clear SD */ - pch_udc_clear_disconnect(dev); + if ((pch_vbus_gpio_get_value(dev) != 0) || !dev->vbus_gpio.intr) + pch_udc_clear_disconnect(dev); dev->connected = 1; return 0; -- cgit v0.10.2 From 609ca228073ae06c5513474d2cdf0af7ee5766ec Mon Sep 17 00:00:00 2001 From: Sebastian Andrzej Siewior Date: Mon, 6 Feb 2012 18:46:35 +0100 Subject: usb: gadget: clean the ep in autoconf before returning it. Since commit 72c973dd aka ("usb: gadget: add usb_endpoint_descriptor to struct usb_ep) the descriptor is part of the ep. Most gadgets like g_zero or masstorage call config_ep_by_speed() to grab an available endpoint which may be used for FS/HS/SS bulk/iso/intr and in a second they assign the proper descriptor by calling config_ep_by_speed(). This is good so far. A few of them like ncm call config_ep_by_speed() only if ep->desc not assigned earlier. That means ep->desc is never assigned if the endpoint was used by another gadget before it was removed. Some of those gadgets also assign ep->driver_data to NULL on reset or ep_disable part _but_ keep a reference to this endpoint. At ep_enable time they assign driver_data to their private data. This probably needs a clean up of its own. Signed-off-by: Sebastian Andrzej Siewior Signed-off-by: Felipe Balbi diff --git a/drivers/usb/gadget/epautoconf.c b/drivers/usb/gadget/epautoconf.c index 753aa06..f59f7e3 100644 --- a/drivers/usb/gadget/epautoconf.c +++ b/drivers/usb/gadget/epautoconf.c @@ -275,24 +275,24 @@ struct usb_ep *usb_ep_autoconfig_ss( /* ep-e, ep-f are PIO with only 64 byte fifos */ ep = find_ep (gadget, "ep-e"); if (ep && ep_matches(gadget, ep, desc, ep_comp)) - return ep; + goto found_ep; ep = find_ep (gadget, "ep-f"); if (ep && ep_matches(gadget, ep, desc, ep_comp)) - return ep; + goto found_ep; } else if (gadget_is_goku (gadget)) { if (USB_ENDPOINT_XFER_INT == type) { /* single buffering is enough */ ep = find_ep(gadget, "ep3-bulk"); if (ep && ep_matches(gadget, ep, desc, ep_comp)) - return ep; + goto found_ep; } else if (USB_ENDPOINT_XFER_BULK == type && (USB_DIR_IN & desc->bEndpointAddress)) { /* DMA may be available */ ep = find_ep(gadget, "ep2-bulk"); if (ep && ep_matches(gadget, ep, desc, ep_comp)) - return ep; + goto found_ep; } #ifdef CONFIG_BLACKFIN @@ -311,18 +311,22 @@ struct usb_ep *usb_ep_autoconfig_ss( } else ep = NULL; if (ep && ep_matches(gadget, ep, desc, ep_comp)) - return ep; + goto found_ep; #endif } /* Second, look at endpoints until an unclaimed one looks usable */ list_for_each_entry (ep, &gadget->ep_list, ep_list) { if (ep_matches(gadget, ep, desc, ep_comp)) - return ep; + goto found_ep; } /* Fail */ return NULL; +found_ep: + ep->desc = NULL; + ep->comp_desc = NULL; + return ep; } /** -- cgit v0.10.2 From 6fecfb05c077586ba85aa6f52f10abf30a4bfb40 Mon Sep 17 00:00:00 2001 From: Sebastian Andrzej Siewior Date: Mon, 6 Feb 2012 18:46:36 +0100 Subject: usb: gadget: add usb3.0 descriptors to serial gadgets This patch adds SS descriptors to the ACM & generic serial gadget. The ACM part was tested with minicom + dummy + send / receive files over ttyACM <=> ttyGS0. The generic serial part (f_serial) was not tested (haven't found a driver on the host side). The nokia & multi gadget use HS at most. Signed-off-by: Sebastian Andrzej Siewior Signed-off-by: Felipe Balbi diff --git a/drivers/usb/gadget/f_acm.c b/drivers/usb/gadget/f_acm.c index e482121..d672250a 100644 --- a/drivers/usb/gadget/f_acm.c +++ b/drivers/usb/gadget/f_acm.c @@ -237,6 +237,42 @@ static struct usb_descriptor_header *acm_hs_function[] = { NULL, }; +static struct usb_endpoint_descriptor acm_ss_in_desc = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + .bmAttributes = USB_ENDPOINT_XFER_BULK, + .wMaxPacketSize = cpu_to_le16(1024), +}; + +static struct usb_endpoint_descriptor acm_ss_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 acm_ss_bulk_comp_desc = { + .bLength = sizeof acm_ss_bulk_comp_desc, + .bDescriptorType = USB_DT_SS_ENDPOINT_COMP, +}; + +static struct usb_descriptor_header *acm_ss_function[] = { + (struct usb_descriptor_header *) &acm_iad_descriptor, + (struct usb_descriptor_header *) &acm_control_interface_desc, + (struct usb_descriptor_header *) &acm_header_desc, + (struct usb_descriptor_header *) &acm_call_mgmt_descriptor, + (struct usb_descriptor_header *) &acm_descriptor, + (struct usb_descriptor_header *) &acm_union_desc, + (struct usb_descriptor_header *) &acm_hs_notify_desc, + (struct usb_descriptor_header *) &acm_ss_bulk_comp_desc, + (struct usb_descriptor_header *) &acm_data_interface_desc, + (struct usb_descriptor_header *) &acm_ss_in_desc, + (struct usb_descriptor_header *) &acm_ss_bulk_comp_desc, + (struct usb_descriptor_header *) &acm_ss_out_desc, + (struct usb_descriptor_header *) &acm_ss_bulk_comp_desc, + NULL, +}; + /* string descriptors: */ #define ACM_CTRL_IDX 0 @@ -643,9 +679,21 @@ acm_bind(struct usb_configuration *c, struct usb_function *f) /* copy descriptors */ f->hs_descriptors = usb_copy_descriptors(acm_hs_function); } + if (gadget_is_superspeed(c->cdev->gadget)) { + acm_ss_in_desc.bEndpointAddress = + acm_fs_in_desc.bEndpointAddress; + acm_ss_out_desc.bEndpointAddress = + acm_fs_out_desc.bEndpointAddress; + + /* copy descriptors, and track endpoint copies */ + f->ss_descriptors = usb_copy_descriptors(acm_ss_function); + if (!f->ss_descriptors) + goto fail; + } DBG(cdev, "acm ttyGS%d: %s speed IN/%s OUT/%s NOTIFY/%s\n", acm->port_num, + gadget_is_superspeed(c->cdev->gadget) ? "super" : gadget_is_dualspeed(c->cdev->gadget) ? "dual" : "full", acm->port.in->name, acm->port.out->name, acm->notify->name); @@ -675,6 +723,8 @@ acm_unbind(struct usb_configuration *c, struct usb_function *f) if (gadget_is_dualspeed(c->cdev->gadget)) usb_free_descriptors(f->hs_descriptors); + if (gadget_is_superspeed(c->cdev->gadget)) + usb_free_descriptors(f->ss_descriptors); usb_free_descriptors(f->descriptors); gs_free_req(acm->notify, acm->notify_req); kfree(acm); diff --git a/drivers/usb/gadget/f_serial.c b/drivers/usb/gadget/f_serial.c index cf33a8d..07197d6 100644 --- a/drivers/usb/gadget/f_serial.c +++ b/drivers/usb/gadget/f_serial.c @@ -99,6 +99,34 @@ static struct usb_descriptor_header *gser_hs_function[] __initdata = { NULL, }; +static struct usb_endpoint_descriptor gser_ss_in_desc __initdata = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + .bmAttributes = USB_ENDPOINT_XFER_BULK, + .wMaxPacketSize = cpu_to_le16(1024), +}; + +static struct usb_endpoint_descriptor gser_ss_out_desc __initdata = { + .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 gser_ss_bulk_comp_desc __initdata = { + .bLength = sizeof gser_ss_bulk_comp_desc, + .bDescriptorType = USB_DT_SS_ENDPOINT_COMP, +}; + +static struct usb_descriptor_header *gser_ss_function[] __initdata = { + (struct usb_descriptor_header *) &gser_interface_desc, + (struct usb_descriptor_header *) &gser_ss_in_desc, + (struct usb_descriptor_header *) &gser_ss_bulk_comp_desc, + (struct usb_descriptor_header *) &gser_ss_out_desc, + (struct usb_descriptor_header *) &gser_ss_bulk_comp_desc, + NULL, +}; + /* string descriptors: */ static struct usb_string gser_string_defs[] = { @@ -201,9 +229,21 @@ gser_bind(struct usb_configuration *c, struct usb_function *f) /* copy descriptors, and track endpoint copies */ f->hs_descriptors = usb_copy_descriptors(gser_hs_function); } + if (gadget_is_superspeed(c->cdev->gadget)) { + gser_ss_in_desc.bEndpointAddress = + gser_fs_in_desc.bEndpointAddress; + gser_ss_out_desc.bEndpointAddress = + gser_fs_out_desc.bEndpointAddress; + + /* copy descriptors, and track endpoint copies */ + f->ss_descriptors = usb_copy_descriptors(gser_ss_function); + if (!f->ss_descriptors) + goto fail; + } DBG(cdev, "generic ttyGS%d: %s speed IN/%s OUT/%s\n", gser->port_num, + gadget_is_superspeed(c->cdev->gadget) ? "super" : gadget_is_dualspeed(c->cdev->gadget) ? "dual" : "full", gser->port.in->name, gser->port.out->name); return 0; @@ -225,6 +265,8 @@ gser_unbind(struct usb_configuration *c, struct usb_function *f) { if (gadget_is_dualspeed(c->cdev->gadget)) usb_free_descriptors(f->hs_descriptors); + if (gadget_is_superspeed(c->cdev->gadget)) + usb_free_descriptors(f->ss_descriptors); usb_free_descriptors(f->descriptors); kfree(func_to_gser(f)); } diff --git a/drivers/usb/gadget/serial.c b/drivers/usb/gadget/serial.c index ad9e5b2..665c074 100644 --- a/drivers/usb/gadget/serial.c +++ b/drivers/usb/gadget/serial.c @@ -242,7 +242,7 @@ static struct usb_composite_driver gserial_driver = { .name = "g_serial", .dev = &device_desc, .strings = dev_strings, - .max_speed = USB_SPEED_HIGH, + .max_speed = USB_SPEED_SUPER, }; static int __init init(void) -- cgit v0.10.2 From d5261286949fc9ada701c7e30bf89e08a6dbf4de Mon Sep 17 00:00:00 2001 From: Guennadi Liakhovetski Date: Tue, 14 Feb 2012 11:37:17 +0100 Subject: usb: renesas_usbhs: (cosmetic) simplify list operations list.h already provide helpers to find the first entry and to move list nodes to the tail of another list. This patch simply uses those helpers, no functional changes. Signed-off-by: Guennadi Liakhovetski Signed-off-by: Felipe Balbi diff --git a/drivers/usb/renesas_usbhs/fifo.c b/drivers/usb/renesas_usbhs/fifo.c index 03a9cc5..fb2a88c 100644 --- a/drivers/usb/renesas_usbhs/fifo.c +++ b/drivers/usb/renesas_usbhs/fifo.c @@ -76,8 +76,7 @@ void usbhs_pkt_push(struct usbhs_pipe *pipe, struct usbhs_pkt *pkt, pipe->handler = &usbhsf_null_handler; } - list_del_init(&pkt->node); - list_add_tail(&pkt->node, &pipe->list); + list_move_tail(&pkt->node, &pipe->list); /* * each pkt must hold own handler. @@ -107,7 +106,7 @@ static struct usbhs_pkt *__usbhsf_pkt_get(struct usbhs_pipe *pipe) if (list_empty(&pipe->list)) return NULL; - return list_entry(pipe->list.next, struct usbhs_pkt, node); + return list_first_entry(&pipe->list, struct usbhs_pkt, node); } struct usbhs_pkt *usbhs_pkt_pop(struct usbhs_pipe *pipe, struct usbhs_pkt *pkt) -- cgit v0.10.2 From 6e4b74e4690dd03b5664fa4895c3db0607d64742 Mon Sep 17 00:00:00 2001 From: Guennadi Liakhovetski Date: Tue, 14 Feb 2012 11:37:21 +0100 Subject: usb: renesas: fix scheduling in atomic context bug The current renesas_usbhs driver triggers BUG: scheduling while atomic: ksoftirqd/0/3/0x00000102 with enabled CONFIG_DEBUG_ATOMIC_SLEEP, by submitting DMA transfers from an atomic (tasklet) context, which is not supported by the shdma dmaengine driver. Fix it by switching to a work. Signed-off-by: Guennadi Liakhovetski Signed-off-by: Felipe Balbi diff --git a/drivers/usb/renesas_usbhs/fifo.c b/drivers/usb/renesas_usbhs/fifo.c index fb2a88c..3648c82 100644 --- a/drivers/usb/renesas_usbhs/fifo.c +++ b/drivers/usb/renesas_usbhs/fifo.c @@ -765,9 +765,9 @@ static int __usbhsf_dma_map_ctrl(struct usbhs_pkt *pkt, int map) } static void usbhsf_dma_complete(void *arg); -static void usbhsf_dma_prepare_tasklet(unsigned long data) +static void xfer_work(struct work_struct *work) { - struct usbhs_pkt *pkt = (struct usbhs_pkt *)data; + struct usbhs_pkt *pkt = container_of(work, struct usbhs_pkt, work); struct usbhs_pipe *pipe = pkt->pipe; struct usbhs_fifo *fifo = usbhs_pipe_to_fifo(pipe); struct usbhs_priv *priv = usbhs_pipe_to_priv(pipe); @@ -847,11 +847,8 @@ static int usbhsf_dma_prepare_push(struct usbhs_pkt *pkt, int *is_done) pkt->trans = len; - tasklet_init(&fifo->tasklet, - usbhsf_dma_prepare_tasklet, - (unsigned long)pkt); - - tasklet_schedule(&fifo->tasklet); + INIT_WORK(&pkt->work, xfer_work); + schedule_work(&pkt->work); return 0; @@ -941,11 +938,8 @@ static int usbhsf_dma_try_pop(struct usbhs_pkt *pkt, int *is_done) pkt->trans = len; - tasklet_init(&fifo->tasklet, - usbhsf_dma_prepare_tasklet, - (unsigned long)pkt); - - tasklet_schedule(&fifo->tasklet); + INIT_WORK(&pkt->work, xfer_work); + schedule_work(&pkt->work); return 0; diff --git a/drivers/usb/renesas_usbhs/fifo.h b/drivers/usb/renesas_usbhs/fifo.h index f68609c..c31731a 100644 --- a/drivers/usb/renesas_usbhs/fifo.h +++ b/drivers/usb/renesas_usbhs/fifo.h @@ -19,6 +19,7 @@ #include #include +#include #include #include "pipe.h" @@ -31,7 +32,6 @@ struct usbhs_fifo { u32 ctr; /* xFIFOCTR */ struct usbhs_pipe *pipe; - struct tasklet_struct tasklet; struct dma_chan *tx_chan; struct dma_chan *rx_chan; @@ -53,6 +53,7 @@ struct usbhs_pkt { struct usbhs_pkt_handle *handler; void (*done)(struct usbhs_priv *priv, struct usbhs_pkt *pkt); + struct work_struct work; dma_addr_t dma; void *buf; int length; -- cgit v0.10.2 From 18b5b3b55f44c28a0380f3559d1db3c467f94b4a Mon Sep 17 00:00:00 2001 From: Jassi Brar Date: Thu, 2 Feb 2012 21:59:53 +0530 Subject: usb: gadget: Rename audio function to uac1 The extant USB-Audio function driver complies to UAC_1 spec. So name the files accordingly, paving way for inclusion of a new UAC_2 specified driver. Signed-off-by: Yadi Brar Signed-off-by: Jassi Brar Signed-off-by: Felipe Balbi diff --git a/drivers/usb/gadget/audio.c b/drivers/usb/gadget/audio.c index 9d89ae47..056a2d9 100644 --- a/drivers/usb/gadget/audio.c +++ b/drivers/usb/gadget/audio.c @@ -14,7 +14,7 @@ #include #include -#include "u_audio.h" +#include "u_uac1.h" #define DRIVER_DESC "Linux USB Audio Gadget" #define DRIVER_VERSION "Dec 18, 2008" @@ -33,8 +33,8 @@ #include "config.c" #include "epautoconf.c" -#include "u_audio.c" -#include "f_audio.c" +#include "u_uac1.c" +#include "f_uac1.c" /*-------------------------------------------------------------------------*/ diff --git a/drivers/usb/gadget/f_audio.c b/drivers/usb/gadget/f_audio.c deleted file mode 100644 index ec7ffcd..0000000 --- a/drivers/usb/gadget/f_audio.c +++ /dev/null @@ -1,799 +0,0 @@ -/* - * f_audio.c -- USB Audio class function driver - * - * Copyright (C) 2008 Bryan Wu - * Copyright (C) 2008 Analog Devices, Inc - * - * Enter bugs at http://blackfin.uclinux.org/ - * - * Licensed under the GPL-2 or later. - */ - -#include -#include -#include -#include - -#include "u_audio.h" - -#define OUT_EP_MAX_PACKET_SIZE 200 -static int req_buf_size = OUT_EP_MAX_PACKET_SIZE; -module_param(req_buf_size, int, S_IRUGO); -MODULE_PARM_DESC(req_buf_size, "ISO OUT endpoint request buffer size"); - -static int req_count = 256; -module_param(req_count, int, S_IRUGO); -MODULE_PARM_DESC(req_count, "ISO OUT endpoint request count"); - -static int audio_buf_size = 48000; -module_param(audio_buf_size, int, S_IRUGO); -MODULE_PARM_DESC(audio_buf_size, "Audio buffer size"); - -static int generic_set_cmd(struct usb_audio_control *con, u8 cmd, int value); -static int generic_get_cmd(struct usb_audio_control *con, u8 cmd); - -/* - * DESCRIPTORS ... most are static, but strings and full - * configuration descriptors are built on demand. - */ - -/* - * We have two interfaces- AudioControl and AudioStreaming - * TODO: only supcard playback currently - */ -#define F_AUDIO_AC_INTERFACE 0 -#define F_AUDIO_AS_INTERFACE 1 -#define F_AUDIO_NUM_INTERFACES 2 - -/* B.3.1 Standard AC Interface Descriptor */ -static struct usb_interface_descriptor ac_interface_desc __initdata = { - .bLength = USB_DT_INTERFACE_SIZE, - .bDescriptorType = USB_DT_INTERFACE, - .bNumEndpoints = 0, - .bInterfaceClass = USB_CLASS_AUDIO, - .bInterfaceSubClass = USB_SUBCLASS_AUDIOCONTROL, -}; - -DECLARE_UAC_AC_HEADER_DESCRIPTOR(2); - -#define UAC_DT_AC_HEADER_LENGTH UAC_DT_AC_HEADER_SIZE(F_AUDIO_NUM_INTERFACES) -/* 1 input terminal, 1 output terminal and 1 feature unit */ -#define UAC_DT_TOTAL_LENGTH (UAC_DT_AC_HEADER_LENGTH + UAC_DT_INPUT_TERMINAL_SIZE \ - + UAC_DT_OUTPUT_TERMINAL_SIZE + UAC_DT_FEATURE_UNIT_SIZE(0)) -/* B.3.2 Class-Specific AC Interface Descriptor */ -static struct uac1_ac_header_descriptor_2 ac_header_desc = { - .bLength = UAC_DT_AC_HEADER_LENGTH, - .bDescriptorType = USB_DT_CS_INTERFACE, - .bDescriptorSubtype = UAC_HEADER, - .bcdADC = __constant_cpu_to_le16(0x0100), - .wTotalLength = __constant_cpu_to_le16(UAC_DT_TOTAL_LENGTH), - .bInCollection = F_AUDIO_NUM_INTERFACES, - .baInterfaceNr = { - [0] = F_AUDIO_AC_INTERFACE, - [1] = F_AUDIO_AS_INTERFACE, - } -}; - -#define INPUT_TERMINAL_ID 1 -static struct uac_input_terminal_descriptor input_terminal_desc = { - .bLength = UAC_DT_INPUT_TERMINAL_SIZE, - .bDescriptorType = USB_DT_CS_INTERFACE, - .bDescriptorSubtype = UAC_INPUT_TERMINAL, - .bTerminalID = INPUT_TERMINAL_ID, - .wTerminalType = UAC_TERMINAL_STREAMING, - .bAssocTerminal = 0, - .wChannelConfig = 0x3, -}; - -DECLARE_UAC_FEATURE_UNIT_DESCRIPTOR(0); - -#define FEATURE_UNIT_ID 2 -static struct uac_feature_unit_descriptor_0 feature_unit_desc = { - .bLength = UAC_DT_FEATURE_UNIT_SIZE(0), - .bDescriptorType = USB_DT_CS_INTERFACE, - .bDescriptorSubtype = UAC_FEATURE_UNIT, - .bUnitID = FEATURE_UNIT_ID, - .bSourceID = INPUT_TERMINAL_ID, - .bControlSize = 2, - .bmaControls[0] = (UAC_FU_MUTE | UAC_FU_VOLUME), -}; - -static struct usb_audio_control mute_control = { - .list = LIST_HEAD_INIT(mute_control.list), - .name = "Mute Control", - .type = UAC_FU_MUTE, - /* Todo: add real Mute control code */ - .set = generic_set_cmd, - .get = generic_get_cmd, -}; - -static struct usb_audio_control volume_control = { - .list = LIST_HEAD_INIT(volume_control.list), - .name = "Volume Control", - .type = UAC_FU_VOLUME, - /* Todo: add real Volume control code */ - .set = generic_set_cmd, - .get = generic_get_cmd, -}; - -static struct usb_audio_control_selector feature_unit = { - .list = LIST_HEAD_INIT(feature_unit.list), - .id = FEATURE_UNIT_ID, - .name = "Mute & Volume Control", - .type = UAC_FEATURE_UNIT, - .desc = (struct usb_descriptor_header *)&feature_unit_desc, -}; - -#define OUTPUT_TERMINAL_ID 3 -static struct uac1_output_terminal_descriptor output_terminal_desc = { - .bLength = UAC_DT_OUTPUT_TERMINAL_SIZE, - .bDescriptorType = USB_DT_CS_INTERFACE, - .bDescriptorSubtype = UAC_OUTPUT_TERMINAL, - .bTerminalID = OUTPUT_TERMINAL_ID, - .wTerminalType = UAC_OUTPUT_TERMINAL_SPEAKER, - .bAssocTerminal = FEATURE_UNIT_ID, - .bSourceID = FEATURE_UNIT_ID, -}; - -/* B.4.1 Standard AS Interface Descriptor */ -static struct usb_interface_descriptor as_interface_alt_0_desc = { - .bLength = USB_DT_INTERFACE_SIZE, - .bDescriptorType = USB_DT_INTERFACE, - .bAlternateSetting = 0, - .bNumEndpoints = 0, - .bInterfaceClass = USB_CLASS_AUDIO, - .bInterfaceSubClass = USB_SUBCLASS_AUDIOSTREAMING, -}; - -static struct usb_interface_descriptor as_interface_alt_1_desc = { - .bLength = USB_DT_INTERFACE_SIZE, - .bDescriptorType = USB_DT_INTERFACE, - .bAlternateSetting = 1, - .bNumEndpoints = 1, - .bInterfaceClass = USB_CLASS_AUDIO, - .bInterfaceSubClass = USB_SUBCLASS_AUDIOSTREAMING, -}; - -/* B.4.2 Class-Specific AS Interface Descriptor */ -static struct uac1_as_header_descriptor as_header_desc = { - .bLength = UAC_DT_AS_HEADER_SIZE, - .bDescriptorType = USB_DT_CS_INTERFACE, - .bDescriptorSubtype = UAC_AS_GENERAL, - .bTerminalLink = INPUT_TERMINAL_ID, - .bDelay = 1, - .wFormatTag = UAC_FORMAT_TYPE_I_PCM, -}; - -DECLARE_UAC_FORMAT_TYPE_I_DISCRETE_DESC(1); - -static struct uac_format_type_i_discrete_descriptor_1 as_type_i_desc = { - .bLength = UAC_FORMAT_TYPE_I_DISCRETE_DESC_SIZE(1), - .bDescriptorType = USB_DT_CS_INTERFACE, - .bDescriptorSubtype = UAC_FORMAT_TYPE, - .bFormatType = UAC_FORMAT_TYPE_I, - .bSubframeSize = 2, - .bBitResolution = 16, - .bSamFreqType = 1, -}; - -/* Standard ISO OUT Endpoint Descriptor */ -static struct usb_endpoint_descriptor as_out_ep_desc = { - .bLength = USB_DT_ENDPOINT_AUDIO_SIZE, - .bDescriptorType = USB_DT_ENDPOINT, - .bEndpointAddress = USB_DIR_OUT, - .bmAttributes = USB_ENDPOINT_SYNC_ADAPTIVE - | USB_ENDPOINT_XFER_ISOC, - .wMaxPacketSize = __constant_cpu_to_le16(OUT_EP_MAX_PACKET_SIZE), - .bInterval = 4, -}; - -/* Class-specific AS ISO OUT Endpoint Descriptor */ -static struct uac_iso_endpoint_descriptor as_iso_out_desc __initdata = { - .bLength = UAC_ISO_ENDPOINT_DESC_SIZE, - .bDescriptorType = USB_DT_CS_ENDPOINT, - .bDescriptorSubtype = UAC_EP_GENERAL, - .bmAttributes = 1, - .bLockDelayUnits = 1, - .wLockDelay = __constant_cpu_to_le16(1), -}; - -static struct usb_descriptor_header *f_audio_desc[] __initdata = { - (struct usb_descriptor_header *)&ac_interface_desc, - (struct usb_descriptor_header *)&ac_header_desc, - - (struct usb_descriptor_header *)&input_terminal_desc, - (struct usb_descriptor_header *)&output_terminal_desc, - (struct usb_descriptor_header *)&feature_unit_desc, - - (struct usb_descriptor_header *)&as_interface_alt_0_desc, - (struct usb_descriptor_header *)&as_interface_alt_1_desc, - (struct usb_descriptor_header *)&as_header_desc, - - (struct usb_descriptor_header *)&as_type_i_desc, - - (struct usb_descriptor_header *)&as_out_ep_desc, - (struct usb_descriptor_header *)&as_iso_out_desc, - NULL, -}; - -/* string IDs are assigned dynamically */ - -#define STRING_MANUFACTURER_IDX 0 -#define STRING_PRODUCT_IDX 1 - -static char manufacturer[50]; - -static struct usb_string strings_dev[] = { - [STRING_MANUFACTURER_IDX].s = manufacturer, - [STRING_PRODUCT_IDX].s = DRIVER_DESC, - { } /* end of list */ -}; - -static struct usb_gadget_strings stringtab_dev = { - .language = 0x0409, /* en-us */ - .strings = strings_dev, -}; - -static struct usb_gadget_strings *audio_strings[] = { - &stringtab_dev, - NULL, -}; - -/* - * This function is an ALSA sound card following USB Audio Class Spec 1.0. - */ - -/*-------------------------------------------------------------------------*/ -struct f_audio_buf { - u8 *buf; - int actual; - struct list_head list; -}; - -static struct f_audio_buf *f_audio_buffer_alloc(int buf_size) -{ - struct f_audio_buf *copy_buf; - - copy_buf = kzalloc(sizeof *copy_buf, GFP_ATOMIC); - if (!copy_buf) - return ERR_PTR(-ENOMEM); - - copy_buf->buf = kzalloc(buf_size, GFP_ATOMIC); - if (!copy_buf->buf) { - kfree(copy_buf); - return ERR_PTR(-ENOMEM); - } - - return copy_buf; -} - -static void f_audio_buffer_free(struct f_audio_buf *audio_buf) -{ - kfree(audio_buf->buf); - kfree(audio_buf); -} -/*-------------------------------------------------------------------------*/ - -struct f_audio { - struct gaudio card; - - /* endpoints handle full and/or high speeds */ - struct usb_ep *out_ep; - - spinlock_t lock; - struct f_audio_buf *copy_buf; - struct work_struct playback_work; - struct list_head play_queue; - - /* Control Set command */ - struct list_head cs; - u8 set_cmd; - struct usb_audio_control *set_con; -}; - -static inline struct f_audio *func_to_audio(struct usb_function *f) -{ - return container_of(f, struct f_audio, card.func); -} - -/*-------------------------------------------------------------------------*/ - -static void f_audio_playback_work(struct work_struct *data) -{ - struct f_audio *audio = container_of(data, struct f_audio, - playback_work); - struct f_audio_buf *play_buf; - - spin_lock_irq(&audio->lock); - if (list_empty(&audio->play_queue)) { - spin_unlock_irq(&audio->lock); - return; - } - play_buf = list_first_entry(&audio->play_queue, - struct f_audio_buf, list); - list_del(&play_buf->list); - spin_unlock_irq(&audio->lock); - - u_audio_playback(&audio->card, play_buf->buf, play_buf->actual); - f_audio_buffer_free(play_buf); -} - -static int f_audio_out_ep_complete(struct usb_ep *ep, struct usb_request *req) -{ - struct f_audio *audio = req->context; - struct usb_composite_dev *cdev = audio->card.func.config->cdev; - struct f_audio_buf *copy_buf = audio->copy_buf; - int err; - - if (!copy_buf) - return -EINVAL; - - /* Copy buffer is full, add it to the play_queue */ - if (audio_buf_size - copy_buf->actual < req->actual) { - list_add_tail(©_buf->list, &audio->play_queue); - schedule_work(&audio->playback_work); - copy_buf = f_audio_buffer_alloc(audio_buf_size); - if (IS_ERR(copy_buf)) - return -ENOMEM; - } - - memcpy(copy_buf->buf + copy_buf->actual, req->buf, req->actual); - copy_buf->actual += req->actual; - audio->copy_buf = copy_buf; - - err = usb_ep_queue(ep, req, GFP_ATOMIC); - if (err) - ERROR(cdev, "%s queue req: %d\n", ep->name, err); - - return 0; - -} - -static void f_audio_complete(struct usb_ep *ep, struct usb_request *req) -{ - struct f_audio *audio = req->context; - int status = req->status; - u32 data = 0; - struct usb_ep *out_ep = audio->out_ep; - - switch (status) { - - case 0: /* normal completion? */ - if (ep == out_ep) - f_audio_out_ep_complete(ep, req); - else if (audio->set_con) { - memcpy(&data, req->buf, req->length); - audio->set_con->set(audio->set_con, audio->set_cmd, - le16_to_cpu(data)); - audio->set_con = NULL; - } - break; - default: - break; - } -} - -static int audio_set_intf_req(struct usb_function *f, - const struct usb_ctrlrequest *ctrl) -{ - struct f_audio *audio = func_to_audio(f); - struct usb_composite_dev *cdev = f->config->cdev; - struct usb_request *req = cdev->req; - u8 id = ((le16_to_cpu(ctrl->wIndex) >> 8) & 0xFF); - u16 len = le16_to_cpu(ctrl->wLength); - u16 w_value = le16_to_cpu(ctrl->wValue); - u8 con_sel = (w_value >> 8) & 0xFF; - u8 cmd = (ctrl->bRequest & 0x0F); - struct usb_audio_control_selector *cs; - struct usb_audio_control *con; - - DBG(cdev, "bRequest 0x%x, w_value 0x%04x, len %d, entity %d\n", - ctrl->bRequest, w_value, len, id); - - list_for_each_entry(cs, &audio->cs, list) { - if (cs->id == id) { - list_for_each_entry(con, &cs->control, list) { - if (con->type == con_sel) { - audio->set_con = con; - break; - } - } - break; - } - } - - audio->set_cmd = cmd; - req->context = audio; - req->complete = f_audio_complete; - - return len; -} - -static int audio_get_intf_req(struct usb_function *f, - const struct usb_ctrlrequest *ctrl) -{ - struct f_audio *audio = func_to_audio(f); - struct usb_composite_dev *cdev = f->config->cdev; - struct usb_request *req = cdev->req; - int value = -EOPNOTSUPP; - u8 id = ((le16_to_cpu(ctrl->wIndex) >> 8) & 0xFF); - u16 len = le16_to_cpu(ctrl->wLength); - u16 w_value = le16_to_cpu(ctrl->wValue); - u8 con_sel = (w_value >> 8) & 0xFF; - u8 cmd = (ctrl->bRequest & 0x0F); - struct usb_audio_control_selector *cs; - struct usb_audio_control *con; - - DBG(cdev, "bRequest 0x%x, w_value 0x%04x, len %d, entity %d\n", - ctrl->bRequest, w_value, len, id); - - list_for_each_entry(cs, &audio->cs, list) { - if (cs->id == id) { - list_for_each_entry(con, &cs->control, list) { - if (con->type == con_sel && con->get) { - value = con->get(con, cmd); - break; - } - } - break; - } - } - - req->context = audio; - req->complete = f_audio_complete; - memcpy(req->buf, &value, len); - - return len; -} - -static int audio_set_endpoint_req(struct usb_function *f, - const struct usb_ctrlrequest *ctrl) -{ - struct usb_composite_dev *cdev = f->config->cdev; - int value = -EOPNOTSUPP; - u16 ep = le16_to_cpu(ctrl->wIndex); - u16 len = le16_to_cpu(ctrl->wLength); - u16 w_value = le16_to_cpu(ctrl->wValue); - - DBG(cdev, "bRequest 0x%x, w_value 0x%04x, len %d, endpoint %d\n", - ctrl->bRequest, w_value, len, ep); - - switch (ctrl->bRequest) { - case UAC_SET_CUR: - value = len; - break; - - case UAC_SET_MIN: - break; - - case UAC_SET_MAX: - break; - - case UAC_SET_RES: - break; - - case UAC_SET_MEM: - break; - - default: - break; - } - - return value; -} - -static int audio_get_endpoint_req(struct usb_function *f, - const struct usb_ctrlrequest *ctrl) -{ - struct usb_composite_dev *cdev = f->config->cdev; - int value = -EOPNOTSUPP; - u8 ep = ((le16_to_cpu(ctrl->wIndex) >> 8) & 0xFF); - u16 len = le16_to_cpu(ctrl->wLength); - u16 w_value = le16_to_cpu(ctrl->wValue); - - DBG(cdev, "bRequest 0x%x, w_value 0x%04x, len %d, endpoint %d\n", - ctrl->bRequest, w_value, len, ep); - - switch (ctrl->bRequest) { - case UAC_GET_CUR: - case UAC_GET_MIN: - case UAC_GET_MAX: - case UAC_GET_RES: - value = len; - break; - case UAC_GET_MEM: - break; - default: - break; - } - - return value; -} - -static int -f_audio_setup(struct usb_function *f, const struct usb_ctrlrequest *ctrl) -{ - struct usb_composite_dev *cdev = f->config->cdev; - struct usb_request *req = cdev->req; - int value = -EOPNOTSUPP; - u16 w_index = le16_to_cpu(ctrl->wIndex); - u16 w_value = le16_to_cpu(ctrl->wValue); - u16 w_length = le16_to_cpu(ctrl->wLength); - - /* composite driver infrastructure handles everything; interface - * activation uses set_alt(). - */ - switch (ctrl->bRequestType) { - case USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE: - value = audio_set_intf_req(f, ctrl); - break; - - case USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE: - value = audio_get_intf_req(f, ctrl); - break; - - case USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_ENDPOINT: - value = audio_set_endpoint_req(f, ctrl); - break; - - case USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_ENDPOINT: - value = audio_get_endpoint_req(f, ctrl); - break; - - default: - ERROR(cdev, "invalid control req%02x.%02x v%04x i%04x l%d\n", - ctrl->bRequestType, ctrl->bRequest, - w_value, w_index, w_length); - } - - /* respond with data transfer or status phase? */ - if (value >= 0) { - DBG(cdev, "audio req%02x.%02x v%04x i%04x l%d\n", - ctrl->bRequestType, ctrl->bRequest, - w_value, w_index, w_length); - req->zero = 0; - req->length = value; - value = usb_ep_queue(cdev->gadget->ep0, req, GFP_ATOMIC); - if (value < 0) - ERROR(cdev, "audio response on err %d\n", value); - } - - /* device either stalls (value < 0) or reports success */ - return value; -} - -static int f_audio_set_alt(struct usb_function *f, unsigned intf, unsigned alt) -{ - struct f_audio *audio = func_to_audio(f); - struct usb_composite_dev *cdev = f->config->cdev; - struct usb_ep *out_ep = audio->out_ep; - struct usb_request *req; - int i = 0, err = 0; - - DBG(cdev, "intf %d, alt %d\n", intf, alt); - - if (intf == 1) { - if (alt == 1) { - usb_ep_enable(out_ep); - out_ep->driver_data = audio; - audio->copy_buf = f_audio_buffer_alloc(audio_buf_size); - if (IS_ERR(audio->copy_buf)) - return -ENOMEM; - - /* - * allocate a bunch of read buffers - * and queue them all at once. - */ - for (i = 0; i < req_count && err == 0; i++) { - req = usb_ep_alloc_request(out_ep, GFP_ATOMIC); - if (req) { - req->buf = kzalloc(req_buf_size, - GFP_ATOMIC); - if (req->buf) { - req->length = req_buf_size; - req->context = audio; - req->complete = - f_audio_complete; - err = usb_ep_queue(out_ep, - req, GFP_ATOMIC); - if (err) - ERROR(cdev, - "%s queue req: %d\n", - out_ep->name, err); - } else - err = -ENOMEM; - } else - err = -ENOMEM; - } - - } else { - struct f_audio_buf *copy_buf = audio->copy_buf; - if (copy_buf) { - list_add_tail(©_buf->list, - &audio->play_queue); - schedule_work(&audio->playback_work); - } - } - } - - return err; -} - -static void f_audio_disable(struct usb_function *f) -{ - return; -} - -/*-------------------------------------------------------------------------*/ - -static void f_audio_build_desc(struct f_audio *audio) -{ - struct gaudio *card = &audio->card; - u8 *sam_freq; - int rate; - - /* Set channel numbers */ - input_terminal_desc.bNrChannels = u_audio_get_playback_channels(card); - as_type_i_desc.bNrChannels = u_audio_get_playback_channels(card); - - /* Set sample rates */ - rate = u_audio_get_playback_rate(card); - sam_freq = as_type_i_desc.tSamFreq[0]; - memcpy(sam_freq, &rate, 3); - - /* Todo: Set Sample bits and other parameters */ - - return; -} - -/* audio function driver setup/binding */ -static int __init -f_audio_bind(struct usb_configuration *c, struct usb_function *f) -{ - struct usb_composite_dev *cdev = c->cdev; - struct f_audio *audio = func_to_audio(f); - int status; - struct usb_ep *ep; - - f_audio_build_desc(audio); - - /* allocate instance-specific interface IDs, and patch descriptors */ - status = usb_interface_id(c, f); - if (status < 0) - goto fail; - ac_interface_desc.bInterfaceNumber = status; - - status = usb_interface_id(c, f); - if (status < 0) - goto fail; - as_interface_alt_0_desc.bInterfaceNumber = status; - as_interface_alt_1_desc.bInterfaceNumber = status; - - status = -ENODEV; - - /* allocate instance-specific endpoints */ - ep = usb_ep_autoconfig(cdev->gadget, &as_out_ep_desc); - if (!ep) - goto fail; - audio->out_ep = ep; - audio->out_ep->desc = &as_out_ep_desc; - ep->driver_data = cdev; /* claim */ - - status = -ENOMEM; - - /* copy descriptors, and track endpoint copies */ - f->descriptors = usb_copy_descriptors(f_audio_desc); - - /* - * support all relevant hardware speeds... we expect that when - * hardware is dual speed, all bulk-capable endpoints work at - * both speeds - */ - if (gadget_is_dualspeed(c->cdev->gadget)) { - c->highspeed = true; - f->hs_descriptors = usb_copy_descriptors(f_audio_desc); - } - - return 0; - -fail: - - return status; -} - -static void -f_audio_unbind(struct usb_configuration *c, struct usb_function *f) -{ - struct f_audio *audio = func_to_audio(f); - - usb_free_descriptors(f->descriptors); - usb_free_descriptors(f->hs_descriptors); - kfree(audio); -} - -/*-------------------------------------------------------------------------*/ - -static int generic_set_cmd(struct usb_audio_control *con, u8 cmd, int value) -{ - con->data[cmd] = value; - - return 0; -} - -static int generic_get_cmd(struct usb_audio_control *con, u8 cmd) -{ - return con->data[cmd]; -} - -/* Todo: add more control selecotor dynamically */ -int __init control_selector_init(struct f_audio *audio) -{ - INIT_LIST_HEAD(&audio->cs); - list_add(&feature_unit.list, &audio->cs); - - INIT_LIST_HEAD(&feature_unit.control); - list_add(&mute_control.list, &feature_unit.control); - list_add(&volume_control.list, &feature_unit.control); - - volume_control.data[UAC__CUR] = 0xffc0; - volume_control.data[UAC__MIN] = 0xe3a0; - volume_control.data[UAC__MAX] = 0xfff0; - volume_control.data[UAC__RES] = 0x0030; - - return 0; -} - -/** - * audio_bind_config - add USB audio function to a configuration - * @c: the configuration to supcard the USB audio function - * Context: single threaded during gadget setup - * - * Returns zero on success, else negative errno. - */ -int __init audio_bind_config(struct usb_configuration *c) -{ - struct f_audio *audio; - int status; - - /* allocate and initialize one new instance */ - audio = kzalloc(sizeof *audio, GFP_KERNEL); - if (!audio) - return -ENOMEM; - - audio->card.func.name = "g_audio"; - audio->card.gadget = c->cdev->gadget; - - INIT_LIST_HEAD(&audio->play_queue); - spin_lock_init(&audio->lock); - - /* set up ASLA audio devices */ - status = gaudio_setup(&audio->card); - if (status < 0) - goto setup_fail; - - audio->card.func.strings = audio_strings; - audio->card.func.bind = f_audio_bind; - audio->card.func.unbind = f_audio_unbind; - audio->card.func.set_alt = f_audio_set_alt; - audio->card.func.setup = f_audio_setup; - audio->card.func.disable = f_audio_disable; - - control_selector_init(audio); - - INIT_WORK(&audio->playback_work, f_audio_playback_work); - - status = usb_add_function(c, &audio->card.func); - if (status) - goto add_fail; - - INFO(c->cdev, "audio_buf_size %d, req_buf_size %d, req_count %d\n", - audio_buf_size, req_buf_size, req_count); - - return status; - -add_fail: - gaudio_cleanup(); -setup_fail: - kfree(audio); - return status; -} diff --git a/drivers/usb/gadget/f_uac1.c b/drivers/usb/gadget/f_uac1.c new file mode 100644 index 0000000..e4c060f --- /dev/null +++ b/drivers/usb/gadget/f_uac1.c @@ -0,0 +1,799 @@ +/* + * f_audio.c -- USB Audio class function driver + * + * Copyright (C) 2008 Bryan Wu + * Copyright (C) 2008 Analog Devices, Inc + * + * Enter bugs at http://blackfin.uclinux.org/ + * + * Licensed under the GPL-2 or later. + */ + +#include +#include +#include +#include + +#include "u_uac1.h" + +#define OUT_EP_MAX_PACKET_SIZE 200 +static int req_buf_size = OUT_EP_MAX_PACKET_SIZE; +module_param(req_buf_size, int, S_IRUGO); +MODULE_PARM_DESC(req_buf_size, "ISO OUT endpoint request buffer size"); + +static int req_count = 256; +module_param(req_count, int, S_IRUGO); +MODULE_PARM_DESC(req_count, "ISO OUT endpoint request count"); + +static int audio_buf_size = 48000; +module_param(audio_buf_size, int, S_IRUGO); +MODULE_PARM_DESC(audio_buf_size, "Audio buffer size"); + +static int generic_set_cmd(struct usb_audio_control *con, u8 cmd, int value); +static int generic_get_cmd(struct usb_audio_control *con, u8 cmd); + +/* + * DESCRIPTORS ... most are static, but strings and full + * configuration descriptors are built on demand. + */ + +/* + * We have two interfaces- AudioControl and AudioStreaming + * TODO: only supcard playback currently + */ +#define F_AUDIO_AC_INTERFACE 0 +#define F_AUDIO_AS_INTERFACE 1 +#define F_AUDIO_NUM_INTERFACES 2 + +/* B.3.1 Standard AC Interface Descriptor */ +static struct usb_interface_descriptor ac_interface_desc __initdata = { + .bLength = USB_DT_INTERFACE_SIZE, + .bDescriptorType = USB_DT_INTERFACE, + .bNumEndpoints = 0, + .bInterfaceClass = USB_CLASS_AUDIO, + .bInterfaceSubClass = USB_SUBCLASS_AUDIOCONTROL, +}; + +DECLARE_UAC_AC_HEADER_DESCRIPTOR(2); + +#define UAC_DT_AC_HEADER_LENGTH UAC_DT_AC_HEADER_SIZE(F_AUDIO_NUM_INTERFACES) +/* 1 input terminal, 1 output terminal and 1 feature unit */ +#define UAC_DT_TOTAL_LENGTH (UAC_DT_AC_HEADER_LENGTH + UAC_DT_INPUT_TERMINAL_SIZE \ + + UAC_DT_OUTPUT_TERMINAL_SIZE + UAC_DT_FEATURE_UNIT_SIZE(0)) +/* B.3.2 Class-Specific AC Interface Descriptor */ +static struct uac1_ac_header_descriptor_2 ac_header_desc = { + .bLength = UAC_DT_AC_HEADER_LENGTH, + .bDescriptorType = USB_DT_CS_INTERFACE, + .bDescriptorSubtype = UAC_HEADER, + .bcdADC = __constant_cpu_to_le16(0x0100), + .wTotalLength = __constant_cpu_to_le16(UAC_DT_TOTAL_LENGTH), + .bInCollection = F_AUDIO_NUM_INTERFACES, + .baInterfaceNr = { + [0] = F_AUDIO_AC_INTERFACE, + [1] = F_AUDIO_AS_INTERFACE, + } +}; + +#define INPUT_TERMINAL_ID 1 +static struct uac_input_terminal_descriptor input_terminal_desc = { + .bLength = UAC_DT_INPUT_TERMINAL_SIZE, + .bDescriptorType = USB_DT_CS_INTERFACE, + .bDescriptorSubtype = UAC_INPUT_TERMINAL, + .bTerminalID = INPUT_TERMINAL_ID, + .wTerminalType = UAC_TERMINAL_STREAMING, + .bAssocTerminal = 0, + .wChannelConfig = 0x3, +}; + +DECLARE_UAC_FEATURE_UNIT_DESCRIPTOR(0); + +#define FEATURE_UNIT_ID 2 +static struct uac_feature_unit_descriptor_0 feature_unit_desc = { + .bLength = UAC_DT_FEATURE_UNIT_SIZE(0), + .bDescriptorType = USB_DT_CS_INTERFACE, + .bDescriptorSubtype = UAC_FEATURE_UNIT, + .bUnitID = FEATURE_UNIT_ID, + .bSourceID = INPUT_TERMINAL_ID, + .bControlSize = 2, + .bmaControls[0] = (UAC_FU_MUTE | UAC_FU_VOLUME), +}; + +static struct usb_audio_control mute_control = { + .list = LIST_HEAD_INIT(mute_control.list), + .name = "Mute Control", + .type = UAC_FU_MUTE, + /* Todo: add real Mute control code */ + .set = generic_set_cmd, + .get = generic_get_cmd, +}; + +static struct usb_audio_control volume_control = { + .list = LIST_HEAD_INIT(volume_control.list), + .name = "Volume Control", + .type = UAC_FU_VOLUME, + /* Todo: add real Volume control code */ + .set = generic_set_cmd, + .get = generic_get_cmd, +}; + +static struct usb_audio_control_selector feature_unit = { + .list = LIST_HEAD_INIT(feature_unit.list), + .id = FEATURE_UNIT_ID, + .name = "Mute & Volume Control", + .type = UAC_FEATURE_UNIT, + .desc = (struct usb_descriptor_header *)&feature_unit_desc, +}; + +#define OUTPUT_TERMINAL_ID 3 +static struct uac1_output_terminal_descriptor output_terminal_desc = { + .bLength = UAC_DT_OUTPUT_TERMINAL_SIZE, + .bDescriptorType = USB_DT_CS_INTERFACE, + .bDescriptorSubtype = UAC_OUTPUT_TERMINAL, + .bTerminalID = OUTPUT_TERMINAL_ID, + .wTerminalType = UAC_OUTPUT_TERMINAL_SPEAKER, + .bAssocTerminal = FEATURE_UNIT_ID, + .bSourceID = FEATURE_UNIT_ID, +}; + +/* B.4.1 Standard AS Interface Descriptor */ +static struct usb_interface_descriptor as_interface_alt_0_desc = { + .bLength = USB_DT_INTERFACE_SIZE, + .bDescriptorType = USB_DT_INTERFACE, + .bAlternateSetting = 0, + .bNumEndpoints = 0, + .bInterfaceClass = USB_CLASS_AUDIO, + .bInterfaceSubClass = USB_SUBCLASS_AUDIOSTREAMING, +}; + +static struct usb_interface_descriptor as_interface_alt_1_desc = { + .bLength = USB_DT_INTERFACE_SIZE, + .bDescriptorType = USB_DT_INTERFACE, + .bAlternateSetting = 1, + .bNumEndpoints = 1, + .bInterfaceClass = USB_CLASS_AUDIO, + .bInterfaceSubClass = USB_SUBCLASS_AUDIOSTREAMING, +}; + +/* B.4.2 Class-Specific AS Interface Descriptor */ +static struct uac1_as_header_descriptor as_header_desc = { + .bLength = UAC_DT_AS_HEADER_SIZE, + .bDescriptorType = USB_DT_CS_INTERFACE, + .bDescriptorSubtype = UAC_AS_GENERAL, + .bTerminalLink = INPUT_TERMINAL_ID, + .bDelay = 1, + .wFormatTag = UAC_FORMAT_TYPE_I_PCM, +}; + +DECLARE_UAC_FORMAT_TYPE_I_DISCRETE_DESC(1); + +static struct uac_format_type_i_discrete_descriptor_1 as_type_i_desc = { + .bLength = UAC_FORMAT_TYPE_I_DISCRETE_DESC_SIZE(1), + .bDescriptorType = USB_DT_CS_INTERFACE, + .bDescriptorSubtype = UAC_FORMAT_TYPE, + .bFormatType = UAC_FORMAT_TYPE_I, + .bSubframeSize = 2, + .bBitResolution = 16, + .bSamFreqType = 1, +}; + +/* Standard ISO OUT Endpoint Descriptor */ +static struct usb_endpoint_descriptor as_out_ep_desc = { + .bLength = USB_DT_ENDPOINT_AUDIO_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + .bEndpointAddress = USB_DIR_OUT, + .bmAttributes = USB_ENDPOINT_SYNC_ADAPTIVE + | USB_ENDPOINT_XFER_ISOC, + .wMaxPacketSize = __constant_cpu_to_le16(OUT_EP_MAX_PACKET_SIZE), + .bInterval = 4, +}; + +/* Class-specific AS ISO OUT Endpoint Descriptor */ +static struct uac_iso_endpoint_descriptor as_iso_out_desc __initdata = { + .bLength = UAC_ISO_ENDPOINT_DESC_SIZE, + .bDescriptorType = USB_DT_CS_ENDPOINT, + .bDescriptorSubtype = UAC_EP_GENERAL, + .bmAttributes = 1, + .bLockDelayUnits = 1, + .wLockDelay = __constant_cpu_to_le16(1), +}; + +static struct usb_descriptor_header *f_audio_desc[] __initdata = { + (struct usb_descriptor_header *)&ac_interface_desc, + (struct usb_descriptor_header *)&ac_header_desc, + + (struct usb_descriptor_header *)&input_terminal_desc, + (struct usb_descriptor_header *)&output_terminal_desc, + (struct usb_descriptor_header *)&feature_unit_desc, + + (struct usb_descriptor_header *)&as_interface_alt_0_desc, + (struct usb_descriptor_header *)&as_interface_alt_1_desc, + (struct usb_descriptor_header *)&as_header_desc, + + (struct usb_descriptor_header *)&as_type_i_desc, + + (struct usb_descriptor_header *)&as_out_ep_desc, + (struct usb_descriptor_header *)&as_iso_out_desc, + NULL, +}; + +/* string IDs are assigned dynamically */ + +#define STRING_MANUFACTURER_IDX 0 +#define STRING_PRODUCT_IDX 1 + +static char manufacturer[50]; + +static struct usb_string strings_dev[] = { + [STRING_MANUFACTURER_IDX].s = manufacturer, + [STRING_PRODUCT_IDX].s = DRIVER_DESC, + { } /* end of list */ +}; + +static struct usb_gadget_strings stringtab_dev = { + .language = 0x0409, /* en-us */ + .strings = strings_dev, +}; + +static struct usb_gadget_strings *audio_strings[] = { + &stringtab_dev, + NULL, +}; + +/* + * This function is an ALSA sound card following USB Audio Class Spec 1.0. + */ + +/*-------------------------------------------------------------------------*/ +struct f_audio_buf { + u8 *buf; + int actual; + struct list_head list; +}; + +static struct f_audio_buf *f_audio_buffer_alloc(int buf_size) +{ + struct f_audio_buf *copy_buf; + + copy_buf = kzalloc(sizeof *copy_buf, GFP_ATOMIC); + if (!copy_buf) + return ERR_PTR(-ENOMEM); + + copy_buf->buf = kzalloc(buf_size, GFP_ATOMIC); + if (!copy_buf->buf) { + kfree(copy_buf); + return ERR_PTR(-ENOMEM); + } + + return copy_buf; +} + +static void f_audio_buffer_free(struct f_audio_buf *audio_buf) +{ + kfree(audio_buf->buf); + kfree(audio_buf); +} +/*-------------------------------------------------------------------------*/ + +struct f_audio { + struct gaudio card; + + /* endpoints handle full and/or high speeds */ + struct usb_ep *out_ep; + + spinlock_t lock; + struct f_audio_buf *copy_buf; + struct work_struct playback_work; + struct list_head play_queue; + + /* Control Set command */ + struct list_head cs; + u8 set_cmd; + struct usb_audio_control *set_con; +}; + +static inline struct f_audio *func_to_audio(struct usb_function *f) +{ + return container_of(f, struct f_audio, card.func); +} + +/*-------------------------------------------------------------------------*/ + +static void f_audio_playback_work(struct work_struct *data) +{ + struct f_audio *audio = container_of(data, struct f_audio, + playback_work); + struct f_audio_buf *play_buf; + + spin_lock_irq(&audio->lock); + if (list_empty(&audio->play_queue)) { + spin_unlock_irq(&audio->lock); + return; + } + play_buf = list_first_entry(&audio->play_queue, + struct f_audio_buf, list); + list_del(&play_buf->list); + spin_unlock_irq(&audio->lock); + + u_audio_playback(&audio->card, play_buf->buf, play_buf->actual); + f_audio_buffer_free(play_buf); +} + +static int f_audio_out_ep_complete(struct usb_ep *ep, struct usb_request *req) +{ + struct f_audio *audio = req->context; + struct usb_composite_dev *cdev = audio->card.func.config->cdev; + struct f_audio_buf *copy_buf = audio->copy_buf; + int err; + + if (!copy_buf) + return -EINVAL; + + /* Copy buffer is full, add it to the play_queue */ + if (audio_buf_size - copy_buf->actual < req->actual) { + list_add_tail(©_buf->list, &audio->play_queue); + schedule_work(&audio->playback_work); + copy_buf = f_audio_buffer_alloc(audio_buf_size); + if (IS_ERR(copy_buf)) + return -ENOMEM; + } + + memcpy(copy_buf->buf + copy_buf->actual, req->buf, req->actual); + copy_buf->actual += req->actual; + audio->copy_buf = copy_buf; + + err = usb_ep_queue(ep, req, GFP_ATOMIC); + if (err) + ERROR(cdev, "%s queue req: %d\n", ep->name, err); + + return 0; + +} + +static void f_audio_complete(struct usb_ep *ep, struct usb_request *req) +{ + struct f_audio *audio = req->context; + int status = req->status; + u32 data = 0; + struct usb_ep *out_ep = audio->out_ep; + + switch (status) { + + case 0: /* normal completion? */ + if (ep == out_ep) + f_audio_out_ep_complete(ep, req); + else if (audio->set_con) { + memcpy(&data, req->buf, req->length); + audio->set_con->set(audio->set_con, audio->set_cmd, + le16_to_cpu(data)); + audio->set_con = NULL; + } + break; + default: + break; + } +} + +static int audio_set_intf_req(struct usb_function *f, + const struct usb_ctrlrequest *ctrl) +{ + struct f_audio *audio = func_to_audio(f); + struct usb_composite_dev *cdev = f->config->cdev; + struct usb_request *req = cdev->req; + u8 id = ((le16_to_cpu(ctrl->wIndex) >> 8) & 0xFF); + u16 len = le16_to_cpu(ctrl->wLength); + u16 w_value = le16_to_cpu(ctrl->wValue); + u8 con_sel = (w_value >> 8) & 0xFF; + u8 cmd = (ctrl->bRequest & 0x0F); + struct usb_audio_control_selector *cs; + struct usb_audio_control *con; + + DBG(cdev, "bRequest 0x%x, w_value 0x%04x, len %d, entity %d\n", + ctrl->bRequest, w_value, len, id); + + list_for_each_entry(cs, &audio->cs, list) { + if (cs->id == id) { + list_for_each_entry(con, &cs->control, list) { + if (con->type == con_sel) { + audio->set_con = con; + break; + } + } + break; + } + } + + audio->set_cmd = cmd; + req->context = audio; + req->complete = f_audio_complete; + + return len; +} + +static int audio_get_intf_req(struct usb_function *f, + const struct usb_ctrlrequest *ctrl) +{ + struct f_audio *audio = func_to_audio(f); + struct usb_composite_dev *cdev = f->config->cdev; + struct usb_request *req = cdev->req; + int value = -EOPNOTSUPP; + u8 id = ((le16_to_cpu(ctrl->wIndex) >> 8) & 0xFF); + u16 len = le16_to_cpu(ctrl->wLength); + u16 w_value = le16_to_cpu(ctrl->wValue); + u8 con_sel = (w_value >> 8) & 0xFF; + u8 cmd = (ctrl->bRequest & 0x0F); + struct usb_audio_control_selector *cs; + struct usb_audio_control *con; + + DBG(cdev, "bRequest 0x%x, w_value 0x%04x, len %d, entity %d\n", + ctrl->bRequest, w_value, len, id); + + list_for_each_entry(cs, &audio->cs, list) { + if (cs->id == id) { + list_for_each_entry(con, &cs->control, list) { + if (con->type == con_sel && con->get) { + value = con->get(con, cmd); + break; + } + } + break; + } + } + + req->context = audio; + req->complete = f_audio_complete; + memcpy(req->buf, &value, len); + + return len; +} + +static int audio_set_endpoint_req(struct usb_function *f, + const struct usb_ctrlrequest *ctrl) +{ + struct usb_composite_dev *cdev = f->config->cdev; + int value = -EOPNOTSUPP; + u16 ep = le16_to_cpu(ctrl->wIndex); + u16 len = le16_to_cpu(ctrl->wLength); + u16 w_value = le16_to_cpu(ctrl->wValue); + + DBG(cdev, "bRequest 0x%x, w_value 0x%04x, len %d, endpoint %d\n", + ctrl->bRequest, w_value, len, ep); + + switch (ctrl->bRequest) { + case UAC_SET_CUR: + value = len; + break; + + case UAC_SET_MIN: + break; + + case UAC_SET_MAX: + break; + + case UAC_SET_RES: + break; + + case UAC_SET_MEM: + break; + + default: + break; + } + + return value; +} + +static int audio_get_endpoint_req(struct usb_function *f, + const struct usb_ctrlrequest *ctrl) +{ + struct usb_composite_dev *cdev = f->config->cdev; + int value = -EOPNOTSUPP; + u8 ep = ((le16_to_cpu(ctrl->wIndex) >> 8) & 0xFF); + u16 len = le16_to_cpu(ctrl->wLength); + u16 w_value = le16_to_cpu(ctrl->wValue); + + DBG(cdev, "bRequest 0x%x, w_value 0x%04x, len %d, endpoint %d\n", + ctrl->bRequest, w_value, len, ep); + + switch (ctrl->bRequest) { + case UAC_GET_CUR: + case UAC_GET_MIN: + case UAC_GET_MAX: + case UAC_GET_RES: + value = len; + break; + case UAC_GET_MEM: + break; + default: + break; + } + + return value; +} + +static int +f_audio_setup(struct usb_function *f, const struct usb_ctrlrequest *ctrl) +{ + struct usb_composite_dev *cdev = f->config->cdev; + struct usb_request *req = cdev->req; + int value = -EOPNOTSUPP; + u16 w_index = le16_to_cpu(ctrl->wIndex); + u16 w_value = le16_to_cpu(ctrl->wValue); + u16 w_length = le16_to_cpu(ctrl->wLength); + + /* composite driver infrastructure handles everything; interface + * activation uses set_alt(). + */ + switch (ctrl->bRequestType) { + case USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE: + value = audio_set_intf_req(f, ctrl); + break; + + case USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE: + value = audio_get_intf_req(f, ctrl); + break; + + case USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_ENDPOINT: + value = audio_set_endpoint_req(f, ctrl); + break; + + case USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_ENDPOINT: + value = audio_get_endpoint_req(f, ctrl); + break; + + default: + ERROR(cdev, "invalid control req%02x.%02x v%04x i%04x l%d\n", + ctrl->bRequestType, ctrl->bRequest, + w_value, w_index, w_length); + } + + /* respond with data transfer or status phase? */ + if (value >= 0) { + DBG(cdev, "audio req%02x.%02x v%04x i%04x l%d\n", + ctrl->bRequestType, ctrl->bRequest, + w_value, w_index, w_length); + req->zero = 0; + req->length = value; + value = usb_ep_queue(cdev->gadget->ep0, req, GFP_ATOMIC); + if (value < 0) + ERROR(cdev, "audio response on err %d\n", value); + } + + /* device either stalls (value < 0) or reports success */ + return value; +} + +static int f_audio_set_alt(struct usb_function *f, unsigned intf, unsigned alt) +{ + struct f_audio *audio = func_to_audio(f); + struct usb_composite_dev *cdev = f->config->cdev; + struct usb_ep *out_ep = audio->out_ep; + struct usb_request *req; + int i = 0, err = 0; + + DBG(cdev, "intf %d, alt %d\n", intf, alt); + + if (intf == 1) { + if (alt == 1) { + usb_ep_enable(out_ep); + out_ep->driver_data = audio; + audio->copy_buf = f_audio_buffer_alloc(audio_buf_size); + if (IS_ERR(audio->copy_buf)) + return -ENOMEM; + + /* + * allocate a bunch of read buffers + * and queue them all at once. + */ + for (i = 0; i < req_count && err == 0; i++) { + req = usb_ep_alloc_request(out_ep, GFP_ATOMIC); + if (req) { + req->buf = kzalloc(req_buf_size, + GFP_ATOMIC); + if (req->buf) { + req->length = req_buf_size; + req->context = audio; + req->complete = + f_audio_complete; + err = usb_ep_queue(out_ep, + req, GFP_ATOMIC); + if (err) + ERROR(cdev, + "%s queue req: %d\n", + out_ep->name, err); + } else + err = -ENOMEM; + } else + err = -ENOMEM; + } + + } else { + struct f_audio_buf *copy_buf = audio->copy_buf; + if (copy_buf) { + list_add_tail(©_buf->list, + &audio->play_queue); + schedule_work(&audio->playback_work); + } + } + } + + return err; +} + +static void f_audio_disable(struct usb_function *f) +{ + return; +} + +/*-------------------------------------------------------------------------*/ + +static void f_audio_build_desc(struct f_audio *audio) +{ + struct gaudio *card = &audio->card; + u8 *sam_freq; + int rate; + + /* Set channel numbers */ + input_terminal_desc.bNrChannels = u_audio_get_playback_channels(card); + as_type_i_desc.bNrChannels = u_audio_get_playback_channels(card); + + /* Set sample rates */ + rate = u_audio_get_playback_rate(card); + sam_freq = as_type_i_desc.tSamFreq[0]; + memcpy(sam_freq, &rate, 3); + + /* Todo: Set Sample bits and other parameters */ + + return; +} + +/* audio function driver setup/binding */ +static int __init +f_audio_bind(struct usb_configuration *c, struct usb_function *f) +{ + struct usb_composite_dev *cdev = c->cdev; + struct f_audio *audio = func_to_audio(f); + int status; + struct usb_ep *ep; + + f_audio_build_desc(audio); + + /* allocate instance-specific interface IDs, and patch descriptors */ + status = usb_interface_id(c, f); + if (status < 0) + goto fail; + ac_interface_desc.bInterfaceNumber = status; + + status = usb_interface_id(c, f); + if (status < 0) + goto fail; + as_interface_alt_0_desc.bInterfaceNumber = status; + as_interface_alt_1_desc.bInterfaceNumber = status; + + status = -ENODEV; + + /* allocate instance-specific endpoints */ + ep = usb_ep_autoconfig(cdev->gadget, &as_out_ep_desc); + if (!ep) + goto fail; + audio->out_ep = ep; + audio->out_ep->desc = &as_out_ep_desc; + ep->driver_data = cdev; /* claim */ + + status = -ENOMEM; + + /* copy descriptors, and track endpoint copies */ + f->descriptors = usb_copy_descriptors(f_audio_desc); + + /* + * support all relevant hardware speeds... we expect that when + * hardware is dual speed, all bulk-capable endpoints work at + * both speeds + */ + if (gadget_is_dualspeed(c->cdev->gadget)) { + c->highspeed = true; + f->hs_descriptors = usb_copy_descriptors(f_audio_desc); + } + + return 0; + +fail: + + return status; +} + +static void +f_audio_unbind(struct usb_configuration *c, struct usb_function *f) +{ + struct f_audio *audio = func_to_audio(f); + + usb_free_descriptors(f->descriptors); + usb_free_descriptors(f->hs_descriptors); + kfree(audio); +} + +/*-------------------------------------------------------------------------*/ + +static int generic_set_cmd(struct usb_audio_control *con, u8 cmd, int value) +{ + con->data[cmd] = value; + + return 0; +} + +static int generic_get_cmd(struct usb_audio_control *con, u8 cmd) +{ + return con->data[cmd]; +} + +/* Todo: add more control selecotor dynamically */ +int __init control_selector_init(struct f_audio *audio) +{ + INIT_LIST_HEAD(&audio->cs); + list_add(&feature_unit.list, &audio->cs); + + INIT_LIST_HEAD(&feature_unit.control); + list_add(&mute_control.list, &feature_unit.control); + list_add(&volume_control.list, &feature_unit.control); + + volume_control.data[UAC__CUR] = 0xffc0; + volume_control.data[UAC__MIN] = 0xe3a0; + volume_control.data[UAC__MAX] = 0xfff0; + volume_control.data[UAC__RES] = 0x0030; + + return 0; +} + +/** + * audio_bind_config - add USB audio function to a configuration + * @c: the configuration to supcard the USB audio function + * Context: single threaded during gadget setup + * + * Returns zero on success, else negative errno. + */ +int __init audio_bind_config(struct usb_configuration *c) +{ + struct f_audio *audio; + int status; + + /* allocate and initialize one new instance */ + audio = kzalloc(sizeof *audio, GFP_KERNEL); + if (!audio) + return -ENOMEM; + + audio->card.func.name = "g_audio"; + audio->card.gadget = c->cdev->gadget; + + INIT_LIST_HEAD(&audio->play_queue); + spin_lock_init(&audio->lock); + + /* set up ASLA audio devices */ + status = gaudio_setup(&audio->card); + if (status < 0) + goto setup_fail; + + audio->card.func.strings = audio_strings; + audio->card.func.bind = f_audio_bind; + audio->card.func.unbind = f_audio_unbind; + audio->card.func.set_alt = f_audio_set_alt; + audio->card.func.setup = f_audio_setup; + audio->card.func.disable = f_audio_disable; + + control_selector_init(audio); + + INIT_WORK(&audio->playback_work, f_audio_playback_work); + + status = usb_add_function(c, &audio->card.func); + if (status) + goto add_fail; + + INFO(c->cdev, "audio_buf_size %d, req_buf_size %d, req_count %d\n", + audio_buf_size, req_buf_size, req_count); + + return status; + +add_fail: + gaudio_cleanup(); +setup_fail: + kfree(audio); + return status; +} diff --git a/drivers/usb/gadget/u_audio.c b/drivers/usb/gadget/u_audio.c deleted file mode 100644 index 59ffe1e..0000000 --- a/drivers/usb/gadget/u_audio.c +++ /dev/null @@ -1,327 +0,0 @@ -/* - * u_audio.c -- ALSA audio utilities for Gadget stack - * - * Copyright (C) 2008 Bryan Wu - * Copyright (C) 2008 Analog Devices, Inc - * - * Enter bugs at http://blackfin.uclinux.org/ - * - * Licensed under the GPL-2 or later. - */ - -#include -#include -#include -#include -#include -#include -#include - -#include "u_audio.h" - -/* - * This component encapsulates the ALSA devices for USB audio gadget - */ - -#define FILE_PCM_PLAYBACK "/dev/snd/pcmC0D0p" -#define FILE_PCM_CAPTURE "/dev/snd/pcmC0D0c" -#define FILE_CONTROL "/dev/snd/controlC0" - -static char *fn_play = FILE_PCM_PLAYBACK; -module_param(fn_play, charp, S_IRUGO); -MODULE_PARM_DESC(fn_play, "Playback PCM device file name"); - -static char *fn_cap = FILE_PCM_CAPTURE; -module_param(fn_cap, charp, S_IRUGO); -MODULE_PARM_DESC(fn_cap, "Capture PCM device file name"); - -static char *fn_cntl = FILE_CONTROL; -module_param(fn_cntl, charp, S_IRUGO); -MODULE_PARM_DESC(fn_cntl, "Control device file name"); - -/*-------------------------------------------------------------------------*/ - -/** - * Some ALSA internal helper functions - */ -static int snd_interval_refine_set(struct snd_interval *i, unsigned int val) -{ - struct snd_interval t; - t.empty = 0; - t.min = t.max = val; - t.openmin = t.openmax = 0; - t.integer = 1; - return snd_interval_refine(i, &t); -} - -static int _snd_pcm_hw_param_set(struct snd_pcm_hw_params *params, - snd_pcm_hw_param_t var, unsigned int val, - int dir) -{ - int changed; - if (hw_is_mask(var)) { - struct snd_mask *m = hw_param_mask(params, var); - if (val == 0 && dir < 0) { - changed = -EINVAL; - snd_mask_none(m); - } else { - if (dir > 0) - val++; - else if (dir < 0) - val--; - changed = snd_mask_refine_set( - hw_param_mask(params, var), val); - } - } else if (hw_is_interval(var)) { - struct snd_interval *i = hw_param_interval(params, var); - if (val == 0 && dir < 0) { - changed = -EINVAL; - snd_interval_none(i); - } else if (dir == 0) - changed = snd_interval_refine_set(i, val); - else { - struct snd_interval t; - t.openmin = 1; - t.openmax = 1; - t.empty = 0; - t.integer = 0; - if (dir < 0) { - t.min = val - 1; - t.max = val; - } else { - t.min = val; - t.max = val+1; - } - changed = snd_interval_refine(i, &t); - } - } else - return -EINVAL; - if (changed) { - params->cmask |= 1 << var; - params->rmask |= 1 << var; - } - return changed; -} -/*-------------------------------------------------------------------------*/ - -/** - * Set default hardware params - */ -static int playback_default_hw_params(struct gaudio_snd_dev *snd) -{ - struct snd_pcm_substream *substream = snd->substream; - struct snd_pcm_hw_params *params; - snd_pcm_sframes_t result; - - /* - * SNDRV_PCM_ACCESS_RW_INTERLEAVED, - * SNDRV_PCM_FORMAT_S16_LE - * CHANNELS: 2 - * RATE: 48000 - */ - snd->access = SNDRV_PCM_ACCESS_RW_INTERLEAVED; - snd->format = SNDRV_PCM_FORMAT_S16_LE; - snd->channels = 2; - snd->rate = 48000; - - params = kzalloc(sizeof(*params), GFP_KERNEL); - if (!params) - return -ENOMEM; - - _snd_pcm_hw_params_any(params); - _snd_pcm_hw_param_set(params, SNDRV_PCM_HW_PARAM_ACCESS, - snd->access, 0); - _snd_pcm_hw_param_set(params, SNDRV_PCM_HW_PARAM_FORMAT, - snd->format, 0); - _snd_pcm_hw_param_set(params, SNDRV_PCM_HW_PARAM_CHANNELS, - snd->channels, 0); - _snd_pcm_hw_param_set(params, SNDRV_PCM_HW_PARAM_RATE, - snd->rate, 0); - - snd_pcm_kernel_ioctl(substream, SNDRV_PCM_IOCTL_DROP, NULL); - snd_pcm_kernel_ioctl(substream, SNDRV_PCM_IOCTL_HW_PARAMS, params); - - result = snd_pcm_kernel_ioctl(substream, SNDRV_PCM_IOCTL_PREPARE, NULL); - if (result < 0) { - ERROR(snd->card, - "Preparing sound card failed: %d\n", (int)result); - kfree(params); - return result; - } - - /* Store the hardware parameters */ - snd->access = params_access(params); - snd->format = params_format(params); - snd->channels = params_channels(params); - snd->rate = params_rate(params); - - kfree(params); - - INFO(snd->card, - "Hardware params: access %x, format %x, channels %d, rate %d\n", - snd->access, snd->format, snd->channels, snd->rate); - - return 0; -} - -/** - * Playback audio buffer data by ALSA PCM device - */ -static size_t u_audio_playback(struct gaudio *card, void *buf, size_t count) -{ - struct gaudio_snd_dev *snd = &card->playback; - struct snd_pcm_substream *substream = snd->substream; - struct snd_pcm_runtime *runtime = substream->runtime; - mm_segment_t old_fs; - ssize_t result; - snd_pcm_sframes_t frames; - -try_again: - if (runtime->status->state == SNDRV_PCM_STATE_XRUN || - runtime->status->state == SNDRV_PCM_STATE_SUSPENDED) { - result = snd_pcm_kernel_ioctl(substream, - SNDRV_PCM_IOCTL_PREPARE, NULL); - if (result < 0) { - ERROR(card, "Preparing sound card failed: %d\n", - (int)result); - return result; - } - } - - frames = bytes_to_frames(runtime, count); - old_fs = get_fs(); - set_fs(KERNEL_DS); - result = snd_pcm_lib_write(snd->substream, buf, frames); - if (result != frames) { - ERROR(card, "Playback error: %d\n", (int)result); - set_fs(old_fs); - goto try_again; - } - set_fs(old_fs); - - return 0; -} - -static int u_audio_get_playback_channels(struct gaudio *card) -{ - return card->playback.channels; -} - -static int u_audio_get_playback_rate(struct gaudio *card) -{ - return card->playback.rate; -} - -/** - * Open ALSA PCM and control device files - * Initial the PCM or control device - */ -static int gaudio_open_snd_dev(struct gaudio *card) -{ - struct snd_pcm_file *pcm_file; - struct gaudio_snd_dev *snd; - - if (!card) - return -ENODEV; - - /* Open control device */ - snd = &card->control; - snd->filp = filp_open(fn_cntl, O_RDWR, 0); - if (IS_ERR(snd->filp)) { - int ret = PTR_ERR(snd->filp); - ERROR(card, "unable to open sound control device file: %s\n", - fn_cntl); - snd->filp = NULL; - return ret; - } - snd->card = card; - - /* Open PCM playback device and setup substream */ - snd = &card->playback; - snd->filp = filp_open(fn_play, O_WRONLY, 0); - if (IS_ERR(snd->filp)) { - ERROR(card, "No such PCM playback device: %s\n", fn_play); - snd->filp = NULL; - } - pcm_file = snd->filp->private_data; - snd->substream = pcm_file->substream; - snd->card = card; - playback_default_hw_params(snd); - - /* Open PCM capture device and setup substream */ - snd = &card->capture; - snd->filp = filp_open(fn_cap, O_RDONLY, 0); - if (IS_ERR(snd->filp)) { - ERROR(card, "No such PCM capture device: %s\n", fn_cap); - snd->substream = NULL; - snd->card = NULL; - snd->filp = NULL; - } else { - pcm_file = snd->filp->private_data; - snd->substream = pcm_file->substream; - snd->card = card; - } - - return 0; -} - -/** - * Close ALSA PCM and control device files - */ -static int gaudio_close_snd_dev(struct gaudio *gau) -{ - struct gaudio_snd_dev *snd; - - /* Close control device */ - snd = &gau->control; - if (snd->filp) - filp_close(snd->filp, current->files); - - /* Close PCM playback device and setup substream */ - snd = &gau->playback; - if (snd->filp) - filp_close(snd->filp, current->files); - - /* Close PCM capture device and setup substream */ - snd = &gau->capture; - if (snd->filp) - filp_close(snd->filp, current->files); - - return 0; -} - -static struct gaudio *the_card; -/** - * gaudio_setup - setup ALSA interface and preparing for USB transfer - * - * This sets up PCM, mixer or MIDI ALSA devices fore USB gadget using. - * - * Returns negative errno, or zero on success - */ -int __init gaudio_setup(struct gaudio *card) -{ - int ret; - - ret = gaudio_open_snd_dev(card); - if (ret) - ERROR(card, "we need at least one control device\n"); - else if (!the_card) - the_card = card; - - return ret; - -} - -/** - * gaudio_cleanup - remove ALSA device interface - * - * This is called to free all resources allocated by @gaudio_setup(). - */ -void gaudio_cleanup(void) -{ - if (the_card) { - gaudio_close_snd_dev(the_card); - the_card = NULL; - } -} - diff --git a/drivers/usb/gadget/u_audio.h b/drivers/usb/gadget/u_audio.h deleted file mode 100644 index 08ffce3..0000000 --- a/drivers/usb/gadget/u_audio.h +++ /dev/null @@ -1,56 +0,0 @@ -/* - * u_audio.h -- interface to USB gadget "ALSA AUDIO" utilities - * - * Copyright (C) 2008 Bryan Wu - * Copyright (C) 2008 Analog Devices, Inc - * - * Enter bugs at http://blackfin.uclinux.org/ - * - * Licensed under the GPL-2 or later. - */ - -#ifndef __U_AUDIO_H -#define __U_AUDIO_H - -#include -#include -#include -#include - -#include -#include -#include - -#include "gadget_chips.h" - -/* - * This represents the USB side of an audio card device, managed by a USB - * function which provides control and stream interfaces. - */ - -struct gaudio_snd_dev { - struct gaudio *card; - struct file *filp; - struct snd_pcm_substream *substream; - int access; - int format; - int channels; - int rate; -}; - -struct gaudio { - struct usb_function func; - struct usb_gadget *gadget; - - /* ALSA sound device interfaces */ - struct gaudio_snd_dev control; - struct gaudio_snd_dev playback; - struct gaudio_snd_dev capture; - - /* TODO */ -}; - -int gaudio_setup(struct gaudio *card); -void gaudio_cleanup(void); - -#endif /* __U_AUDIO_H */ diff --git a/drivers/usb/gadget/u_uac1.c b/drivers/usb/gadget/u_uac1.c new file mode 100644 index 0000000..af98989 --- /dev/null +++ b/drivers/usb/gadget/u_uac1.c @@ -0,0 +1,327 @@ +/* + * u_uac1.c -- ALSA audio utilities for Gadget stack + * + * Copyright (C) 2008 Bryan Wu + * Copyright (C) 2008 Analog Devices, Inc + * + * Enter bugs at http://blackfin.uclinux.org/ + * + * Licensed under the GPL-2 or later. + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "u_uac1.h" + +/* + * This component encapsulates the ALSA devices for USB audio gadget + */ + +#define FILE_PCM_PLAYBACK "/dev/snd/pcmC0D0p" +#define FILE_PCM_CAPTURE "/dev/snd/pcmC0D0c" +#define FILE_CONTROL "/dev/snd/controlC0" + +static char *fn_play = FILE_PCM_PLAYBACK; +module_param(fn_play, charp, S_IRUGO); +MODULE_PARM_DESC(fn_play, "Playback PCM device file name"); + +static char *fn_cap = FILE_PCM_CAPTURE; +module_param(fn_cap, charp, S_IRUGO); +MODULE_PARM_DESC(fn_cap, "Capture PCM device file name"); + +static char *fn_cntl = FILE_CONTROL; +module_param(fn_cntl, charp, S_IRUGO); +MODULE_PARM_DESC(fn_cntl, "Control device file name"); + +/*-------------------------------------------------------------------------*/ + +/** + * Some ALSA internal helper functions + */ +static int snd_interval_refine_set(struct snd_interval *i, unsigned int val) +{ + struct snd_interval t; + t.empty = 0; + t.min = t.max = val; + t.openmin = t.openmax = 0; + t.integer = 1; + return snd_interval_refine(i, &t); +} + +static int _snd_pcm_hw_param_set(struct snd_pcm_hw_params *params, + snd_pcm_hw_param_t var, unsigned int val, + int dir) +{ + int changed; + if (hw_is_mask(var)) { + struct snd_mask *m = hw_param_mask(params, var); + if (val == 0 && dir < 0) { + changed = -EINVAL; + snd_mask_none(m); + } else { + if (dir > 0) + val++; + else if (dir < 0) + val--; + changed = snd_mask_refine_set( + hw_param_mask(params, var), val); + } + } else if (hw_is_interval(var)) { + struct snd_interval *i = hw_param_interval(params, var); + if (val == 0 && dir < 0) { + changed = -EINVAL; + snd_interval_none(i); + } else if (dir == 0) + changed = snd_interval_refine_set(i, val); + else { + struct snd_interval t; + t.openmin = 1; + t.openmax = 1; + t.empty = 0; + t.integer = 0; + if (dir < 0) { + t.min = val - 1; + t.max = val; + } else { + t.min = val; + t.max = val+1; + } + changed = snd_interval_refine(i, &t); + } + } else + return -EINVAL; + if (changed) { + params->cmask |= 1 << var; + params->rmask |= 1 << var; + } + return changed; +} +/*-------------------------------------------------------------------------*/ + +/** + * Set default hardware params + */ +static int playback_default_hw_params(struct gaudio_snd_dev *snd) +{ + struct snd_pcm_substream *substream = snd->substream; + struct snd_pcm_hw_params *params; + snd_pcm_sframes_t result; + + /* + * SNDRV_PCM_ACCESS_RW_INTERLEAVED, + * SNDRV_PCM_FORMAT_S16_LE + * CHANNELS: 2 + * RATE: 48000 + */ + snd->access = SNDRV_PCM_ACCESS_RW_INTERLEAVED; + snd->format = SNDRV_PCM_FORMAT_S16_LE; + snd->channels = 2; + snd->rate = 48000; + + params = kzalloc(sizeof(*params), GFP_KERNEL); + if (!params) + return -ENOMEM; + + _snd_pcm_hw_params_any(params); + _snd_pcm_hw_param_set(params, SNDRV_PCM_HW_PARAM_ACCESS, + snd->access, 0); + _snd_pcm_hw_param_set(params, SNDRV_PCM_HW_PARAM_FORMAT, + snd->format, 0); + _snd_pcm_hw_param_set(params, SNDRV_PCM_HW_PARAM_CHANNELS, + snd->channels, 0); + _snd_pcm_hw_param_set(params, SNDRV_PCM_HW_PARAM_RATE, + snd->rate, 0); + + snd_pcm_kernel_ioctl(substream, SNDRV_PCM_IOCTL_DROP, NULL); + snd_pcm_kernel_ioctl(substream, SNDRV_PCM_IOCTL_HW_PARAMS, params); + + result = snd_pcm_kernel_ioctl(substream, SNDRV_PCM_IOCTL_PREPARE, NULL); + if (result < 0) { + ERROR(snd->card, + "Preparing sound card failed: %d\n", (int)result); + kfree(params); + return result; + } + + /* Store the hardware parameters */ + snd->access = params_access(params); + snd->format = params_format(params); + snd->channels = params_channels(params); + snd->rate = params_rate(params); + + kfree(params); + + INFO(snd->card, + "Hardware params: access %x, format %x, channels %d, rate %d\n", + snd->access, snd->format, snd->channels, snd->rate); + + return 0; +} + +/** + * Playback audio buffer data by ALSA PCM device + */ +static size_t u_audio_playback(struct gaudio *card, void *buf, size_t count) +{ + struct gaudio_snd_dev *snd = &card->playback; + struct snd_pcm_substream *substream = snd->substream; + struct snd_pcm_runtime *runtime = substream->runtime; + mm_segment_t old_fs; + ssize_t result; + snd_pcm_sframes_t frames; + +try_again: + if (runtime->status->state == SNDRV_PCM_STATE_XRUN || + runtime->status->state == SNDRV_PCM_STATE_SUSPENDED) { + result = snd_pcm_kernel_ioctl(substream, + SNDRV_PCM_IOCTL_PREPARE, NULL); + if (result < 0) { + ERROR(card, "Preparing sound card failed: %d\n", + (int)result); + return result; + } + } + + frames = bytes_to_frames(runtime, count); + old_fs = get_fs(); + set_fs(KERNEL_DS); + result = snd_pcm_lib_write(snd->substream, buf, frames); + if (result != frames) { + ERROR(card, "Playback error: %d\n", (int)result); + set_fs(old_fs); + goto try_again; + } + set_fs(old_fs); + + return 0; +} + +static int u_audio_get_playback_channels(struct gaudio *card) +{ + return card->playback.channels; +} + +static int u_audio_get_playback_rate(struct gaudio *card) +{ + return card->playback.rate; +} + +/** + * Open ALSA PCM and control device files + * Initial the PCM or control device + */ +static int gaudio_open_snd_dev(struct gaudio *card) +{ + struct snd_pcm_file *pcm_file; + struct gaudio_snd_dev *snd; + + if (!card) + return -ENODEV; + + /* Open control device */ + snd = &card->control; + snd->filp = filp_open(fn_cntl, O_RDWR, 0); + if (IS_ERR(snd->filp)) { + int ret = PTR_ERR(snd->filp); + ERROR(card, "unable to open sound control device file: %s\n", + fn_cntl); + snd->filp = NULL; + return ret; + } + snd->card = card; + + /* Open PCM playback device and setup substream */ + snd = &card->playback; + snd->filp = filp_open(fn_play, O_WRONLY, 0); + if (IS_ERR(snd->filp)) { + ERROR(card, "No such PCM playback device: %s\n", fn_play); + snd->filp = NULL; + } + pcm_file = snd->filp->private_data; + snd->substream = pcm_file->substream; + snd->card = card; + playback_default_hw_params(snd); + + /* Open PCM capture device and setup substream */ + snd = &card->capture; + snd->filp = filp_open(fn_cap, O_RDONLY, 0); + if (IS_ERR(snd->filp)) { + ERROR(card, "No such PCM capture device: %s\n", fn_cap); + snd->substream = NULL; + snd->card = NULL; + snd->filp = NULL; + } else { + pcm_file = snd->filp->private_data; + snd->substream = pcm_file->substream; + snd->card = card; + } + + return 0; +} + +/** + * Close ALSA PCM and control device files + */ +static int gaudio_close_snd_dev(struct gaudio *gau) +{ + struct gaudio_snd_dev *snd; + + /* Close control device */ + snd = &gau->control; + if (snd->filp) + filp_close(snd->filp, current->files); + + /* Close PCM playback device and setup substream */ + snd = &gau->playback; + if (snd->filp) + filp_close(snd->filp, current->files); + + /* Close PCM capture device and setup substream */ + snd = &gau->capture; + if (snd->filp) + filp_close(snd->filp, current->files); + + return 0; +} + +static struct gaudio *the_card; +/** + * gaudio_setup - setup ALSA interface and preparing for USB transfer + * + * This sets up PCM, mixer or MIDI ALSA devices fore USB gadget using. + * + * Returns negative errno, or zero on success + */ +int __init gaudio_setup(struct gaudio *card) +{ + int ret; + + ret = gaudio_open_snd_dev(card); + if (ret) + ERROR(card, "we need at least one control device\n"); + else if (!the_card) + the_card = card; + + return ret; + +} + +/** + * gaudio_cleanup - remove ALSA device interface + * + * This is called to free all resources allocated by @gaudio_setup(). + */ +void gaudio_cleanup(void) +{ + if (the_card) { + gaudio_close_snd_dev(the_card); + the_card = NULL; + } +} + diff --git a/drivers/usb/gadget/u_uac1.h b/drivers/usb/gadget/u_uac1.h new file mode 100644 index 0000000..18c2e72 --- /dev/null +++ b/drivers/usb/gadget/u_uac1.h @@ -0,0 +1,56 @@ +/* + * u_uac1.h -- interface to USB gadget "ALSA AUDIO" utilities + * + * Copyright (C) 2008 Bryan Wu + * Copyright (C) 2008 Analog Devices, Inc + * + * Enter bugs at http://blackfin.uclinux.org/ + * + * Licensed under the GPL-2 or later. + */ + +#ifndef __U_AUDIO_H +#define __U_AUDIO_H + +#include +#include +#include +#include + +#include +#include +#include + +#include "gadget_chips.h" + +/* + * This represents the USB side of an audio card device, managed by a USB + * function which provides control and stream interfaces. + */ + +struct gaudio_snd_dev { + struct gaudio *card; + struct file *filp; + struct snd_pcm_substream *substream; + int access; + int format; + int channels; + int rate; +}; + +struct gaudio { + struct usb_function func; + struct usb_gadget *gadget; + + /* ALSA sound device interfaces */ + struct gaudio_snd_dev control; + struct gaudio_snd_dev playback; + struct gaudio_snd_dev capture; + + /* TODO */ +}; + +int gaudio_setup(struct gaudio *card); +void gaudio_cleanup(void); + +#endif /* __U_AUDIO_H */ -- cgit v0.10.2 From 0d4e1b2a7eb7f4db8b3dc1c7ef4b507040f87ded Mon Sep 17 00:00:00 2001 From: Jassi Brar Date: Thu, 2 Feb 2012 22:00:48 +0530 Subject: usb: uac2: Add ACHeader and FormatType descriptor Add missing, but needed, ACHeader and FormatType descriptor definitions. Signed-off-by: Yadi Brar Signed-off-by: Jassi Brar Signed-off-by: Felipe Balbi diff --git a/include/linux/usb/audio-v2.h b/include/linux/usb/audio-v2.h index 964cb60..ed13053 100644 --- a/include/linux/usb/audio-v2.h +++ b/include/linux/usb/audio-v2.h @@ -43,6 +43,27 @@ static inline bool uac2_control_is_writeable(u32 bmControls, u8 control) return (bmControls >> (control * 2)) & 0x2; } +/* 4.7.2 Class-Specific AC Interface Descriptor */ +struct uac2_ac_header_descriptor { + __u8 bLength; /* 9 */ + __u8 bDescriptorType; /* USB_DT_CS_INTERFACE */ + __u8 bDescriptorSubtype; /* UAC_MS_HEADER */ + __le16 bcdADC; /* 0x0200 */ + __u8 bCategory; + __le16 wTotalLength; /* includes Unit and Terminal desc. */ + __u8 bmControls; +} __packed; + +/* 2.3.1.6 Type I Format Type Descriptor (Frmts20 final.pdf)*/ +struct uac2_format_type_i_descriptor { + __u8 bLength; /* in bytes: 6 */ + __u8 bDescriptorType; /* USB_DT_CS_INTERFACE */ + __u8 bDescriptorSubtype; /* FORMAT_TYPE */ + __u8 bFormatType; /* FORMAT_TYPE_1 */ + __u8 bSubslotSize; /* {1,2,3,4} */ + __u8 bBitResolution; +} __packed; + /* 4.7.2.1 Clock Source Descriptor */ struct uac_clock_source_descriptor { -- cgit v0.10.2 From d747a916872241daef68c864ef09f8bef0c35bf8 Mon Sep 17 00:00:00 2001 From: Jassi Brar Date: Thu, 2 Feb 2012 22:01:12 +0530 Subject: usb: gadget: audio: Move string IDs to audio.c Move manufacturer and product string ids into audio.c so as to be reusable by the new uac2 version of gadget driver. Signed-off-by: Yadi Brar Signed-off-by: Jassi Brar Signed-off-by: Felipe Balbi diff --git a/drivers/usb/gadget/audio.c b/drivers/usb/gadget/audio.c index 056a2d9..33e9327 100644 --- a/drivers/usb/gadget/audio.c +++ b/drivers/usb/gadget/audio.c @@ -33,6 +33,29 @@ #include "config.c" #include "epautoconf.c" +/* string IDs are assigned dynamically */ + +#define STRING_MANUFACTURER_IDX 0 +#define STRING_PRODUCT_IDX 1 + +static char manufacturer[50]; + +static struct usb_string strings_dev[] = { + [STRING_MANUFACTURER_IDX].s = manufacturer, + [STRING_PRODUCT_IDX].s = DRIVER_DESC, + { } /* end of list */ +}; + +static struct usb_gadget_strings stringtab_dev = { + .language = 0x0409, /* en-us */ + .strings = strings_dev, +}; + +static struct usb_gadget_strings *audio_strings[] = { + &stringtab_dev, + NULL, +}; + #include "u_uac1.c" #include "f_uac1.c" diff --git a/drivers/usb/gadget/f_uac1.c b/drivers/usb/gadget/f_uac1.c index e4c060f..1a5dcd5 100644 --- a/drivers/usb/gadget/f_uac1.c +++ b/drivers/usb/gadget/f_uac1.c @@ -216,29 +216,6 @@ static struct usb_descriptor_header *f_audio_desc[] __initdata = { NULL, }; -/* string IDs are assigned dynamically */ - -#define STRING_MANUFACTURER_IDX 0 -#define STRING_PRODUCT_IDX 1 - -static char manufacturer[50]; - -static struct usb_string strings_dev[] = { - [STRING_MANUFACTURER_IDX].s = manufacturer, - [STRING_PRODUCT_IDX].s = DRIVER_DESC, - { } /* end of list */ -}; - -static struct usb_gadget_strings stringtab_dev = { - .language = 0x0409, /* en-us */ - .strings = strings_dev, -}; - -static struct usb_gadget_strings *audio_strings[] = { - &stringtab_dev, - NULL, -}; - /* * This function is an ALSA sound card following USB Audio Class Spec 1.0. */ -- cgit v0.10.2 From 132fcb460839a876f5bc8b71bede60f8d0875757 Mon Sep 17 00:00:00 2001 From: Jassi Brar Date: Thu, 2 Feb 2012 22:01:34 +0530 Subject: usb: gadget: Add Audio Class 2.0 Driver This is a flexible USB Audio Class 2.0 compliant gadget driver that implements a simple topology with a virtual sound card exposed at the function side. The driver doesn't expect any real audio codec to be present on the function - the audio streams are simply sinked to and sourced from a virtual ALSA sound card created. The user-space application may choose to do whatever it wants with the data received from the USB Host and choose to provide whatever it wants as audio data to the USB Host. Capture(USB-Out) and Playback(USB-In) can be run at independent configurations specified via module parameters while loading the driver. Make this new version as the default selection by a new Kconfig choice. Signed-off-by: Yadi Brar Signed-off-by: Jassi Brar Signed-off-by: Felipe Balbi diff --git a/drivers/usb/gadget/Kconfig b/drivers/usb/gadget/Kconfig index 7ecb68a..1623b52 100644 --- a/drivers/usb/gadget/Kconfig +++ b/drivers/usb/gadget/Kconfig @@ -599,16 +599,29 @@ config USB_AUDIO depends on SND select SND_PCM help - Gadget Audio is compatible with USB Audio Class specification 1.0. - It will include at least one AudioControl interface, zero or more - AudioStream interface and zero or more MIDIStream interface. - - Gadget Audio will use on-board ALSA (CONFIG_SND) audio card to - playback or capture audio stream. + This Gadget Audio driver is compatible with USB Audio Class + specification 2.0. It implements 1 AudioControl interface, + 1 AudioStreaming Interface each for USB-OUT and USB-IN. + Number of channels, sample rate and sample size can be + specified as module parameters. + This driver doesn't expect any real Audio codec to be present + on the device - the audio streams are simply sinked to and + sourced from a virtual ALSA sound card created. The user-space + application may choose to do whatever it wants with the data + received from the USB Host and choose to provide whatever it + wants as audio data to the USB Host. Say "y" to link the driver statically, or "m" to build a dynamically linked module called "g_audio". +config GADGET_UAC1 + bool "UAC 1.0 (Legacy)" + depends on USB_AUDIO + help + If you instead want older UAC Spec-1.0 driver that also has audio + paths hardwired to the Audio codec chip on-board and doesn't work + without one. + config USB_ETH tristate "Ethernet Gadget (with CDC Ethernet support)" depends on NET diff --git a/drivers/usb/gadget/audio.c b/drivers/usb/gadget/audio.c index 33e9327..9889924 100644 --- a/drivers/usb/gadget/audio.c +++ b/drivers/usb/gadget/audio.c @@ -14,10 +14,8 @@ #include #include -#include "u_uac1.h" - #define DRIVER_DESC "Linux USB Audio Gadget" -#define DRIVER_VERSION "Dec 18, 2008" +#define DRIVER_VERSION "Feb 2, 2012" /*-------------------------------------------------------------------------*/ @@ -56,8 +54,13 @@ static struct usb_gadget_strings *audio_strings[] = { NULL, }; +#ifdef CONFIG_GADGET_UAC1 +#include "u_uac1.h" #include "u_uac1.c" #include "f_uac1.c" +#else +#include "f_uac2.c" +#endif /*-------------------------------------------------------------------------*/ @@ -77,9 +80,15 @@ static struct usb_device_descriptor device_desc = { .bcdUSB = __constant_cpu_to_le16(0x200), +#ifdef CONFIG_GADGET_UAC1 .bDeviceClass = USB_CLASS_PER_INTERFACE, .bDeviceSubClass = 0, .bDeviceProtocol = 0, +#else + .bDeviceClass = USB_CLASS_MISC, + .bDeviceSubClass = 0x02, + .bDeviceProtocol = 0x01, +#endif /* .bMaxPacketSize0 = f(hardware) */ /* Vendor and product id defaults change according to what configs @@ -131,6 +140,9 @@ static struct usb_configuration audio_config_driver = { .bConfigurationValue = 1, /* .iConfiguration = DYNAMIC */ .bmAttributes = USB_CONFIG_ATT_SELFPOWER, +#ifndef CONFIG_GADGET_UAC1 + .unbind = uac2_unbind_config, +#endif }; /*-------------------------------------------------------------------------*/ @@ -180,7 +192,9 @@ fail: static int __exit audio_unbind(struct usb_composite_dev *cdev) { +#ifdef CONFIG_GADGET_UAC1 gaudio_cleanup(); +#endif return 0; } diff --git a/drivers/usb/gadget/f_uac2.c b/drivers/usb/gadget/f_uac2.c new file mode 100644 index 0000000..e7cc4de --- /dev/null +++ b/drivers/usb/gadget/f_uac2.c @@ -0,0 +1,1449 @@ +/* + * f_uac2.c -- USB Audio Class 2.0 Function + * + * Copyright (C) 2011 + * Yadwinder Singh (yadi.brar01@gmail.com) + * Jaswinder Singh (jaswinder.singh@linaro.org) + * + * 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 + +/* Playback(USB-IN) Default Stereo - Fl/Fr */ +static int p_chmask = 0x3; +module_param(p_chmask, uint, S_IRUGO); +MODULE_PARM_DESC(p_chmask, "Playback Channel Mask"); + +/* Playback Default 48 KHz */ +static int p_srate = 48000; +module_param(p_srate, uint, S_IRUGO); +MODULE_PARM_DESC(p_srate, "Playback Sampling Rate"); + +/* Playback Default 16bits/sample */ +static int p_ssize = 2; +module_param(p_ssize, uint, S_IRUGO); +MODULE_PARM_DESC(p_ssize, "Playback Sample Size(bytes)"); + +/* Capture(USB-OUT) Default Stereo - Fl/Fr */ +static int c_chmask = 0x3; +module_param(c_chmask, uint, S_IRUGO); +MODULE_PARM_DESC(c_chmask, "Capture Channel Mask"); + +/* Capture Default 64 KHz */ +static int c_srate = 64000; +module_param(c_srate, uint, S_IRUGO); +MODULE_PARM_DESC(c_srate, "Capture Sampling Rate"); + +/* Capture Default 16bits/sample */ +static int c_ssize = 2; +module_param(c_ssize, uint, S_IRUGO); +MODULE_PARM_DESC(c_ssize, "Capture Sample Size(bytes)"); + +#define DMA_ADDR_INVALID (~(dma_addr_t)0) + +#define ALT_SET(x, a) do {(x) &= ~0xff; (x) |= (a); } while (0) +#define ALT_GET(x) ((x) & 0xff) +#define INTF_SET(x, i) do {(x) &= 0xff; (x) |= ((i) << 8); } while (0) +#define INTF_GET(x) ((x >> 8) & 0xff) + +/* Keep everyone on toes */ +#define USB_XFERS 2 + +/* + * The driver implements a simple UAC_2 topology. + * USB-OUT -> IT_1 -> OT_3 -> ALSA_Capture + * ALSA_Playback -> IT_2 -> OT_4 -> USB-IN + * Capture and Playback sampling rates are independently + * controlled by two clock sources : + * CLK_5 := c_srate, and CLK_6 := p_srate + */ +#define USB_OUT_IT_ID 1 +#define IO_IN_IT_ID 2 +#define IO_OUT_OT_ID 3 +#define USB_IN_OT_ID 4 +#define USB_OUT_CLK_ID 5 +#define USB_IN_CLK_ID 6 + +#define CONTROL_ABSENT 0 +#define CONTROL_RDONLY 1 +#define CONTROL_RDWR 3 + +#define CLK_FREQ_CTRL 0 +#define CLK_VLD_CTRL 2 + +#define COPY_CTRL 0 +#define CONN_CTRL 2 +#define OVRLD_CTRL 4 +#define CLSTR_CTRL 6 +#define UNFLW_CTRL 8 +#define OVFLW_CTRL 10 + +const char *uac2_name = "snd_uac2"; + +struct uac2_req { + struct uac2_rtd_params *pp; /* parent param */ + struct usb_request *req; +}; + +struct uac2_rtd_params { + bool ep_enabled; /* if the ep is enabled */ + /* Size of the ring buffer */ + size_t dma_bytes; + unsigned char *dma_area; + + struct snd_pcm_substream *ss; + + /* Ring buffer */ + ssize_t hw_ptr; + + void *rbuf; + + size_t period_size; + + unsigned max_psize; + struct uac2_req ureq[USB_XFERS]; + + spinlock_t lock; +}; + +struct snd_uac2_chip { + struct platform_device pdev; + struct platform_driver pdrv; + + struct uac2_rtd_params p_prm; + struct uac2_rtd_params c_prm; + + struct snd_card *card; + struct snd_pcm *pcm; +}; + +#define BUFF_SIZE_MAX (PAGE_SIZE * 16) +#define PRD_SIZE_MAX PAGE_SIZE +#define MIN_PERIODS 4 + +static struct snd_pcm_hardware uac2_pcm_hardware = { + .info = SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_BLOCK_TRANSFER + | SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_MMAP_VALID + | SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_RESUME, + .rates = SNDRV_PCM_RATE_CONTINUOUS, + .periods_max = BUFF_SIZE_MAX / PRD_SIZE_MAX, + .buffer_bytes_max = BUFF_SIZE_MAX, + .period_bytes_max = PRD_SIZE_MAX, + .periods_min = MIN_PERIODS, +}; + +struct audio_dev { + /* Currently active {Interface[15:8] | AltSettings[7:0]} */ + __u16 ac_alt, as_out_alt, as_in_alt; + + struct usb_ep *in_ep, *out_ep; + struct usb_function func; + + /* The ALSA Sound Card it represents on the USB-Client side */ + struct snd_uac2_chip uac2; +}; + +static struct audio_dev *agdev_g; + +static inline +struct audio_dev *func_to_agdev(struct usb_function *f) +{ + return container_of(f, struct audio_dev, func); +} + +static inline +struct audio_dev *uac2_to_agdev(struct snd_uac2_chip *u) +{ + return container_of(u, struct audio_dev, uac2); +} + +static inline +struct snd_uac2_chip *pdev_to_uac2(struct platform_device *p) +{ + return container_of(p, struct snd_uac2_chip, pdev); +} + +static inline +struct snd_uac2_chip *prm_to_uac2(struct uac2_rtd_params *r) +{ + struct snd_uac2_chip *uac2 = container_of(r, + struct snd_uac2_chip, c_prm); + + if (&uac2->c_prm != r) + uac2 = container_of(r, struct snd_uac2_chip, p_prm); + + return uac2; +} + +static inline +uint num_channels(uint chanmask) +{ + uint num = 0; + + while (chanmask) { + num += (chanmask & 1); + chanmask >>= 1; + } + + return num; +} + +static void +agdev_iso_complete(struct usb_ep *ep, struct usb_request *req) +{ + unsigned pending; + unsigned long flags; + bool update_alsa = false; + unsigned char *src, *dst; + int status = req->status; + struct uac2_req *ur = req->context; + struct snd_pcm_substream *substream; + struct uac2_rtd_params *prm = ur->pp; + struct snd_uac2_chip *uac2 = prm_to_uac2(prm); + + /* i/f shutting down */ + if (!prm->ep_enabled) + return; + + /* + * We can't really do much about bad xfers. + * Afterall, the ISOCH xfers could fail legitimately. + */ + if (status) + pr_debug("%s: iso_complete status(%d) %d/%d\n", + __func__, status, req->actual, req->length); + + substream = prm->ss; + + /* Do nothing if ALSA isn't active */ + if (!substream) + goto exit; + + spin_lock_irqsave(&prm->lock, flags); + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + src = prm->dma_area + prm->hw_ptr; + req->actual = req->length; + dst = req->buf; + } else { + dst = prm->dma_area + prm->hw_ptr; + src = req->buf; + } + + pending = prm->hw_ptr % prm->period_size; + pending += req->actual; + if (pending >= prm->period_size) + update_alsa = true; + + prm->hw_ptr = (prm->hw_ptr + req->actual) % prm->dma_bytes; + + spin_unlock_irqrestore(&prm->lock, flags); + + /* Pack USB load in ALSA ring buffer */ + memcpy(dst, src, req->actual); +exit: + if (usb_ep_queue(ep, req, GFP_ATOMIC)) + dev_err(&uac2->pdev.dev, "%d Error!\n", __LINE__); + + if (update_alsa) + snd_pcm_period_elapsed(substream); + + return; +} + +static int +uac2_pcm_trigger(struct snd_pcm_substream *substream, int cmd) +{ + struct snd_uac2_chip *uac2 = snd_pcm_substream_chip(substream); + struct audio_dev *agdev = uac2_to_agdev(uac2); + struct uac2_rtd_params *prm; + unsigned long flags; + struct usb_ep *ep; + int err = 0; + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + ep = agdev->in_ep; + prm = &uac2->p_prm; + } else { + ep = agdev->out_ep; + prm = &uac2->c_prm; + } + + spin_lock_irqsave(&prm->lock, flags); + + /* Reset */ + prm->hw_ptr = 0; + + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + case SNDRV_PCM_TRIGGER_RESUME: + prm->ss = substream; + break; + case SNDRV_PCM_TRIGGER_STOP: + case SNDRV_PCM_TRIGGER_SUSPEND: + prm->ss = NULL; + break; + default: + err = -EINVAL; + } + + spin_unlock_irqrestore(&prm->lock, flags); + + /* Clear buffer after Play stops */ + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK && !prm->ss) + memset(prm->rbuf, 0, prm->max_psize * USB_XFERS); + + return err; +} + +static snd_pcm_uframes_t uac2_pcm_pointer(struct snd_pcm_substream *substream) +{ + struct snd_uac2_chip *uac2 = snd_pcm_substream_chip(substream); + struct uac2_rtd_params *prm; + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + prm = &uac2->p_prm; + else + prm = &uac2->c_prm; + + return bytes_to_frames(substream->runtime, prm->hw_ptr); +} + +static int uac2_pcm_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *hw_params) +{ + struct snd_uac2_chip *uac2 = snd_pcm_substream_chip(substream); + struct uac2_rtd_params *prm; + int err; + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + prm = &uac2->p_prm; + else + prm = &uac2->c_prm; + + err = snd_pcm_lib_malloc_pages(substream, + params_buffer_bytes(hw_params)); + if (err >= 0) { + prm->dma_bytes = substream->runtime->dma_bytes; + prm->dma_area = substream->runtime->dma_area; + prm->period_size = params_period_bytes(hw_params); + } + + return err; +} + +static int uac2_pcm_hw_free(struct snd_pcm_substream *substream) +{ + struct snd_uac2_chip *uac2 = snd_pcm_substream_chip(substream); + struct uac2_rtd_params *prm; + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + prm = &uac2->p_prm; + else + prm = &uac2->c_prm; + + prm->dma_area = NULL; + prm->dma_bytes = 0; + prm->period_size = 0; + + return snd_pcm_lib_free_pages(substream); +} + +static int uac2_pcm_open(struct snd_pcm_substream *substream) +{ + struct snd_uac2_chip *uac2 = snd_pcm_substream_chip(substream); + struct snd_pcm_runtime *runtime = substream->runtime; + + runtime->hw = uac2_pcm_hardware; + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + spin_lock_init(&uac2->p_prm.lock); + runtime->hw.rate_min = p_srate; + runtime->hw.formats = SNDRV_PCM_FMTBIT_S16_LE; /* ! p_ssize ! */ + runtime->hw.channels_min = num_channels(p_chmask); + runtime->hw.period_bytes_min = 2 * uac2->p_prm.max_psize + / runtime->hw.periods_min; + } else { + spin_lock_init(&uac2->c_prm.lock); + runtime->hw.rate_min = c_srate; + runtime->hw.formats = SNDRV_PCM_FMTBIT_S16_LE; /* ! c_ssize ! */ + runtime->hw.channels_min = num_channels(c_chmask); + runtime->hw.period_bytes_min = 2 * uac2->c_prm.max_psize + / runtime->hw.periods_min; + } + + runtime->hw.rate_max = runtime->hw.rate_min; + runtime->hw.channels_max = runtime->hw.channels_min; + + snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS); + + return 0; +} + +/* ALSA cries without these function pointers */ +static int uac2_pcm_null(struct snd_pcm_substream *substream) +{ + return 0; +} + +static struct snd_pcm_ops uac2_pcm_ops = { + .open = uac2_pcm_open, + .close = uac2_pcm_null, + .ioctl = snd_pcm_lib_ioctl, + .hw_params = uac2_pcm_hw_params, + .hw_free = uac2_pcm_hw_free, + .trigger = uac2_pcm_trigger, + .pointer = uac2_pcm_pointer, + .prepare = uac2_pcm_null, +}; + +static int __devinit snd_uac2_probe(struct platform_device *pdev) +{ + struct snd_uac2_chip *uac2 = pdev_to_uac2(pdev); + struct snd_card *card; + struct snd_pcm *pcm; + int err; + + /* Choose any slot, with no id */ + err = snd_card_create(-1, NULL, THIS_MODULE, 0, &card); + if (err < 0) + return err; + + uac2->card = card; + + /* + * Create first PCM device + * Create a substream only for non-zero channel streams + */ + err = snd_pcm_new(uac2->card, "UAC2 PCM", 0, + p_chmask ? 1 : 0, c_chmask ? 1 : 0, &pcm); + if (err < 0) + goto snd_fail; + + strcpy(pcm->name, "UAC2 PCM"); + pcm->private_data = uac2; + + uac2->pcm = pcm; + + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &uac2_pcm_ops); + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &uac2_pcm_ops); + + strcpy(card->driver, "UAC2_Gadget"); + strcpy(card->shortname, "UAC2_Gadget"); + sprintf(card->longname, "UAC2_Gadget %i", pdev->id); + + snd_card_set_dev(card, &pdev->dev); + + snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_CONTINUOUS, + snd_dma_continuous_data(GFP_KERNEL), 0, BUFF_SIZE_MAX); + + err = snd_card_register(card); + if (!err) { + platform_set_drvdata(pdev, card); + return 0; + } + +snd_fail: + snd_card_free(card); + + uac2->pcm = NULL; + uac2->card = NULL; + + return err; +} + +static int __devexit snd_uac2_remove(struct platform_device *pdev) +{ + struct snd_card *card = platform_get_drvdata(pdev); + + platform_set_drvdata(pdev, NULL); + + if (card) + return snd_card_free(card); + + return 0; +} + +static int alsa_uac2_init(struct audio_dev *agdev) +{ + struct snd_uac2_chip *uac2 = &agdev->uac2; + int err; + + uac2->pdrv.probe = snd_uac2_probe; + uac2->pdrv.remove = snd_uac2_remove; + uac2->pdrv.driver.name = uac2_name; + + uac2->pdev.id = 0; + uac2->pdev.name = uac2_name; + + /* Register snd_uac2 driver */ + err = platform_driver_register(&uac2->pdrv); + if (err) + return err; + + /* Register snd_uac2 device */ + err = platform_device_register(&uac2->pdev); + if (err) + platform_driver_unregister(&uac2->pdrv); + + return err; +} + +static void alsa_uac2_exit(struct audio_dev *agdev) +{ + struct snd_uac2_chip *uac2 = &agdev->uac2; + + platform_driver_unregister(&uac2->pdrv); + platform_device_unregister(&uac2->pdev); +} + + +/* --------- USB Function Interface ------------- */ + +enum { + STR_ASSOC, + STR_IF_CTRL, + STR_CLKSRC_IN, + STR_CLKSRC_OUT, + STR_USB_IT, + STR_IO_IT, + STR_USB_OT, + STR_IO_OT, + STR_AS_OUT_ALT0, + STR_AS_OUT_ALT1, + STR_AS_IN_ALT0, + STR_AS_IN_ALT1, +}; + +static const char ifassoc[] = "Source/Sink"; +static const char ifctrl[] = "Topology Control"; +static char clksrc_in[8]; +static char clksrc_out[8]; +static const char usb_it[] = "USBH Out"; +static const char io_it[] = "USBD Out"; +static const char usb_ot[] = "USBH In"; +static const char io_ot[] = "USBD In"; +static const char out_alt0[] = "Playback Inactive"; +static const char out_alt1[] = "Playback Active"; +static const char in_alt0[] = "Capture Inactive"; +static const char in_alt1[] = "Capture Active"; + +static struct usb_string strings_fn[] = { + [STR_ASSOC].s = ifassoc, + [STR_IF_CTRL].s = ifctrl, + [STR_CLKSRC_IN].s = clksrc_in, + [STR_CLKSRC_OUT].s = clksrc_out, + [STR_USB_IT].s = usb_it, + [STR_IO_IT].s = io_it, + [STR_USB_OT].s = usb_ot, + [STR_IO_OT].s = io_ot, + [STR_AS_OUT_ALT0].s = out_alt0, + [STR_AS_OUT_ALT1].s = out_alt1, + [STR_AS_IN_ALT0].s = in_alt0, + [STR_AS_IN_ALT1].s = in_alt1, + { }, +}; + +static struct usb_gadget_strings str_fn = { + .language = 0x0409, /* en-us */ + .strings = strings_fn, +}; + +static struct usb_gadget_strings *fn_strings[] = { + &str_fn, + NULL, +}; + +static struct usb_qualifier_descriptor devqual_desc = { + .bLength = sizeof devqual_desc, + .bDescriptorType = USB_DT_DEVICE_QUALIFIER, + + .bcdUSB = cpu_to_le16(0x200), + .bDeviceClass = USB_CLASS_MISC, + .bDeviceSubClass = 0x02, + .bDeviceProtocol = 0x01, + .bNumConfigurations = 1, + .bRESERVED = 0, +}; + +static struct usb_interface_assoc_descriptor iad_desc = { + .bLength = sizeof iad_desc, + .bDescriptorType = USB_DT_INTERFACE_ASSOCIATION, + + .bFirstInterface = 0, + .bInterfaceCount = 3, + .bFunctionClass = USB_CLASS_AUDIO, + .bFunctionSubClass = UAC2_FUNCTION_SUBCLASS_UNDEFINED, + .bFunctionProtocol = UAC_VERSION_2, +}; + +/* Audio Control Interface */ +static struct usb_interface_descriptor std_ac_if_desc = { + .bLength = sizeof std_ac_if_desc, + .bDescriptorType = USB_DT_INTERFACE, + + .bAlternateSetting = 0, + .bNumEndpoints = 0, + .bInterfaceClass = USB_CLASS_AUDIO, + .bInterfaceSubClass = USB_SUBCLASS_AUDIOCONTROL, + .bInterfaceProtocol = UAC_VERSION_2, +}; + +/* Clock source for IN traffic */ +struct uac_clock_source_descriptor in_clk_src_desc = { + .bLength = sizeof in_clk_src_desc, + .bDescriptorType = USB_DT_CS_INTERFACE, + + .bDescriptorSubtype = UAC2_CLOCK_SOURCE, + .bClockID = USB_IN_CLK_ID, + .bmAttributes = UAC_CLOCK_SOURCE_TYPE_INT_FIXED, + .bmControls = (CONTROL_RDONLY << CLK_FREQ_CTRL), + .bAssocTerminal = 0, +}; + +/* Clock source for OUT traffic */ +struct uac_clock_source_descriptor out_clk_src_desc = { + .bLength = sizeof out_clk_src_desc, + .bDescriptorType = USB_DT_CS_INTERFACE, + + .bDescriptorSubtype = UAC2_CLOCK_SOURCE, + .bClockID = USB_OUT_CLK_ID, + .bmAttributes = UAC_CLOCK_SOURCE_TYPE_INT_FIXED, + .bmControls = (CONTROL_RDONLY << CLK_FREQ_CTRL), + .bAssocTerminal = 0, +}; + +/* Input Terminal for USB_OUT */ +struct uac2_input_terminal_descriptor usb_out_it_desc = { + .bLength = sizeof usb_out_it_desc, + .bDescriptorType = USB_DT_CS_INTERFACE, + + .bDescriptorSubtype = UAC_INPUT_TERMINAL, + .bTerminalID = USB_OUT_IT_ID, + .wTerminalType = cpu_to_le16(UAC_TERMINAL_STREAMING), + .bAssocTerminal = 0, + .bCSourceID = USB_OUT_CLK_ID, + .iChannelNames = 0, + .bmControls = (CONTROL_RDWR << COPY_CTRL), +}; + +/* Input Terminal for I/O-In */ +struct uac2_input_terminal_descriptor io_in_it_desc = { + .bLength = sizeof io_in_it_desc, + .bDescriptorType = USB_DT_CS_INTERFACE, + + .bDescriptorSubtype = UAC_INPUT_TERMINAL, + .bTerminalID = IO_IN_IT_ID, + .wTerminalType = cpu_to_le16(UAC_INPUT_TERMINAL_UNDEFINED), + .bAssocTerminal = 0, + .bCSourceID = USB_IN_CLK_ID, + .iChannelNames = 0, + .bmControls = (CONTROL_RDWR << COPY_CTRL), +}; + +/* Ouput Terminal for USB_IN */ +struct uac2_output_terminal_descriptor usb_in_ot_desc = { + .bLength = sizeof usb_in_ot_desc, + .bDescriptorType = USB_DT_CS_INTERFACE, + + .bDescriptorSubtype = UAC_OUTPUT_TERMINAL, + .bTerminalID = USB_IN_OT_ID, + .wTerminalType = cpu_to_le16(UAC_TERMINAL_STREAMING), + .bAssocTerminal = 0, + .bSourceID = IO_IN_IT_ID, + .bCSourceID = USB_IN_CLK_ID, + .bmControls = (CONTROL_RDWR << COPY_CTRL), +}; + +/* Ouput Terminal for I/O-Out */ +struct uac2_output_terminal_descriptor io_out_ot_desc = { + .bLength = sizeof io_out_ot_desc, + .bDescriptorType = USB_DT_CS_INTERFACE, + + .bDescriptorSubtype = UAC_OUTPUT_TERMINAL, + .bTerminalID = IO_OUT_OT_ID, + .wTerminalType = cpu_to_le16(UAC_OUTPUT_TERMINAL_UNDEFINED), + .bAssocTerminal = 0, + .bSourceID = USB_OUT_IT_ID, + .bCSourceID = USB_OUT_CLK_ID, + .bmControls = (CONTROL_RDWR << COPY_CTRL), +}; + +struct uac2_ac_header_descriptor ac_hdr_desc = { + .bLength = sizeof ac_hdr_desc, + .bDescriptorType = USB_DT_CS_INTERFACE, + + .bDescriptorSubtype = UAC_MS_HEADER, + .bcdADC = cpu_to_le16(0x200), + .bCategory = UAC2_FUNCTION_IO_BOX, + .wTotalLength = sizeof in_clk_src_desc + sizeof out_clk_src_desc + + sizeof usb_out_it_desc + sizeof io_in_it_desc + + sizeof usb_in_ot_desc + sizeof io_out_ot_desc, + .bmControls = 0, +}; + +/* Audio Streaming OUT Interface - Alt0 */ +static struct usb_interface_descriptor std_as_out_if0_desc = { + .bLength = sizeof std_as_out_if0_desc, + .bDescriptorType = USB_DT_INTERFACE, + + .bAlternateSetting = 0, + .bNumEndpoints = 0, + .bInterfaceClass = USB_CLASS_AUDIO, + .bInterfaceSubClass = USB_SUBCLASS_AUDIOSTREAMING, + .bInterfaceProtocol = UAC_VERSION_2, +}; + +/* Audio Streaming OUT Interface - Alt1 */ +static struct usb_interface_descriptor std_as_out_if1_desc = { + .bLength = sizeof std_as_out_if1_desc, + .bDescriptorType = USB_DT_INTERFACE, + + .bAlternateSetting = 1, + .bNumEndpoints = 1, + .bInterfaceClass = USB_CLASS_AUDIO, + .bInterfaceSubClass = USB_SUBCLASS_AUDIOSTREAMING, + .bInterfaceProtocol = UAC_VERSION_2, +}; + +/* Audio Stream OUT Intface Desc */ +struct uac2_as_header_descriptor as_out_hdr_desc = { + .bLength = sizeof as_out_hdr_desc, + .bDescriptorType = USB_DT_CS_INTERFACE, + + .bDescriptorSubtype = UAC_AS_GENERAL, + .bTerminalLink = USB_OUT_IT_ID, + .bmControls = 0, + .bFormatType = UAC_FORMAT_TYPE_I, + .bmFormats = cpu_to_le32(UAC_FORMAT_TYPE_I_PCM), + .iChannelNames = 0, +}; + +/* Audio USB_OUT Format */ +struct uac2_format_type_i_descriptor as_out_fmt1_desc = { + .bLength = sizeof as_out_fmt1_desc, + .bDescriptorType = USB_DT_CS_INTERFACE, + .bDescriptorSubtype = UAC_FORMAT_TYPE, + .bFormatType = UAC_FORMAT_TYPE_I, +}; + +/* STD AS ISO OUT Endpoint */ +struct usb_endpoint_descriptor fs_epout_desc = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + + .bEndpointAddress = USB_DIR_OUT, + .bmAttributes = USB_ENDPOINT_XFER_ISOC | USB_ENDPOINT_SYNC_ASYNC, + .bInterval = 1, +}; + +struct usb_endpoint_descriptor hs_epout_desc = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + + .bmAttributes = USB_ENDPOINT_XFER_ISOC | USB_ENDPOINT_SYNC_ASYNC, + .bInterval = 4, +}; + +/* CS AS ISO OUT Endpoint */ +static struct uac2_iso_endpoint_descriptor as_iso_out_desc = { + .bLength = sizeof as_iso_out_desc, + .bDescriptorType = USB_DT_CS_ENDPOINT, + + .bDescriptorSubtype = UAC_EP_GENERAL, + .bmAttributes = 0, + .bmControls = 0, + .bLockDelayUnits = 0, + .wLockDelay = 0, +}; + +/* Audio Streaming IN Interface - Alt0 */ +static struct usb_interface_descriptor std_as_in_if0_desc = { + .bLength = sizeof std_as_in_if0_desc, + .bDescriptorType = USB_DT_INTERFACE, + + .bAlternateSetting = 0, + .bNumEndpoints = 0, + .bInterfaceClass = USB_CLASS_AUDIO, + .bInterfaceSubClass = USB_SUBCLASS_AUDIOSTREAMING, + .bInterfaceProtocol = UAC_VERSION_2, +}; + +/* Audio Streaming IN Interface - Alt1 */ +static struct usb_interface_descriptor std_as_in_if1_desc = { + .bLength = sizeof std_as_in_if1_desc, + .bDescriptorType = USB_DT_INTERFACE, + + .bAlternateSetting = 1, + .bNumEndpoints = 1, + .bInterfaceClass = USB_CLASS_AUDIO, + .bInterfaceSubClass = USB_SUBCLASS_AUDIOSTREAMING, + .bInterfaceProtocol = UAC_VERSION_2, +}; + +/* Audio Stream IN Intface Desc */ +struct uac2_as_header_descriptor as_in_hdr_desc = { + .bLength = sizeof as_in_hdr_desc, + .bDescriptorType = USB_DT_CS_INTERFACE, + + .bDescriptorSubtype = UAC_AS_GENERAL, + .bTerminalLink = USB_IN_OT_ID, + .bmControls = 0, + .bFormatType = UAC_FORMAT_TYPE_I, + .bmFormats = cpu_to_le32(UAC_FORMAT_TYPE_I_PCM), + .iChannelNames = 0, +}; + +/* Audio USB_IN Format */ +struct uac2_format_type_i_descriptor as_in_fmt1_desc = { + .bLength = sizeof as_in_fmt1_desc, + .bDescriptorType = USB_DT_CS_INTERFACE, + .bDescriptorSubtype = UAC_FORMAT_TYPE, + .bFormatType = UAC_FORMAT_TYPE_I, +}; + +/* STD AS ISO IN Endpoint */ +struct usb_endpoint_descriptor fs_epin_desc = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + + .bEndpointAddress = USB_DIR_IN, + .bmAttributes = USB_ENDPOINT_XFER_ISOC | USB_ENDPOINT_SYNC_ASYNC, + .bInterval = 1, +}; + +struct usb_endpoint_descriptor hs_epin_desc = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + + .bmAttributes = USB_ENDPOINT_XFER_ISOC | USB_ENDPOINT_SYNC_ASYNC, + .bInterval = 4, +}; + +/* CS AS ISO IN Endpoint */ +static struct uac2_iso_endpoint_descriptor as_iso_in_desc = { + .bLength = sizeof as_iso_in_desc, + .bDescriptorType = USB_DT_CS_ENDPOINT, + + .bDescriptorSubtype = UAC_EP_GENERAL, + .bmAttributes = 0, + .bmControls = 0, + .bLockDelayUnits = 0, + .wLockDelay = 0, +}; + +static struct usb_descriptor_header *fs_audio_desc[] = { + (struct usb_descriptor_header *)&iad_desc, + (struct usb_descriptor_header *)&std_ac_if_desc, + + (struct usb_descriptor_header *)&ac_hdr_desc, + (struct usb_descriptor_header *)&in_clk_src_desc, + (struct usb_descriptor_header *)&out_clk_src_desc, + (struct usb_descriptor_header *)&usb_out_it_desc, + (struct usb_descriptor_header *)&io_in_it_desc, + (struct usb_descriptor_header *)&usb_in_ot_desc, + (struct usb_descriptor_header *)&io_out_ot_desc, + + (struct usb_descriptor_header *)&std_as_out_if0_desc, + (struct usb_descriptor_header *)&std_as_out_if1_desc, + + (struct usb_descriptor_header *)&as_out_hdr_desc, + (struct usb_descriptor_header *)&as_out_fmt1_desc, + (struct usb_descriptor_header *)&fs_epout_desc, + (struct usb_descriptor_header *)&as_iso_out_desc, + + (struct usb_descriptor_header *)&std_as_in_if0_desc, + (struct usb_descriptor_header *)&std_as_in_if1_desc, + + (struct usb_descriptor_header *)&as_in_hdr_desc, + (struct usb_descriptor_header *)&as_in_fmt1_desc, + (struct usb_descriptor_header *)&fs_epin_desc, + (struct usb_descriptor_header *)&as_iso_in_desc, + NULL, +}; + +static struct usb_descriptor_header *hs_audio_desc[] = { + (struct usb_descriptor_header *)&iad_desc, + (struct usb_descriptor_header *)&std_ac_if_desc, + + (struct usb_descriptor_header *)&ac_hdr_desc, + (struct usb_descriptor_header *)&in_clk_src_desc, + (struct usb_descriptor_header *)&out_clk_src_desc, + (struct usb_descriptor_header *)&usb_out_it_desc, + (struct usb_descriptor_header *)&io_in_it_desc, + (struct usb_descriptor_header *)&usb_in_ot_desc, + (struct usb_descriptor_header *)&io_out_ot_desc, + + (struct usb_descriptor_header *)&std_as_out_if0_desc, + (struct usb_descriptor_header *)&std_as_out_if1_desc, + + (struct usb_descriptor_header *)&as_out_hdr_desc, + (struct usb_descriptor_header *)&as_out_fmt1_desc, + (struct usb_descriptor_header *)&hs_epout_desc, + (struct usb_descriptor_header *)&as_iso_out_desc, + + (struct usb_descriptor_header *)&std_as_in_if0_desc, + (struct usb_descriptor_header *)&std_as_in_if1_desc, + + (struct usb_descriptor_header *)&as_in_hdr_desc, + (struct usb_descriptor_header *)&as_in_fmt1_desc, + (struct usb_descriptor_header *)&hs_epin_desc, + (struct usb_descriptor_header *)&as_iso_in_desc, + NULL, +}; + +struct cntrl_cur_lay3 { + __u32 dCUR; +}; + +struct cntrl_range_lay3 { + __u16 wNumSubRanges; + __u32 dMIN; + __u32 dMAX; + __u32 dRES; +} __packed; + +static inline void +free_ep(struct uac2_rtd_params *prm, struct usb_ep *ep) +{ + struct snd_uac2_chip *uac2 = prm_to_uac2(prm); + int i; + + prm->ep_enabled = false; + + for (i = 0; i < USB_XFERS; i++) { + if (prm->ureq[i].req) { + usb_ep_dequeue(ep, prm->ureq[i].req); + usb_ep_free_request(ep, prm->ureq[i].req); + prm->ureq[i].req = NULL; + } + } + + if (usb_ep_disable(ep)) + dev_err(&uac2->pdev.dev, + "%s:%d Error!\n", __func__, __LINE__); +} + +static int __init +afunc_bind(struct usb_configuration *cfg, struct usb_function *fn) +{ + struct audio_dev *agdev = func_to_agdev(fn); + struct snd_uac2_chip *uac2 = &agdev->uac2; + struct usb_composite_dev *cdev = cfg->cdev; + struct usb_gadget *gadget = cdev->gadget; + struct uac2_rtd_params *prm; + int ret; + + ret = usb_interface_id(cfg, fn); + if (ret < 0) { + dev_err(&uac2->pdev.dev, + "%s:%d Error!\n", __func__, __LINE__); + return ret; + } + std_ac_if_desc.bInterfaceNumber = ret; + ALT_SET(agdev->ac_alt, 0); + INTF_SET(agdev->ac_alt, ret); + + ret = usb_interface_id(cfg, fn); + if (ret < 0) { + dev_err(&uac2->pdev.dev, + "%s:%d Error!\n", __func__, __LINE__); + return ret; + } + std_as_out_if0_desc.bInterfaceNumber = ret; + std_as_out_if1_desc.bInterfaceNumber = ret; + ALT_SET(agdev->as_out_alt, 0); + INTF_SET(agdev->as_out_alt, ret); + + ret = usb_interface_id(cfg, fn); + if (ret < 0) { + dev_err(&uac2->pdev.dev, + "%s:%d Error!\n", __func__, __LINE__); + return ret; + } + std_as_in_if0_desc.bInterfaceNumber = ret; + std_as_in_if1_desc.bInterfaceNumber = ret; + ALT_SET(agdev->as_in_alt, 0); + INTF_SET(agdev->as_in_alt, ret); + + agdev->out_ep = usb_ep_autoconfig(gadget, &fs_epout_desc); + if (!agdev->out_ep) + dev_err(&uac2->pdev.dev, + "%s:%d Error!\n", __func__, __LINE__); + agdev->out_ep->driver_data = agdev; + + agdev->in_ep = usb_ep_autoconfig(gadget, &fs_epin_desc); + if (!agdev->in_ep) + dev_err(&uac2->pdev.dev, + "%s:%d Error!\n", __func__, __LINE__); + agdev->in_ep->driver_data = agdev; + + hs_epout_desc.bEndpointAddress = fs_epout_desc.bEndpointAddress; + hs_epout_desc.wMaxPacketSize = fs_epout_desc.wMaxPacketSize; + hs_epin_desc.bEndpointAddress = fs_epin_desc.bEndpointAddress; + hs_epin_desc.wMaxPacketSize = fs_epin_desc.wMaxPacketSize; + + fn->descriptors = usb_copy_descriptors(fs_audio_desc); + if (gadget_is_dualspeed(gadget)) + fn->hs_descriptors = usb_copy_descriptors(hs_audio_desc); + + prm = &agdev->uac2.c_prm; + prm->max_psize = hs_epout_desc.wMaxPacketSize; + prm->rbuf = kzalloc(prm->max_psize * USB_XFERS, GFP_KERNEL); + if (!prm->rbuf) { + prm->max_psize = 0; + dev_err(&uac2->pdev.dev, + "%s:%d Error!\n", __func__, __LINE__); + } + + prm = &agdev->uac2.p_prm; + prm->max_psize = hs_epin_desc.wMaxPacketSize; + prm->rbuf = kzalloc(prm->max_psize * USB_XFERS, GFP_KERNEL); + if (!prm->rbuf) { + prm->max_psize = 0; + dev_err(&uac2->pdev.dev, + "%s:%d Error!\n", __func__, __LINE__); + } + + return alsa_uac2_init(agdev); +} + +static void +afunc_unbind(struct usb_configuration *cfg, struct usb_function *fn) +{ + struct audio_dev *agdev = func_to_agdev(fn); + struct usb_composite_dev *cdev = cfg->cdev; + struct usb_gadget *gadget = cdev->gadget; + struct uac2_rtd_params *prm; + + alsa_uac2_exit(agdev); + + prm = &agdev->uac2.p_prm; + kfree(prm->rbuf); + + prm = &agdev->uac2.c_prm; + kfree(prm->rbuf); + + if (gadget_is_dualspeed(gadget)) + usb_free_descriptors(fn->hs_descriptors); + usb_free_descriptors(fn->descriptors); + + if (agdev->in_ep) + agdev->in_ep->driver_data = NULL; + if (agdev->out_ep) + agdev->out_ep->driver_data = NULL; +} + +static int +afunc_set_alt(struct usb_function *fn, unsigned intf, unsigned alt) +{ + struct usb_composite_dev *cdev = fn->config->cdev; + struct audio_dev *agdev = func_to_agdev(fn); + struct snd_uac2_chip *uac2 = &agdev->uac2; + struct usb_gadget *gadget = cdev->gadget; + struct usb_request *req; + struct usb_ep *ep; + struct uac2_rtd_params *prm; + int i; + + /* No i/f has more than 2 alt settings */ + if (alt > 1) { + dev_err(&uac2->pdev.dev, + "%s:%d Error!\n", __func__, __LINE__); + return -EINVAL; + } + + if (intf == INTF_GET(agdev->ac_alt)) { + /* Control I/f has only 1 AltSetting - 0 */ + if (alt) { + dev_err(&uac2->pdev.dev, + "%s:%d Error!\n", __func__, __LINE__); + return -EINVAL; + } + return 0; + } + + if (intf == INTF_GET(agdev->as_out_alt)) { + ep = agdev->out_ep; + prm = &uac2->c_prm; + config_ep_by_speed(gadget, fn, ep); + ALT_SET(agdev->as_out_alt, alt); + } else if (intf == INTF_GET(agdev->as_in_alt)) { + ep = agdev->in_ep; + prm = &uac2->p_prm; + config_ep_by_speed(gadget, fn, ep); + ALT_SET(agdev->as_in_alt, alt); + } else { + dev_err(&uac2->pdev.dev, + "%s:%d Error!\n", __func__, __LINE__); + return -EINVAL; + } + + if (alt == 0) { + free_ep(prm, ep); + return 0; + } + + prm->ep_enabled = true; + usb_ep_enable(ep); + + for (i = 0; i < USB_XFERS; i++) { + if (prm->ureq[i].req) { + if (usb_ep_queue(ep, prm->ureq[i].req, GFP_ATOMIC)) + dev_err(&uac2->pdev.dev, "%d Error!\n", + __LINE__); + continue; + } + + req = usb_ep_alloc_request(ep, GFP_ATOMIC); + if (req == NULL) { + dev_err(&uac2->pdev.dev, + "%s:%d Error!\n", __func__, __LINE__); + return -EINVAL; + } + + prm->ureq[i].req = req; + prm->ureq[i].pp = prm; + + req->zero = 0; + req->dma = DMA_ADDR_INVALID; + req->context = &prm->ureq[i]; + req->length = prm->max_psize; + req->complete = agdev_iso_complete; + req->buf = prm->rbuf + i * req->length; + + if (usb_ep_queue(ep, req, GFP_ATOMIC)) + dev_err(&uac2->pdev.dev, "%d Error!\n", __LINE__); + } + + return 0; +} + +static int +afunc_get_alt(struct usb_function *fn, unsigned intf) +{ + struct audio_dev *agdev = func_to_agdev(fn); + struct snd_uac2_chip *uac2 = &agdev->uac2; + + if (intf == INTF_GET(agdev->ac_alt)) + return ALT_GET(agdev->ac_alt); + else if (intf == INTF_GET(agdev->as_out_alt)) + return ALT_GET(agdev->as_out_alt); + else if (intf == INTF_GET(agdev->as_in_alt)) + return ALT_GET(agdev->as_in_alt); + else + dev_err(&uac2->pdev.dev, + "%s:%d Invalid Interface %d!\n", + __func__, __LINE__, intf); + + return -EINVAL; +} + +static void +afunc_disable(struct usb_function *fn) +{ + struct audio_dev *agdev = func_to_agdev(fn); + struct snd_uac2_chip *uac2 = &agdev->uac2; + + free_ep(&uac2->p_prm, agdev->in_ep); + ALT_SET(agdev->as_in_alt, 0); + + free_ep(&uac2->c_prm, agdev->out_ep); + ALT_SET(agdev->as_out_alt, 0); +} + +static int +in_rq_cur(struct usb_function *fn, const struct usb_ctrlrequest *cr) +{ + struct usb_request *req = fn->config->cdev->req; + struct audio_dev *agdev = func_to_agdev(fn); + struct snd_uac2_chip *uac2 = &agdev->uac2; + u16 w_length = le16_to_cpu(cr->wLength); + u16 w_index = le16_to_cpu(cr->wIndex); + u16 w_value = le16_to_cpu(cr->wValue); + u8 entity_id = (w_index >> 8) & 0xff; + u8 control_selector = w_value >> 8; + int value = -EOPNOTSUPP; + + if (control_selector == UAC2_CS_CONTROL_SAM_FREQ) { + struct cntrl_cur_lay3 c; + + if (entity_id == USB_IN_CLK_ID) + c.dCUR = p_srate; + else if (entity_id == USB_OUT_CLK_ID) + c.dCUR = c_srate; + + value = min_t(unsigned, w_length, sizeof c); + memcpy(req->buf, &c, value); + } else if (control_selector == UAC2_CS_CONTROL_CLOCK_VALID) { + *(u8 *)req->buf = 1; + value = min_t(unsigned, w_length, 1); + } else { + dev_err(&uac2->pdev.dev, + "%s:%d control_selector=%d TODO!\n", + __func__, __LINE__, control_selector); + } + + return value; +} + +static int +in_rq_range(struct usb_function *fn, const struct usb_ctrlrequest *cr) +{ + struct usb_request *req = fn->config->cdev->req; + struct audio_dev *agdev = func_to_agdev(fn); + struct snd_uac2_chip *uac2 = &agdev->uac2; + u16 w_length = le16_to_cpu(cr->wLength); + u16 w_index = le16_to_cpu(cr->wIndex); + u16 w_value = le16_to_cpu(cr->wValue); + u8 entity_id = (w_index >> 8) & 0xff; + u8 control_selector = w_value >> 8; + struct cntrl_range_lay3 r; + int value = -EOPNOTSUPP; + + if (control_selector == UAC2_CS_CONTROL_SAM_FREQ) { + if (entity_id == USB_IN_CLK_ID) + r.dMIN = p_srate; + else if (entity_id == USB_OUT_CLK_ID) + r.dMIN = c_srate; + else + return -EOPNOTSUPP; + + r.dMAX = r.dMIN; + r.dRES = 0; + r.wNumSubRanges = 1; + + value = min_t(unsigned, w_length, sizeof r); + memcpy(req->buf, &r, value); + } else { + dev_err(&uac2->pdev.dev, + "%s:%d control_selector=%d TODO!\n", + __func__, __LINE__, control_selector); + } + + return value; +} + +static int +ac_rq_in(struct usb_function *fn, const struct usb_ctrlrequest *cr) +{ + if (cr->bRequest == UAC2_CS_CUR) + return in_rq_cur(fn, cr); + else if (cr->bRequest == UAC2_CS_RANGE) + return in_rq_range(fn, cr); + else + return -EOPNOTSUPP; +} + +static int +out_rq_cur(struct usb_function *fn, const struct usb_ctrlrequest *cr) +{ + u16 w_length = le16_to_cpu(cr->wLength); + u16 w_value = le16_to_cpu(cr->wValue); + u8 control_selector = w_value >> 8; + + if (control_selector == UAC2_CS_CONTROL_SAM_FREQ) + return w_length; + + return -EOPNOTSUPP; +} + +static int +setup_rq_inf(struct usb_function *fn, const struct usb_ctrlrequest *cr) +{ + struct audio_dev *agdev = func_to_agdev(fn); + struct snd_uac2_chip *uac2 = &agdev->uac2; + u16 w_index = le16_to_cpu(cr->wIndex); + u8 intf = w_index & 0xff; + + if (intf != INTF_GET(agdev->ac_alt)) { + dev_err(&uac2->pdev.dev, + "%s:%d Error!\n", __func__, __LINE__); + return -EOPNOTSUPP; + } + + if (cr->bRequestType & USB_DIR_IN) + return ac_rq_in(fn, cr); + else if (cr->bRequest == UAC2_CS_CUR) + return out_rq_cur(fn, cr); + + return -EOPNOTSUPP; +} + +static int +afunc_setup(struct usb_function *fn, const struct usb_ctrlrequest *cr) +{ + struct usb_composite_dev *cdev = fn->config->cdev; + struct audio_dev *agdev = func_to_agdev(fn); + struct snd_uac2_chip *uac2 = &agdev->uac2; + struct usb_request *req = cdev->req; + u16 w_length = le16_to_cpu(cr->wLength); + int value = -EOPNOTSUPP; + + /* Only Class specific requests are supposed to reach here */ + if ((cr->bRequestType & USB_TYPE_MASK) != USB_TYPE_CLASS) + return -EOPNOTSUPP; + + if ((cr->bRequestType & USB_RECIP_MASK) == USB_RECIP_INTERFACE) + value = setup_rq_inf(fn, cr); + else + dev_err(&uac2->pdev.dev, "%s:%d Error!\n", __func__, __LINE__); + + if (value >= 0) { + req->length = value; + req->zero = value < w_length; + value = usb_ep_queue(cdev->gadget->ep0, req, GFP_ATOMIC); + if (value < 0) { + dev_err(&uac2->pdev.dev, + "%s:%d Error!\n", __func__, __LINE__); + req->status = 0; + } + } + + return value; +} + +static int audio_bind_config(struct usb_configuration *cfg) +{ + int id, res; + + agdev_g = kzalloc(sizeof *agdev_g, GFP_KERNEL); + if (agdev_g == NULL) { + printk(KERN_ERR "Unable to allocate audio gadget\n"); + return -ENOMEM; + } + + id = usb_string_id(cfg->cdev); + if (id < 0) + return id; + + strings_fn[STR_ASSOC].id = id; + iad_desc.iFunction = id, + + id = usb_string_id(cfg->cdev); + if (id < 0) + return id; + + strings_fn[STR_IF_CTRL].id = id; + std_ac_if_desc.iInterface = id, + + id = usb_string_id(cfg->cdev); + if (id < 0) + return id; + + strings_fn[STR_CLKSRC_IN].id = id; + in_clk_src_desc.iClockSource = id, + + id = usb_string_id(cfg->cdev); + if (id < 0) + return id; + + strings_fn[STR_CLKSRC_OUT].id = id; + out_clk_src_desc.iClockSource = id, + + id = usb_string_id(cfg->cdev); + if (id < 0) + return id; + + strings_fn[STR_USB_IT].id = id; + usb_out_it_desc.iTerminal = id, + + id = usb_string_id(cfg->cdev); + if (id < 0) + return id; + + strings_fn[STR_IO_IT].id = id; + io_in_it_desc.iTerminal = id; + + id = usb_string_id(cfg->cdev); + if (id < 0) + return id; + + strings_fn[STR_USB_OT].id = id; + usb_in_ot_desc.iTerminal = id; + + id = usb_string_id(cfg->cdev); + if (id < 0) + return id; + + strings_fn[STR_IO_OT].id = id; + io_out_ot_desc.iTerminal = id; + + id = usb_string_id(cfg->cdev); + if (id < 0) + return id; + + strings_fn[STR_AS_OUT_ALT0].id = id; + std_as_out_if0_desc.iInterface = id; + + id = usb_string_id(cfg->cdev); + if (id < 0) + return id; + + strings_fn[STR_AS_OUT_ALT1].id = id; + std_as_out_if1_desc.iInterface = id; + + id = usb_string_id(cfg->cdev); + if (id < 0) + return id; + + strings_fn[STR_AS_IN_ALT0].id = id; + std_as_in_if0_desc.iInterface = id; + + id = usb_string_id(cfg->cdev); + if (id < 0) + return id; + + strings_fn[STR_AS_IN_ALT1].id = id; + std_as_in_if1_desc.iInterface = id; + + agdev_g->func.name = "uac2_func"; + agdev_g->func.strings = fn_strings; + agdev_g->func.bind = afunc_bind; + agdev_g->func.unbind = afunc_unbind; + agdev_g->func.set_alt = afunc_set_alt; + agdev_g->func.get_alt = afunc_get_alt; + agdev_g->func.disable = afunc_disable; + agdev_g->func.setup = afunc_setup; + + /* Initialize the configurable parameters */ + usb_out_it_desc.bNrChannels = num_channels(c_chmask); + usb_out_it_desc.bmChannelConfig = cpu_to_le32(c_chmask); + io_in_it_desc.bNrChannels = num_channels(p_chmask); + io_in_it_desc.bmChannelConfig = cpu_to_le32(p_chmask); + as_out_hdr_desc.bNrChannels = num_channels(c_chmask); + as_out_hdr_desc.bmChannelConfig = cpu_to_le32(c_chmask); + as_in_hdr_desc.bNrChannels = num_channels(p_chmask); + as_in_hdr_desc.bmChannelConfig = cpu_to_le32(p_chmask); + as_out_fmt1_desc.bSubslotSize = c_ssize; + as_out_fmt1_desc.bBitResolution = c_ssize * 8; + as_in_fmt1_desc.bSubslotSize = p_ssize; + as_in_fmt1_desc.bBitResolution = p_ssize * 8; + + snprintf(clksrc_in, sizeof(clksrc_in), "%uHz", p_srate); + snprintf(clksrc_out, sizeof(clksrc_out), "%uHz", c_srate); + + res = usb_add_function(cfg, &agdev_g->func); + if (res < 0) + kfree(agdev_g); + + return res; +} + +static void +uac2_unbind_config(struct usb_configuration *cfg) +{ + kfree(agdev_g); + agdev_g = NULL; +} -- cgit v0.10.2 From a003c187cac7a88101fe7f254bde6582865f7908 Mon Sep 17 00:00:00 2001 From: Danny Kukawka Date: Wed, 15 Feb 2012 18:56:07 +0100 Subject: usb: at91_udc: linux/prefetch.h included twice drivers/usb/gadget/at91_udc.c included 'linux/prefetch.h' twice, remove the duplicate. Signed-off-by: Danny Kukawka Signed-off-by: Felipe Balbi diff --git a/drivers/usb/gadget/at91_udc.c b/drivers/usb/gadget/at91_udc.c index 143a725..d8dee22 100644 --- a/drivers/usb/gadget/at91_udc.c +++ b/drivers/usb/gadget/at91_udc.c @@ -29,7 +29,6 @@ #include #include #include -#include #include #include -- cgit v0.10.2 From f9c56cdd3905c96c600456203637bd7ec8ec6383 Mon Sep 17 00:00:00 2001 From: Ido Shayevitz Date: Wed, 8 Feb 2012 13:56:48 +0200 Subject: usb: gadget: Clear usb_endpoint_descriptor inside the struct usb_ep on disable This fix a bug in f_serial, which expect the ep->desc to be NULL after disabling an endpoint. Cc: stable@vger.kernel.org Signed-off-by: Ido Shayevitz Signed-off-by: Felipe Balbi diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c index a696bde..a1a04d0 100644 --- a/drivers/usb/dwc3/gadget.c +++ b/drivers/usb/dwc3/gadget.c @@ -440,6 +440,7 @@ static int __dwc3_gadget_ep_disable(struct dwc3_ep *dep) dep->stream_capable = false; dep->desc = NULL; + dep->endpoint.desc = NULL; dep->comp_desc = NULL; dep->type = 0; dep->flags = 0; diff --git a/drivers/usb/gadget/amd5536udc.c b/drivers/usb/gadget/amd5536udc.c index c16ff55..18883bd 100644 --- a/drivers/usb/gadget/amd5536udc.c +++ b/drivers/usb/gadget/amd5536udc.c @@ -445,6 +445,7 @@ static void ep_init(struct udc_regs __iomem *regs, struct udc_ep *ep) VDBG(ep->dev, "ep-%d reset\n", ep->num); ep->desc = NULL; + ep->ep.desc = NULL; ep->ep.ops = &udc_ep_ops; INIT_LIST_HEAD(&ep->queue); diff --git a/drivers/usb/gadget/at91_udc.c b/drivers/usb/gadget/at91_udc.c index d8dee22..15a8cdb 100644 --- a/drivers/usb/gadget/at91_udc.c +++ b/drivers/usb/gadget/at91_udc.c @@ -557,6 +557,7 @@ static int at91_ep_disable (struct usb_ep * _ep) /* restore the endpoint's pristine config */ ep->desc = NULL; + ep->ep.desc = NULL; ep->ep.maxpacket = ep->maxpacket; /* reset fifos and endpoint */ diff --git a/drivers/usb/gadget/atmel_usba_udc.c b/drivers/usb/gadget/atmel_usba_udc.c index e2fb6d5..5e10f65 100644 --- a/drivers/usb/gadget/atmel_usba_udc.c +++ b/drivers/usb/gadget/atmel_usba_udc.c @@ -659,6 +659,7 @@ static int usba_ep_disable(struct usb_ep *_ep) return -EINVAL; } ep->desc = NULL; + ep->ep.desc = NULL; list_splice_init(&ep->queue, &req_list); if (ep->can_dma) { diff --git a/drivers/usb/gadget/ci13xxx_udc.c b/drivers/usb/gadget/ci13xxx_udc.c index 27e3137..9e89289 100644 --- a/drivers/usb/gadget/ci13xxx_udc.c +++ b/drivers/usb/gadget/ci13xxx_udc.c @@ -2181,6 +2181,7 @@ static int ep_disable(struct usb_ep *ep) } while (mEp->dir != direction); mEp->desc = NULL; + mEp->ep.desc = NULL; spin_unlock_irqrestore(mEp->lock, flags); return retval; diff --git a/drivers/usb/gadget/fsl_qe_udc.c b/drivers/usb/gadget/fsl_qe_udc.c index b95697c..877a2c4 100644 --- a/drivers/usb/gadget/fsl_qe_udc.c +++ b/drivers/usb/gadget/fsl_qe_udc.c @@ -1638,6 +1638,7 @@ static int qe_ep_disable(struct usb_ep *_ep) /* Nuke all pending requests (does flush) */ nuke(ep, -ESHUTDOWN); ep->desc = NULL; + ep->ep.desc = NULL; ep->stopped = 1; ep->tx_req = NULL; qe_ep_reset(udc, ep->epnum); diff --git a/drivers/usb/gadget/fsl_udc_core.c b/drivers/usb/gadget/fsl_udc_core.c index d7ea6c0..b543573 100644 --- a/drivers/usb/gadget/fsl_udc_core.c +++ b/drivers/usb/gadget/fsl_udc_core.c @@ -659,6 +659,7 @@ static int fsl_ep_disable(struct usb_ep *_ep) nuke(ep, -ESHUTDOWN); ep->desc = NULL; + ep->ep.desc = NULL; ep->stopped = 1; spin_unlock_irqrestore(&udc->lock, flags); diff --git a/drivers/usb/gadget/goku_udc.c b/drivers/usb/gadget/goku_udc.c index 5af70fc..5977749 100644 --- a/drivers/usb/gadget/goku_udc.c +++ b/drivers/usb/gadget/goku_udc.c @@ -235,6 +235,7 @@ static void ep_reset(struct goku_udc_regs __iomem *regs, struct goku_ep *ep) ep->ep.maxpacket = MAX_FIFO_SIZE; ep->desc = NULL; + ep->ep.desc = NULL; ep->stopped = 1; ep->irqs = 0; ep->dma = 0; diff --git a/drivers/usb/gadget/langwell_udc.c b/drivers/usb/gadget/langwell_udc.c index fa0fcc1..a2bb5e4 100644 --- a/drivers/usb/gadget/langwell_udc.c +++ b/drivers/usb/gadget/langwell_udc.c @@ -492,6 +492,7 @@ static int langwell_ep_disable(struct usb_ep *_ep) nuke(ep, -ESHUTDOWN); ep->desc = NULL; + ep->ep.desc = NULL; ep->stopped = 1; spin_unlock_irqrestore(&dev->lock, flags); diff --git a/drivers/usb/gadget/mv_udc_core.c b/drivers/usb/gadget/mv_udc_core.c index 8dd398b..ec6009f 100644 --- a/drivers/usb/gadget/mv_udc_core.c +++ b/drivers/usb/gadget/mv_udc_core.c @@ -608,6 +608,7 @@ static int mv_ep_disable(struct usb_ep *_ep) nuke(ep, -ESHUTDOWN); ep->desc = NULL; + ep->ep.desc = NULL; ep->stopped = 1; spin_unlock_irqrestore(&udc->lock, flags); diff --git a/drivers/usb/gadget/omap_udc.c b/drivers/usb/gadget/omap_udc.c index 576cd85..7d0835e 100644 --- a/drivers/usb/gadget/omap_udc.c +++ b/drivers/usb/gadget/omap_udc.c @@ -251,6 +251,7 @@ static int omap_ep_disable(struct usb_ep *_ep) spin_lock_irqsave(&ep->udc->lock, flags); ep->desc = NULL; + ep->ep.desc = NULL; nuke (ep, -ESHUTDOWN); ep->ep.maxpacket = ep->maxpacket; ep->has_dma = 0; diff --git a/drivers/usb/gadget/pch_udc.c b/drivers/usb/gadget/pch_udc.c index a992084..350dbcd 100644 --- a/drivers/usb/gadget/pch_udc.c +++ b/drivers/usb/gadget/pch_udc.c @@ -1742,6 +1742,7 @@ static int pch_udc_pcd_ep_disable(struct usb_ep *usbep) pch_udc_ep_disable(ep); pch_udc_disable_ep_interrupts(ep->dev, PCH_UDC_EPINT(ep->in, ep->num)); ep->desc = NULL; + ep->ep.desc = NULL; INIT_LIST_HEAD(&ep->queue); spin_unlock_irqrestore(&ep->dev->lock, iflags); return 0; diff --git a/drivers/usb/gadget/pxa25x_udc.c b/drivers/usb/gadget/pxa25x_udc.c index dd47063..33adf38 100644 --- a/drivers/usb/gadget/pxa25x_udc.c +++ b/drivers/usb/gadget/pxa25x_udc.c @@ -283,6 +283,7 @@ static int pxa25x_ep_disable (struct usb_ep *_ep) pxa25x_ep_fifo_flush (_ep); ep->desc = NULL; + ep->ep.desc = NULL; ep->stopped = 1; local_irq_restore(flags); @@ -1192,6 +1193,7 @@ static void udc_reinit(struct pxa25x_udc *dev) list_add_tail (&ep->ep.ep_list, &dev->gadget.ep_list); ep->desc = NULL; + ep->ep.desc = NULL; ep->stopped = 0; INIT_LIST_HEAD (&ep->queue); ep->pio_irqs = 0; diff --git a/drivers/usb/gadget/s3c-hsudc.c b/drivers/usb/gadget/s3c-hsudc.c index 5e87293..ace95f9 100644 --- a/drivers/usb/gadget/s3c-hsudc.c +++ b/drivers/usb/gadget/s3c-hsudc.c @@ -817,6 +817,7 @@ static int s3c_hsudc_ep_disable(struct usb_ep *_ep) s3c_hsudc_nuke_ep(hsep, -ESHUTDOWN); hsep->desc = 0; + hsep->ep.desc = NULL; hsep->stopped = 1; spin_unlock_irqrestore(&hsudc->lock, flags); @@ -1006,6 +1007,7 @@ static void s3c_hsudc_initep(struct s3c_hsudc *hsudc, hsep->ep.ops = &s3c_hsudc_ep_ops; hsep->fifo = hsudc->regs + S3C_BR(epnum); hsep->desc = 0; + hsep->ep.desc = NULL; hsep->stopped = 0; hsep->wedge = 0; diff --git a/drivers/usb/gadget/s3c2410_udc.c b/drivers/usb/gadget/s3c2410_udc.c index 3f87cb9..ab9c65e 100644 --- a/drivers/usb/gadget/s3c2410_udc.c +++ b/drivers/usb/gadget/s3c2410_udc.c @@ -1148,6 +1148,7 @@ static int s3c2410_udc_ep_disable(struct usb_ep *_ep) dprintk(DEBUG_NORMAL, "ep_disable: %s\n", _ep->name); ep->desc = NULL; + ep->ep.desc = NULL; ep->halted = 1; s3c2410_udc_nuke (ep->dev, ep, -ESHUTDOWN); @@ -1630,6 +1631,7 @@ static void s3c2410_udc_reinit(struct s3c2410_udc *dev) ep->dev = dev; ep->desc = NULL; + ep->ep.desc = NULL; ep->halted = 0; INIT_LIST_HEAD (&ep->queue); } -- cgit v0.10.2 From 3b2a2e47174cd978258bbb0fdf2e2b1b5ec2144c Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Mon, 20 Feb 2012 17:35:50 -0800 Subject: usb: renesas_usbhs: bugfix: add .release function to gpriv->gadget.dev This patch fixup below warning on device_unregister() renesas_usbhs renesas_usbhs.1: host probed renesas_usbhs renesas_usbhs.1: gadget probed renesas_usbhs renesas_usbhs.1: irq request err ------------[ cut here ]------------ WARNING: at ${LINUX}/drivers/base/core.c:1) Device 'gadget' does not have a release() function, it is broken and must be fi. Modules linked in: [] (unwind_backtrace+0x0/0xe4) from [] (warn_slowpath_commo) [] (warn_slowpath_common+0x4c/0x64) from [] (warn_slowpath_) [] (warn_slowpath_fmt+0x2c/0x3c) from [] (device_release+0x) [] (device_release+0x70/0x84) from [] (kobject_cleanup+0x58) [] (kobject_cleanup+0x58/0x6c) from [] (usbhs_mod_gadget_re) [] (usbhs_mod_gadget_remove+0x3c/0x6c) from [] (usbhs_mod_p) [] (usbhs_mod_probe+0x68/0x80) from [] (usbhs_probe+0x1cc/0) ... Cc: stable@vger.kernel.org Signed-off-by: Kuninori Morimoto Signed-off-by: Felipe Balbi diff --git a/drivers/usb/renesas_usbhs/mod_gadget.c b/drivers/usb/renesas_usbhs/mod_gadget.c index 528691d5f..f37b20c 100644 --- a/drivers/usb/renesas_usbhs/mod_gadget.c +++ b/drivers/usb/renesas_usbhs/mod_gadget.c @@ -941,6 +941,11 @@ static int usbhsg_stop(struct usbhs_priv *priv) return usbhsg_try_stop(priv, USBHSG_STATUS_STARTED); } +static void usbhs_mod_gadget_release(struct device *pdev) +{ + /* do nothing */ +} + int usbhs_mod_gadget_probe(struct usbhs_priv *priv) { struct usbhsg_gpriv *gpriv; @@ -989,6 +994,7 @@ int usbhs_mod_gadget_probe(struct usbhs_priv *priv) */ dev_set_name(&gpriv->gadget.dev, "gadget"); gpriv->gadget.dev.parent = dev; + gpriv->gadget.dev.release = usbhs_mod_gadget_release; gpriv->gadget.name = "renesas_usbhs_udc"; gpriv->gadget.ops = &usbhsg_gadget_ops; gpriv->gadget.max_speed = USB_SPEED_HIGH; -- cgit v0.10.2 From c5cc5ed86667d4ae74fe40ee4ed893f4b46aba05 Mon Sep 17 00:00:00 2001 From: Peter Chen Date: Thu, 16 Feb 2012 09:36:25 +0800 Subject: usb: fsl_udc_core: Fix scheduling while atomic dump message When loading g_ether gadget, there is below message: Backtrace: [<80012248>] (dump_backtrace+0x0/0x10c) from [<803cb42c>] (dump_stack+0x18/0x1c) r7:00000000 r6:80512000 r5:8052bef8 r4:80513f30 [<803cb414>] (dump_stack+0x0/0x1c) from [<8000feb4>] (show_regs+0x44/0x50) [<8000fe70>] (show_regs+0x0/0x50) from [<8004c840>] (__schedule_bug+0x68/0x84) r5:8052bef8 r4:80513f30 [<8004c7d8>] (__schedule_bug+0x0/0x84) from [<803cd0e4>] (__schedule+0x4b0/0x528) r5:8052bef8 r4:809aad00 [<803ccc34>] (__schedule+0x0/0x528) from [<803cd214>] (_cond_resched+0x44/0x58) [<803cd1d0>] (_cond_resched+0x0/0x58) from [<800a9488>] (dma_pool_alloc+0x184/0x250) r5:9f9b4000 r4:9fb4fb80 [<800a9304>] (dma_pool_alloc+0x0/0x250) from [<802a8ad8>] (fsl_req_to_dtd+0xac/0x180) [<802a8a2c>] (fsl_req_to_dtd+0x0/0x180) from [<802a8ce4>] (fsl_ep_queue+0x138/0x274) [<802a8bac>] (fsl_ep_queue+0x0/0x274) from [<7f004328>] (composite_setup+0x2d4/0xfac [g_ether]) [<7f004054>] (composite_setup+0x0/0xfac [g_ether]) from [<802a9bb4>] (fsl_udc_irq+0x8dc/0xd38) [<802a92d8>] (fsl_udc_irq+0x0/0xd38) from [<800704f8>] (handle_irq_event_percpu+0x54/0x188) [<800704a4>] (handle_irq_event_percpu+0x0/0x188) from [<80070674>] (handle_irq_event+0x48/0x68) [<8007062c>] (handle_irq_event+0x0/0x68) from [<800738ec>] (handle_level_irq+0xb4/0x138) r5:80514f94 r4:80514f40 [<80073838>] (handle_level_irq+0x0/0x138) from [<8006ffa4>] (generic_handle_irq+0x38/0x44) r7:00000012 r6:80510b1c r5:80529860 r4:80512000 [<8006ff6c>] (generic_handle_irq+0x0/0x44) from [<8000f4c4>] (handle_IRQ+0x54/0xb4) [<8000f470>] (handle_IRQ+0x0/0xb4) from [<800085b8>] (tzic_handle_irq+0x64/0x94) r9:412fc085 r8:00000000 r7:80513f30 r6:00000001 r5:00000000 r4:00000000 [<80008554>] (tzic_handle_irq+0x0/0x94) from [<8000e680>] (__irq_svc+0x40/0x60) The reason of above dump message is calling dma_poll_alloc with can-schedule mem_flags at atomic context. To fix this problem, below changes are made: - fsl_req_to_dtd doesn't need to be protected by spin_lock_irqsave, as struct usb_request can be access at process context. Move lock to beginning of hardware visit (fsl_queue_td). - Change the memory flag which using to allocate dTD descriptor buffer, the memory flag can be from gadget layer. It is tested at i.mx51 bbg board with g_mass_storage, g_ether, g_serial. Cc: stable@vger.kernel.org Signed-off-by: Peter Chen Acked-by: Li Yang Signed-off-by: Felipe Balbi diff --git a/drivers/usb/gadget/fsl_udc_core.c b/drivers/usb/gadget/fsl_udc_core.c index b543573..8db5a66 100644 --- a/drivers/usb/gadget/fsl_udc_core.c +++ b/drivers/usb/gadget/fsl_udc_core.c @@ -769,7 +769,7 @@ static void fsl_queue_td(struct fsl_ep *ep, struct fsl_req *req) * @is_last: return flag if it is the last dTD of the request * return: pointer to the built dTD */ static struct ep_td_struct *fsl_build_dtd(struct fsl_req *req, unsigned *length, - dma_addr_t *dma, int *is_last) + dma_addr_t *dma, int *is_last, gfp_t gfp_flags) { u32 swap_temp; struct ep_td_struct *dtd; @@ -778,7 +778,7 @@ static struct ep_td_struct *fsl_build_dtd(struct fsl_req *req, unsigned *length, *length = min(req->req.length - req->req.actual, (unsigned)EP_MAX_LENGTH_TRANSFER); - dtd = dma_pool_alloc(udc_controller->td_pool, GFP_KERNEL, dma); + dtd = dma_pool_alloc(udc_controller->td_pool, gfp_flags, dma); if (dtd == NULL) return dtd; @@ -828,7 +828,7 @@ static struct ep_td_struct *fsl_build_dtd(struct fsl_req *req, unsigned *length, } /* Generate dtd chain for a request */ -static int fsl_req_to_dtd(struct fsl_req *req) +static int fsl_req_to_dtd(struct fsl_req *req, gfp_t gfp_flags) { unsigned count; int is_last; @@ -837,7 +837,7 @@ static int fsl_req_to_dtd(struct fsl_req *req) dma_addr_t dma; do { - dtd = fsl_build_dtd(req, &count, &dma, &is_last); + dtd = fsl_build_dtd(req, &count, &dma, &is_last, gfp_flags); if (dtd == NULL) return -ENOMEM; @@ -911,13 +911,11 @@ fsl_ep_queue(struct usb_ep *_ep, struct usb_request *_req, gfp_t gfp_flags) req->req.actual = 0; req->dtd_count = 0; - spin_lock_irqsave(&udc->lock, flags); - /* build dtds and push them to device queue */ - if (!fsl_req_to_dtd(req)) { + if (!fsl_req_to_dtd(req, gfp_flags)) { + spin_lock_irqsave(&udc->lock, flags); fsl_queue_td(ep, req); } else { - spin_unlock_irqrestore(&udc->lock, flags); return -ENOMEM; } @@ -1296,7 +1294,7 @@ static int ep0_prime_status(struct fsl_udc *udc, int direction) ep_is_in(ep) ? DMA_TO_DEVICE : DMA_FROM_DEVICE); req->mapped = 1; - if (fsl_req_to_dtd(req) == 0) + if (fsl_req_to_dtd(req, GFP_ATOMIC) == 0) fsl_queue_td(ep, req); else return -ENOMEM; @@ -1380,7 +1378,7 @@ static void ch9getstatus(struct fsl_udc *udc, u8 request_type, u16 value, req->mapped = 1; /* prime the data phase */ - if ((fsl_req_to_dtd(req) == 0)) + if ((fsl_req_to_dtd(req, GFP_ATOMIC) == 0)) fsl_queue_td(ep, req); else /* no mem */ goto stall; -- cgit v0.10.2 From 170b778ffbfca8619b6971e8a5e8b864712b72e2 Mon Sep 17 00:00:00 2001 From: Cyril Roelandt Date: Sat, 25 Feb 2012 02:14:57 +0100 Subject: usb: amd5536udc: Fix brace coding style issues. Remove a bunch of unneeded braces. Signed-off-by: Cyril Roelandt Signed-off-by: Felipe Balbi diff --git a/drivers/usb/gadget/amd5536udc.c b/drivers/usb/gadget/amd5536udc.c index 18883bd..7df2f70 100644 --- a/drivers/usb/gadget/amd5536udc.c +++ b/drivers/usb/gadget/amd5536udc.c @@ -204,9 +204,8 @@ static void print_regs(struct udc *dev) DBG(dev, "DMA mode = BF (buffer fill mode)\n"); dev_info(&dev->pdev->dev, "DMA mode (%s)\n", "BF"); } - if (!use_dma) { + if (!use_dma) dev_info(&dev->pdev->dev, "FIFO mode\n"); - } DBG(dev, "-------------------------------------------------------\n"); } @@ -570,9 +569,8 @@ udc_free_request(struct usb_ep *usbep, struct usb_request *usbreq) VDBG(ep->dev, "req->td_data=%p\n", req->td_data); /* free dma chain if created */ - if (req->chain_len > 1) { + if (req->chain_len > 1) udc_free_dma_chain(ep->dev, req); - } pci_pool_free(ep->dev->data_requests, req->td_data, req->td_phys); @@ -640,9 +638,8 @@ udc_txfifo_write(struct udc_ep *ep, struct usb_request *req) bytes = remaining; /* dwords first */ - for (i = 0; i < bytes / UDC_DWORD_BYTES; i++) { + for (i = 0; i < bytes / UDC_DWORD_BYTES; i++) writel(*(buf + i), ep->txfifo); - } /* remaining bytes must be written by byte access */ for (j = 0; j < bytes % UDC_DWORD_BYTES; j++) { @@ -661,9 +658,8 @@ static int udc_rxfifo_read_dwords(struct udc *dev, u32 *buf, int dwords) VDBG(dev, "udc_read_dwords(): %d dwords\n", dwords); - for (i = 0; i < dwords; i++) { + for (i = 0; i < dwords; i++) *(buf + i) = readl(dev->rxfifo); - } return 0; } @@ -676,9 +672,8 @@ static int udc_rxfifo_read_bytes(struct udc *dev, u8 *buf, int bytes) VDBG(dev, "udc_read_bytes(): %d bytes\n", bytes); /* dwords first */ - for (i = 0; i < bytes / UDC_DWORD_BYTES; i++) { + for (i = 0; i < bytes / UDC_DWORD_BYTES; i++) *((u32 *)(buf + (i<<2))) = readl(dev->rxfifo); - } /* remaining bytes must be read by byte access */ if (bytes % UDC_DWORD_BYTES) { @@ -898,9 +893,8 @@ static struct udc_data_dma *udc_get_last_dma_desc(struct udc_request *req) struct udc_data_dma *td; td = req->td_data; - while (td && !(td->status & AMD_BIT(UDC_DMA_IN_STS_L))) { + while (td && !(td->status & AMD_BIT(UDC_DMA_IN_STS_L))) td = phys_to_virt(td->next); - } return td; @@ -950,21 +944,18 @@ static int udc_create_dma_chain( dma_addr = DMA_DONT_USE; /* unset L bit in first desc for OUT */ - if (!ep->in) { + if (!ep->in) req->td_data->status &= AMD_CLEAR_BIT(UDC_DMA_IN_STS_L); - } /* alloc only new desc's if not already available */ len = req->req.length / ep->ep.maxpacket; - if (req->req.length % ep->ep.maxpacket) { + if (req->req.length % ep->ep.maxpacket) len++; - } if (len > req->chain_len) { /* shorter chain already allocated before */ - if (req->chain_len > 1) { + if (req->chain_len > 1) udc_free_dma_chain(ep->dev, req); - } req->chain_len = len; create_new_chain = 1; } @@ -1007,11 +998,12 @@ static int udc_create_dma_chain( /* link td and assign tx bytes */ if (i == buf_len) { - if (create_new_chain) { + if (create_new_chain) req->td_data->next = dma_addr; - } else { - /* req->td_data->next = virt_to_phys(td); */ - } + /* + else + req->td_data->next = virt_to_phys(td); + */ /* write tx bytes */ if (ep->in) { /* first desc */ @@ -1025,11 +1017,12 @@ static int udc_create_dma_chain( UDC_DMA_IN_STS_TXBYTES); } } else { - if (create_new_chain) { + if (create_new_chain) last->next = dma_addr; - } else { - /* last->next = virt_to_phys(td); */ - } + /* + else + last->next = virt_to_phys(td); + */ if (ep->in) { /* write tx bytes */ td->status = AMD_ADDBITS(td->status, @@ -1480,11 +1473,10 @@ static int startup_registers(struct udc *dev) /* program speed */ tmp = readl(&dev->regs->cfg); - if (use_fullspeed) { + if (use_fullspeed) tmp = AMD_ADDBITS(tmp, UDC_DEVCFG_SPD_FS, UDC_DEVCFG_SPD); - } else { + else tmp = AMD_ADDBITS(tmp, UDC_DEVCFG_SPD_HS, UDC_DEVCFG_SPD); - } writel(tmp, &dev->regs->cfg); return 0; @@ -1505,9 +1497,8 @@ static void udc_basic_init(struct udc *dev) mod_timer(&udc_timer, jiffies - 1); } /* stop poll stall timer */ - if (timer_pending(&udc_pollstall_timer)) { + if (timer_pending(&udc_pollstall_timer)) mod_timer(&udc_pollstall_timer, jiffies - 1); - } /* disable DMA */ tmp = readl(&dev->regs->ctl); tmp &= AMD_UNMASK_BIT(UDC_DEVCTL_RDE); @@ -1541,11 +1532,10 @@ static void udc_setup_endpoints(struct udc *dev) /* read enum speed */ tmp = readl(&dev->regs->sts); tmp = AMD_GETBITS(tmp, UDC_DEVSTS_ENUM_SPEED); - if (tmp == UDC_DEVSTS_ENUM_SPEED_HIGH) { + if (tmp == UDC_DEVSTS_ENUM_SPEED_HIGH) dev->gadget.speed = USB_SPEED_HIGH; - } else if (tmp == UDC_DEVSTS_ENUM_SPEED_FULL) { + else if (tmp == UDC_DEVSTS_ENUM_SPEED_FULL) dev->gadget.speed = USB_SPEED_FULL; - } /* set basic ep parameters */ for (tmp = 0; tmp < UDC_EP_NUM; tmp++) { @@ -1571,9 +1561,8 @@ static void udc_setup_endpoints(struct udc *dev) * disabling ep interrupts when ENUM interrupt occurs but ep is * not enabled by gadget driver */ - if (!ep->desc) { + if (!ep->desc) ep_init(dev->regs, ep); - } if (use_dma) { /* @@ -1671,9 +1660,8 @@ static void udc_tasklet_disconnect(unsigned long par) spin_lock(&dev->lock); /* empty queues */ - for (tmp = 0; tmp < UDC_EP_NUM; tmp++) { + for (tmp = 0; tmp < UDC_EP_NUM; tmp++) empty_req_queue(&dev->ep[tmp]); - } } @@ -1747,9 +1735,8 @@ static void udc_timer_function(unsigned long v) * open the fifo */ udc_timer.expires = jiffies + HZ/UDC_RDE_TIMER_DIV; - if (!stop_timer) { + if (!stop_timer) add_timer(&udc_timer); - } } else { /* * fifo contains data now, setup timer for opening @@ -1761,9 +1748,8 @@ static void udc_timer_function(unsigned long v) set_rde++; /* debug: lhadmot_timer_start = 221070 */ udc_timer.expires = jiffies + HZ*UDC_RDE_TIMER_SECONDS; - if (!stop_timer) { + if (!stop_timer) add_timer(&udc_timer); - } } } else @@ -1908,19 +1894,17 @@ static void activate_control_endpoints(struct udc *dev) mod_timer(&udc_timer, jiffies - 1); } /* stop pollstall timer */ - if (timer_pending(&udc_pollstall_timer)) { + if (timer_pending(&udc_pollstall_timer)) mod_timer(&udc_pollstall_timer, jiffies - 1); - } /* enable DMA */ tmp = readl(&dev->regs->ctl); tmp |= AMD_BIT(UDC_DEVCTL_MODE) | AMD_BIT(UDC_DEVCTL_RDE) | AMD_BIT(UDC_DEVCTL_TDE); - if (use_dma_bufferfill_mode) { + if (use_dma_bufferfill_mode) tmp |= AMD_BIT(UDC_DEVCTL_BF); - } else if (use_dma_ppb_du) { + else if (use_dma_ppb_du) tmp |= AMD_BIT(UDC_DEVCTL_DU); - } writel(tmp, &dev->regs->ctl); } @@ -2105,9 +2089,8 @@ static void udc_ep0_set_rde(struct udc *dev) udc_timer.expires = jiffies + HZ/UDC_RDE_TIMER_DIV; set_rde = 1; - if (!stop_timer) { + if (!stop_timer) add_timer(&udc_timer); - } } } } @@ -2295,9 +2278,8 @@ static irqreturn_t udc_data_out_isr(struct udc *dev, int ep_ix) jiffies + HZ*UDC_RDE_TIMER_SECONDS; set_rde = 1; - if (!stop_timer) { + if (!stop_timer) add_timer(&udc_timer); - } } if (ep->num != UDC_EP0OUT_IX) dev->data_ep_queued = 0; @@ -2319,9 +2301,8 @@ static irqreturn_t udc_data_out_isr(struct udc *dev, int ep_ix) /* check pending CNAKS */ if (cnak_pending) { /* CNAk processing when rxfifo empty only */ - if (readl(&dev->regs->sts) & AMD_BIT(UDC_DEVSTS_RXFIFO_EMPTY)) { + if (readl(&dev->regs->sts) & AMD_BIT(UDC_DEVSTS_RXFIFO_EMPTY)) udc_process_cnak_queue(dev); - } } /* clear OUT bits in ep status */ @@ -2582,9 +2563,8 @@ __acquires(dev->lock) if (!timer_pending(&udc_timer)) { udc_timer.expires = jiffies + HZ/UDC_RDE_TIMER_DIV; - if (!stop_timer) { + if (!stop_timer) add_timer(&udc_timer); - } } } @@ -2698,9 +2678,8 @@ __acquires(dev->lock) /* check pending CNAKS */ if (cnak_pending) { /* CNAk processing when rxfifo empty only */ - if (readl(&dev->regs->sts) & AMD_BIT(UDC_DEVSTS_RXFIFO_EMPTY)) { + if (readl(&dev->regs->sts) & AMD_BIT(UDC_DEVSTS_RXFIFO_EMPTY)) udc_process_cnak_queue(dev); - } } finished: -- cgit v0.10.2 From 5647a149e2b9260530a11c8494b529de56e226ab Mon Sep 17 00:00:00 2001 From: Cyril Roelandt Date: Sat, 25 Feb 2012 02:14:58 +0100 Subject: usb: amd5536udc: Fix coding style issues. Remove unnecessary whitespaces. Signed-off-by: Cyril Roelandt Signed-off-by: Felipe Balbi diff --git a/drivers/usb/gadget/amd5536udc.c b/drivers/usb/gadget/amd5536udc.c index 7df2f70..5e2cc0a 100644 --- a/drivers/usb/gadget/amd5536udc.c +++ b/drivers/usb/gadget/amd5536udc.c @@ -2115,7 +2115,7 @@ static irqreturn_t udc_data_out_isr(struct udc *dev, int ep_ix) if (use_dma) { /* BNA event ? */ if (tmp & AMD_BIT(UDC_EPSTS_BNA)) { - DBG(dev, "BNA ep%dout occurred - DESPTR = %x \n", + DBG(dev, "BNA ep%dout occurred - DESPTR = %x\n", ep->num, readl(&ep->regs->desptr)); /* clear BNA */ writel(tmp | AMD_BIT(UDC_EPSTS_BNA), &ep->regs->sts); @@ -2330,7 +2330,7 @@ static irqreturn_t udc_data_in_isr(struct udc *dev, int ep_ix) /* BNA ? */ if (epsts & AMD_BIT(UDC_EPSTS_BNA)) { dev_err(&dev->pdev->dev, - "BNA ep%din occurred - DESPTR = %08lx \n", + "BNA ep%din occurred - DESPTR = %08lx\n", ep->num, (unsigned long) readl(&ep->regs->desptr)); @@ -2343,7 +2343,7 @@ static irqreturn_t udc_data_in_isr(struct udc *dev, int ep_ix) /* HE event ? */ if (epsts & AMD_BIT(UDC_EPSTS_HE)) { dev_err(&dev->pdev->dev, - "HE ep%dn occurred - DESPTR = %08lx \n", + "HE ep%dn occurred - DESPTR = %08lx\n", ep->num, (unsigned long) readl(&ep->regs->desptr)); /* clear HE */ @@ -2703,7 +2703,7 @@ static irqreturn_t udc_control_in_isr(struct udc *dev) tmp = readl(&dev->ep[UDC_EP0IN_IX].regs->sts); /* DMA completion */ if (tmp & AMD_BIT(UDC_EPSTS_TDC)) { - VDBG(dev, "isr: TDC clear \n"); + VDBG(dev, "isr: TDC clear\n"); ret_val = IRQ_HANDLED; /* clear TDC bit */ -- cgit v0.10.2 From 1435db486c131b4b76d8e2a261ae6292ffc3f7ca Mon Sep 17 00:00:00 2001 From: Cyril Roelandt Date: Sat, 25 Feb 2012 02:14:59 +0100 Subject: usb: amd5536udc: Fix indentation Remove an unnecessary level of indentation. Signed-off-by: Cyril Roelandt Signed-off-by: Felipe Balbi diff --git a/drivers/usb/gadget/amd5536udc.c b/drivers/usb/gadget/amd5536udc.c index 5e2cc0a..ad72ab7 100644 --- a/drivers/usb/gadget/amd5536udc.c +++ b/drivers/usb/gadget/amd5536udc.c @@ -2409,9 +2409,9 @@ static irqreturn_t udc_data_in_isr(struct udc *dev, int ep_ix) /* write fifo */ udc_txfifo_write(ep, &req->req); len = req->req.length - req->req.actual; - if (len > ep->ep.maxpacket) - len = ep->ep.maxpacket; - req->req.actual += len; + if (len > ep->ep.maxpacket) + len = ep->ep.maxpacket; + req->req.actual += len; if (req->req.actual == req->req.length || (len != ep->ep.maxpacket)) { /* complete req */ -- cgit v0.10.2 From 1b8860df38963218a30e9f1f39d649c992cb9efa Mon Sep 17 00:00:00 2001 From: Cyril Roelandt Date: Sat, 25 Feb 2012 02:15:00 +0100 Subject: usb: amd5536udc: use the DEFINE_PCI_DEVICE_TABLE macro Use DEFINE_PCI_DEVICE_TABLE instead of "const struct pci_device_id". Signed-off-by: Cyril Roelandt Signed-off-by: Felipe Balbi diff --git a/drivers/usb/gadget/amd5536udc.c b/drivers/usb/gadget/amd5536udc.c index ad72ab7..d28bc24 100644 --- a/drivers/usb/gadget/amd5536udc.c +++ b/drivers/usb/gadget/amd5536udc.c @@ -3406,7 +3406,7 @@ static int udc_remote_wakeup(struct udc *dev) } /* PCI device parameters */ -static const struct pci_device_id pci_id[] = { +static DEFINE_PCI_DEVICE_TABLE(pci_id) = { { PCI_DEVICE(PCI_VENDOR_ID_AMD, 0x2096), .class = (PCI_CLASS_SERIAL_USB << 8) | 0xfe, -- cgit v0.10.2 From 34af373865b91aa4108134b5a4748d40a100111c Mon Sep 17 00:00:00 2001 From: Cyril Roelandt Date: Sun, 26 Feb 2012 16:00:25 +0100 Subject: usb: amd5536udc: Fix the type of ep_string Use "static const char *const" instead of "static const char *". Signed-off-by: Cyril Roelandt Signed-off-by: Felipe Balbi diff --git a/drivers/usb/gadget/amd5536udc.c b/drivers/usb/gadget/amd5536udc.c index d28bc24..9349c4e 100644 --- a/drivers/usb/gadget/amd5536udc.c +++ b/drivers/usb/gadget/amd5536udc.c @@ -140,7 +140,7 @@ static DECLARE_TASKLET(disconnect_tasklet, udc_tasklet_disconnect, /* endpoint names used for print */ static const char ep0_string[] = "ep0in"; -static const char *ep_string[] = { +static const char *const ep_string[] = { ep0_string, "ep1in-int", "ep2in-bulk", "ep3in-bulk", "ep4in-bulk", "ep5in-bulk", "ep6in-bulk", "ep7in-bulk", "ep8in-bulk", "ep9in-bulk", "ep10in-bulk", -- cgit v0.10.2 From c15e03e1553d8fb334de26727b0edadd582a46e7 Mon Sep 17 00:00:00 2001 From: Cyril Roelandt Date: Sat, 25 Feb 2012 02:15:02 +0100 Subject: usb: amd5536udc: Remove old CVS markers Git does not care about CVS markers. Signed-off-by: Cyril Roelandt Signed-off-by: Felipe Balbi diff --git a/drivers/usb/gadget/amd5536udc.c b/drivers/usb/gadget/amd5536udc.c index 9349c4e..1355a2f 100644 --- a/drivers/usb/gadget/amd5536udc.c +++ b/drivers/usb/gadget/amd5536udc.c @@ -29,7 +29,7 @@ /* Driver strings */ #define UDC_MOD_DESCRIPTION "AMD 5536 UDC - USB Device Controller" -#define UDC_DRIVER_VERSION_STRING "01.00.0206 - $Revision: #3 $" +#define UDC_DRIVER_VERSION_STRING "01.00.0206" /* system */ #include -- cgit v0.10.2 From a698908d3b3be915ac20cd37faeff1216f6b4fe8 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Thu, 15 Dec 2011 13:31:48 +0200 Subject: usb: gadget: add generic map/unmap request utilities such utilities are currently duplicated on all UDC drivers basically with the same structure. Let's group all implementations into one generic implementation and get rid of that duplication. Signed-off-by: Felipe Balbi diff --git a/drivers/usb/gadget/udc-core.c b/drivers/usb/gadget/udc-core.c index 0b0d12c..56da49f 100644 --- a/drivers/usb/gadget/udc-core.c +++ b/drivers/usb/gadget/udc-core.c @@ -22,6 +22,7 @@ #include #include #include +#include #include #include @@ -49,6 +50,57 @@ static DEFINE_MUTEX(udc_lock); /* ------------------------------------------------------------------------- */ +int usb_gadget_map_request(struct usb_gadget *gadget, + struct usb_request *req, int is_in) +{ + if (req->length == 0) + return 0; + + if (req->num_sgs) { + int mapped; + + mapped = dma_map_sg(&gadget->dev, req->sg, req->num_sgs, + is_in ? DMA_TO_DEVICE : DMA_FROM_DEVICE); + if (mapped == 0) { + dev_err(&gadget->dev, "failed to map SGs\n"); + return -EFAULT; + } + + req->num_mapped_sgs = mapped; + } else { + req->dma = dma_map_single(&gadget->dev, req->buf, req->length, + is_in ? DMA_TO_DEVICE : DMA_FROM_DEVICE); + + if (dma_mapping_error(&gadget->dev, req->dma)) { + dev_err(&gadget->dev, "failed to map buffer\n"); + return -EFAULT; + } + } + + return 0; +} +EXPORT_SYMBOL_GPL(usb_gadget_map_request); + +void usb_gadget_unmap_request(struct usb_gadget *gadget, + struct usb_request *req, int is_in) +{ + if (req->length == 0) + return; + + if (req->num_mapped_sgs) { + dma_unmap_sg(&gadget->dev, req->sg, req->num_mapped_sgs, + is_in ? DMA_TO_DEVICE : DMA_FROM_DEVICE); + + req->num_mapped_sgs = 0; + } else { + dma_unmap_single(&gadget->dev, req->dma, req->length, + is_in ? DMA_TO_DEVICE : DMA_FROM_DEVICE); + } +} +EXPORT_SYMBOL_GPL(usb_gadget_unmap_request); + +/* ------------------------------------------------------------------------- */ + /** * usb_gadget_start - tells usb device controller to start up * @gadget: The gadget we want to get started diff --git a/include/linux/usb/gadget.h b/include/linux/usb/gadget.h index da653b5..9517466 100644 --- a/include/linux/usb/gadget.h +++ b/include/linux/usb/gadget.h @@ -950,6 +950,16 @@ static inline void usb_free_descriptors(struct usb_descriptor_header **v) /*-------------------------------------------------------------------------*/ +/* utility to simplify map/unmap of usb_requests to/from DMA */ + +extern int usb_gadget_map_request(struct usb_gadget *gadget, + struct usb_request *req, int is_in); + +extern void usb_gadget_unmap_request(struct usb_gadget *gadget, + struct usb_request *req, int is_in); + +/*-------------------------------------------------------------------------*/ + /* utility wrapping a simple endpoint selection policy */ extern struct usb_ep *usb_ep_autoconfig(struct usb_gadget *, -- cgit v0.10.2 From 0fc9a1be09d9f8b19bcf64ab96836cb92beb0970 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Mon, 19 Dec 2011 11:32:34 +0200 Subject: usb: dwc3: gadget: use generic map/unmap routines those routines have everything we need to map/unmap USB requests and it's better to use them. In order to achieve that, we had to add a simple change on how we allocate and use our setup buffer; we cannot allocate it from coherent anymore otherwise the generic map/unmap routines won't be able to easily know that the GetStatus request already has a DMA address. Signed-off-by: Felipe Balbi diff --git a/drivers/usb/dwc3/core.h b/drivers/usb/dwc3/core.h index 9e57f8e..a72f42f 100644 --- a/drivers/usb/dwc3/core.h +++ b/drivers/usb/dwc3/core.h @@ -572,7 +572,6 @@ struct dwc3_request { * @ctrl_req_addr: dma address of ctrl_req * @ep0_trb: dma address of ep0_trb * @ep0_usb_req: dummy req used while handling STD USB requests - * @setup_buf_addr: dma address of setup_buf * @ep0_bounce_addr: dma address of ep0_bounce * @lock: for synchronizing * @dev: pointer to our struct device @@ -609,7 +608,6 @@ struct dwc3 { u8 *setup_buf; dma_addr_t ctrl_req_addr; dma_addr_t ep0_trb_addr; - dma_addr_t setup_buf_addr; dma_addr_t ep0_bounce_addr; struct dwc3_request ep0_usb_req; /* device lock */ diff --git a/drivers/usb/dwc3/ep0.c b/drivers/usb/dwc3/ep0.c index 2f51de5..24137d8 100644 --- a/drivers/usb/dwc3/ep0.c +++ b/drivers/usb/dwc3/ep0.c @@ -309,7 +309,7 @@ static int dwc3_ep0_handle_status(struct dwc3 *dwc, dep = dwc->eps[0]; dwc->ep0_usb_req.dep = dep; dwc->ep0_usb_req.request.length = sizeof(*response_pkt); - dwc->ep0_usb_req.request.dma = dwc->setup_buf_addr; + dwc->ep0_usb_req.request.buf = dwc->setup_buf; dwc->ep0_usb_req.request.complete = dwc3_ep0_status_cmpl; return __dwc3_gadget_ep0_queue(dep, &dwc->ep0_usb_req); @@ -686,7 +686,12 @@ static void dwc3_ep0_do_control_data(struct dwc3 *dwc, DWC3_TRBCTL_CONTROL_DATA); } else if ((req->request.length % dep->endpoint.maxpacket) && (event->endpoint_number == 0)) { - dwc3_map_buffer_to_dma(req); + ret = usb_gadget_map_request(&dwc->gadget, &req->request, + event->endpoint_number); + if (ret) { + dev_dbg(dwc->dev, "failed to map request\n"); + return; + } WARN_ON(req->request.length > dep->endpoint.maxpacket); @@ -701,7 +706,12 @@ static void dwc3_ep0_do_control_data(struct dwc3 *dwc, dwc->ep0_bounce_addr, dep->endpoint.maxpacket, DWC3_TRBCTL_CONTROL_DATA); } else { - dwc3_map_buffer_to_dma(req); + ret = usb_gadget_map_request(&dwc->gadget, &req->request, + event->endpoint_number); + if (ret) { + dev_dbg(dwc->dev, "failed to map request\n"); + return; + } ret = dwc3_ep0_start_trans(dwc, event->endpoint_number, req->request.dma, req->request.length, diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c index a1a04d0..1009e7e 100644 --- a/drivers/usb/dwc3/gadget.c +++ b/drivers/usb/dwc3/gadget.c @@ -54,70 +54,6 @@ #include "gadget.h" #include "io.h" -#define DMA_ADDR_INVALID (~(dma_addr_t)0) - -void dwc3_map_buffer_to_dma(struct dwc3_request *req) -{ - struct dwc3 *dwc = req->dep->dwc; - - if (req->request.length == 0) { - /* req->request.dma = dwc->setup_buf_addr; */ - return; - } - - if (req->request.num_sgs) { - int mapped; - - mapped = dma_map_sg(dwc->dev, req->request.sg, - req->request.num_sgs, - req->direction ? DMA_TO_DEVICE - : DMA_FROM_DEVICE); - if (mapped < 0) { - dev_err(dwc->dev, "failed to map SGs\n"); - return; - } - - req->request.num_mapped_sgs = mapped; - return; - } - - if (req->request.dma == DMA_ADDR_INVALID) { - req->request.dma = dma_map_single(dwc->dev, req->request.buf, - req->request.length, req->direction - ? DMA_TO_DEVICE : DMA_FROM_DEVICE); - req->mapped = true; - } -} - -void dwc3_unmap_buffer_from_dma(struct dwc3_request *req) -{ - struct dwc3 *dwc = req->dep->dwc; - - if (req->request.length == 0) { - req->request.dma = DMA_ADDR_INVALID; - return; - } - - if (req->request.num_mapped_sgs) { - req->request.dma = DMA_ADDR_INVALID; - dma_unmap_sg(dwc->dev, req->request.sg, - req->request.num_sgs, - req->direction ? DMA_TO_DEVICE - : DMA_FROM_DEVICE); - - req->request.num_mapped_sgs = 0; - return; - } - - if (req->mapped) { - dma_unmap_single(dwc->dev, req->request.dma, - req->request.length, req->direction - ? DMA_TO_DEVICE : DMA_FROM_DEVICE); - req->mapped = 0; - req->request.dma = DMA_ADDR_INVALID; - } -} - void dwc3_gadget_giveback(struct dwc3_ep *dep, struct dwc3_request *req, int status) { @@ -144,14 +80,15 @@ void dwc3_gadget_giveback(struct dwc3_ep *dep, struct dwc3_request *req, if (req->request.status == -EINPROGRESS) req->request.status = status; - dwc3_unmap_buffer_from_dma(req); + usb_gadget_unmap_request(&dwc->gadget, &req->request, + req->direction); dev_dbg(dwc->dev, "request %p from %s completed %d/%d ===> %d\n", req, dep->name, req->request.actual, req->request.length, status); spin_unlock(&dwc->lock); - req->request.complete(&req->dep->endpoint, &req->request); + req->request.complete(&dep->endpoint, &req->request); spin_lock(&dwc->lock); } @@ -563,7 +500,6 @@ static struct usb_request *dwc3_gadget_ep_alloc_request(struct usb_ep *ep, req->epnum = dep->number; req->dep = dep; - req->request.dma = DMA_ADDR_INVALID; return &req->request; } @@ -822,7 +758,8 @@ static int __dwc3_gadget_kick_transfer(struct dwc3_ep *dep, u16 cmd_param, * here and stop, unmap, free and del each of the linked * requests instead of we do now. */ - dwc3_unmap_buffer_from_dma(req); + usb_gadget_unmap_request(&dwc->gadget, &req->request, + req->direction); list_del(&req->list); return ret; } @@ -838,6 +775,9 @@ static int __dwc3_gadget_kick_transfer(struct dwc3_ep *dep, u16 cmd_param, static int __dwc3_gadget_ep_queue(struct dwc3_ep *dep, struct dwc3_request *req) { + struct dwc3 *dwc = dep->dwc; + int ret; + req->request.actual = 0; req->request.status = -EINPROGRESS; req->direction = dep->direction; @@ -855,7 +795,11 @@ static int __dwc3_gadget_ep_queue(struct dwc3_ep *dep, struct dwc3_request *req) * This will also avoid Host cancelling URBs due to too * many NACKs. */ - dwc3_map_buffer_to_dma(req); + ret = usb_gadget_map_request(&dwc->gadget, &req->request, + dep->direction); + if (ret) + return ret; + list_add_tail(&req->list, &dep->request_list); /* @@ -2150,9 +2094,8 @@ int __devinit dwc3_gadget_init(struct dwc3 *dwc) goto err1; } - dwc->setup_buf = dma_alloc_coherent(dwc->dev, - sizeof(*dwc->setup_buf) * 2, - &dwc->setup_buf_addr, GFP_KERNEL); + dwc->setup_buf = kzalloc(sizeof(*dwc->setup_buf) * 2, + GFP_KERNEL); if (!dwc->setup_buf) { dev_err(dwc->dev, "failed to allocate setup buffer\n"); ret = -ENOMEM; @@ -2243,8 +2186,7 @@ err4: dwc->ep0_bounce_addr); err3: - dma_free_coherent(dwc->dev, sizeof(*dwc->setup_buf) * 2, - dwc->setup_buf, dwc->setup_buf_addr); + kfree(dwc->setup_buf); err2: dma_free_coherent(dwc->dev, sizeof(*dwc->ep0_trb), @@ -2273,8 +2215,7 @@ void dwc3_gadget_exit(struct dwc3 *dwc) dma_free_coherent(dwc->dev, 512, dwc->ep0_bounce, dwc->ep0_bounce_addr); - dma_free_coherent(dwc->dev, sizeof(*dwc->setup_buf) * 2, - dwc->setup_buf, dwc->setup_buf_addr); + kfree(dwc->setup_buf); dma_free_coherent(dwc->dev, sizeof(*dwc->ep0_trb), dwc->ep0_trb, dwc->ep0_trb_addr); diff --git a/drivers/usb/dwc3/gadget.h b/drivers/usb/dwc3/gadget.h index d97f467..12f1e10 100644 --- a/drivers/usb/dwc3/gadget.h +++ b/drivers/usb/dwc3/gadget.h @@ -108,8 +108,6 @@ int dwc3_gadget_ep0_queue(struct usb_ep *ep, struct usb_request *request, int __dwc3_gadget_ep_set_halt(struct dwc3_ep *dep, int value); int dwc3_send_gadget_ep_cmd(struct dwc3 *dwc, unsigned ep, unsigned cmd, struct dwc3_gadget_ep_cmd_params *params); -void dwc3_map_buffer_to_dma(struct dwc3_request *req); -void dwc3_unmap_buffer_from_dma(struct dwc3_request *req); /** * dwc3_gadget_ep_get_transfer_index - Gets transfer index from HW -- cgit v0.10.2 From ac8a138cb61d641e1522f3470f1a85f2df21b8bf Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Mon, 19 Dec 2011 11:48:24 +0200 Subject: usb: gadget: langwell: use generic map/unmap functions those routines have everything we need to map/unmap USB requests and it's better to use them. Signed-off-by: Felipe Balbi diff --git a/drivers/usb/gadget/langwell_udc.c b/drivers/usb/gadget/langwell_udc.c index a2bb5e4..e91f11a 100644 --- a/drivers/usb/gadget/langwell_udc.c +++ b/drivers/usb/gadget/langwell_udc.c @@ -406,16 +406,7 @@ static void done(struct langwell_ep *ep, struct langwell_request *req, dma_pool_free(dev->dtd_pool, curr_dtd, curr_dtd->dtd_dma); } - if (req->mapped) { - dma_unmap_single(&dev->pdev->dev, - req->req.dma, req->req.length, - is_in(ep) ? PCI_DMA_TODEVICE : PCI_DMA_FROMDEVICE); - req->req.dma = DMA_ADDR_INVALID; - req->mapped = 0; - } else - dma_sync_single_for_cpu(&dev->pdev->dev, req->req.dma, - req->req.length, - is_in(ep) ? DMA_TO_DEVICE : DMA_FROM_DEVICE); + usb_gadget_unmap_request(&dev->gadget, &req->req, is_in(ep)); if (status != -ESHUTDOWN) dev_dbg(&dev->pdev->dev, @@ -755,7 +746,8 @@ static int langwell_ep_queue(struct usb_ep *_ep, struct usb_request *_req, struct langwell_ep *ep; struct langwell_udc *dev; unsigned long flags; - int is_iso = 0, zlflag = 0; + int is_iso = 0; + int ret; /* always require a cpu-view buffer */ req = container_of(_req, struct langwell_request, req); @@ -782,33 +774,10 @@ static int langwell_ep_queue(struct usb_ep *_ep, struct usb_request *_req, if (unlikely(!dev->driver || dev->gadget.speed == USB_SPEED_UNKNOWN)) return -ESHUTDOWN; - /* set up dma mapping in case the caller didn't */ - if (_req->dma == DMA_ADDR_INVALID) { - /* WORKAROUND: WARN_ON(size == 0) */ - if (_req->length == 0) { - dev_vdbg(&dev->pdev->dev, "req->length: 0->1\n"); - zlflag = 1; - _req->length++; - } - - _req->dma = dma_map_single(&dev->pdev->dev, - _req->buf, _req->length, - is_in(ep) ? DMA_TO_DEVICE : DMA_FROM_DEVICE); - if (zlflag && (_req->length == 1)) { - dev_vdbg(&dev->pdev->dev, "req->length: 1->0\n"); - zlflag = 0; - _req->length = 0; - } - - req->mapped = 1; - dev_vdbg(&dev->pdev->dev, "req->mapped = 1\n"); - } else { - dma_sync_single_for_device(&dev->pdev->dev, - _req->dma, _req->length, - is_in(ep) ? DMA_TO_DEVICE : DMA_FROM_DEVICE); - req->mapped = 0; - dev_vdbg(&dev->pdev->dev, "req->mapped = 0\n"); - } + /* set up dma mapping */ + ret = usb_gadget_map_request(&dev->gadget, &req->req, is_in(ep)); + if (ret) + return ret; dev_dbg(&dev->pdev->dev, "%s queue req %p, len %u, buf %p, dma 0x%08x\n", -- cgit v0.10.2 From ade78f9feb2ee3e03460ef3730d7656c4903d999 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Mon, 19 Dec 2011 11:57:16 +0200 Subject: usb: renesas: gadget: use generic map/unmap routines those routines have everything we need to map/unmap USB requests and it's better to use them. Tested-by: Kuninori Morimoto Signed-off-by: Felipe Balbi diff --git a/drivers/usb/renesas_usbhs/mod_gadget.c b/drivers/usb/renesas_usbhs/mod_gadget.c index f37b20c..937f2d4 100644 --- a/drivers/usb/renesas_usbhs/mod_gadget.c +++ b/drivers/usb/renesas_usbhs/mod_gadget.c @@ -165,69 +165,32 @@ static void usbhsg_queue_push(struct usbhsg_uep *uep, /* * dma map/unmap */ -static int usbhsg_dma_map(struct device *dev, - struct usbhs_pkt *pkt, - enum dma_data_direction dir) -{ - struct usbhsg_request *ureq = usbhsg_pkt_to_ureq(pkt); - struct usb_request *req = &ureq->req; - - if (pkt->dma != DMA_ADDR_INVALID) { - dev_err(dev, "dma is already mapped\n"); - return -EIO; - } - - if (req->dma == DMA_ADDR_INVALID) { - pkt->dma = dma_map_single(dev, pkt->buf, pkt->length, dir); - } else { - dma_sync_single_for_device(dev, req->dma, req->length, dir); - pkt->dma = req->dma; - } - - if (dma_mapping_error(dev, pkt->dma)) { - dev_err(dev, "dma mapping error %llx\n", (u64)pkt->dma); - return -EIO; - } - - return 0; -} - -static int usbhsg_dma_unmap(struct device *dev, - struct usbhs_pkt *pkt, - enum dma_data_direction dir) +static int usbhsg_dma_map_ctrl(struct usbhs_pkt *pkt, int map) { struct usbhsg_request *ureq = usbhsg_pkt_to_ureq(pkt); struct usb_request *req = &ureq->req; - - if (pkt->dma == DMA_ADDR_INVALID) { - dev_err(dev, "dma is not mapped\n"); - return -EIO; - } - - if (req->dma == DMA_ADDR_INVALID) - dma_unmap_single(dev, pkt->dma, pkt->length, dir); - else - dma_sync_single_for_cpu(dev, req->dma, req->length, dir); - - pkt->dma = DMA_ADDR_INVALID; - - return 0; -} - -static int usbhsg_dma_map_ctrl(struct usbhs_pkt *pkt, int map) -{ struct usbhs_pipe *pipe = pkt->pipe; struct usbhsg_uep *uep = usbhsg_pipe_to_uep(pipe); struct usbhsg_gpriv *gpriv = usbhsg_uep_to_gpriv(uep); - struct device *dev = usbhsg_gpriv_to_dev(gpriv); enum dma_data_direction dir; + int ret = 0; - dir = usbhs_pipe_is_dir_in(pipe) ? DMA_FROM_DEVICE : DMA_TO_DEVICE; + dir = usbhs_pipe_is_dir_host(pipe); - if (map) - return usbhsg_dma_map(dev, pkt, dir); - else - return usbhsg_dma_unmap(dev, pkt, dir); + if (map) { + /* it can not use scatter/gather */ + WARN_ON(req->num_sgs); + + ret = usb_gadget_map_request(&gpriv->gadget, req, dir); + if (ret < 0) + return ret; + + pkt->dma = req->dma; + } else { + usb_gadget_unmap_request(&gpriv->gadget, req, dir); + } + + return ret; } /* @@ -657,8 +620,6 @@ static struct usb_request *usbhsg_ep_alloc_request(struct usb_ep *ep, usbhs_pkt_init(usbhsg_ureq_to_pkt(ureq)); - ureq->req.dma = DMA_ADDR_INVALID; - return &ureq->req; } -- cgit v0.10.2 From 220e860009ff4091b75f614eb0d09a7a8486bbe6 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Mon, 19 Dec 2011 12:01:28 +0200 Subject: usb: gadget: amd5536: use generic map/unmap routines those routines have everything we need to map/unmap USB requests and it's better to use them. Signed-off-by: Felipe Balbi diff --git a/drivers/usb/gadget/amd5536udc.c b/drivers/usb/gadget/amd5536udc.c index 1355a2f..2204a4c 100644 --- a/drivers/usb/gadget/amd5536udc.c +++ b/drivers/usb/gadget/amd5536udc.c @@ -827,20 +827,8 @@ __acquires(ep->dev->lock) dev = ep->dev; /* unmap DMA */ - if (req->dma_mapping) { - if (ep->in) - pci_unmap_single(dev->pdev, - req->req.dma, - req->req.length, - PCI_DMA_TODEVICE); - else - pci_unmap_single(dev->pdev, - req->req.dma, - req->req.length, - PCI_DMA_FROMDEVICE); - req->dma_mapping = 0; - req->req.dma = DMA_DONT_USE; - } + if (ep->dma) + usb_gadget_unmap_request(&dev->gadget, &req->req, ep->in); halted = ep->halted; ep->halted = 1; @@ -1089,20 +1077,11 @@ udc_queue(struct usb_ep *usbep, struct usb_request *usbreq, gfp_t gfp) return -ESHUTDOWN; /* map dma (usually done before) */ - if (ep->dma && usbreq->length != 0 - && (usbreq->dma == DMA_DONT_USE || usbreq->dma == 0)) { + if (ep->dma) { VDBG(dev, "DMA map req %p\n", req); - if (ep->in) - usbreq->dma = pci_map_single(dev->pdev, - usbreq->buf, - usbreq->length, - PCI_DMA_TODEVICE); - else - usbreq->dma = pci_map_single(dev->pdev, - usbreq->buf, - usbreq->length, - PCI_DMA_FROMDEVICE); - req->dma_mapping = 1; + retval = usb_gadget_map_request(&udc->gadget, usbreq, ep->in); + if (retval) + return retval; } VDBG(dev, "%s queue req %p, len %d req->td_data=%p buf %p\n", -- cgit v0.10.2 From 05d00fbe87f185d55edf7461d4f714a3909bc908 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Mon, 19 Dec 2011 12:03:57 +0200 Subject: usb: gadget: r8a66597: use generic map/unmap routines those routines have everything we need to map/unmap USB requests and it's better to use them. Signed-off-by: Felipe Balbi diff --git a/drivers/usb/gadget/r8a66597-udc.c b/drivers/usb/gadget/r8a66597-udc.c index f5b8d21..c4401e7 100644 --- a/drivers/usb/gadget/r8a66597-udc.c +++ b/drivers/usb/gadget/r8a66597-udc.c @@ -663,11 +663,7 @@ static int sudmac_alloc_channel(struct r8a66597 *r8a66597, ep->fifoctr = D0FIFOCTR; /* dma mapping */ - req->req.dma = dma_map_single(r8a66597_to_dev(ep->r8a66597), - req->req.buf, req->req.length, - dma->dir ? DMA_TO_DEVICE : DMA_FROM_DEVICE); - - return 0; + return usb_gadget_map_request(&r8a66597->gadget, &req->req, dma->dir); } static void sudmac_free_channel(struct r8a66597 *r8a66597, @@ -677,9 +673,7 @@ static void sudmac_free_channel(struct r8a66597 *r8a66597, if (!r8a66597_is_sudmac(r8a66597)) return; - dma_unmap_single(r8a66597_to_dev(ep->r8a66597), - req->req.dma, req->req.length, - ep->dma->dir ? DMA_TO_DEVICE : DMA_FROM_DEVICE); + usb_gadget_unmap_request(&r8a66597->gadget, &req->req, ep->dma->dir); r8a66597_bclr(r8a66597, DREQE, ep->fifosel); r8a66597_change_curpipe(r8a66597, 0, 0, ep->fifosel); -- cgit v0.10.2 From af93f2c77ac79e2f0bed542b5890f618d9d0cdb3 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Mon, 19 Dec 2011 12:07:40 +0200 Subject: usb: gadget: net2272: use generic map/umap routines those routines have everything we need to map/unmap USB requests and it's better to use them. Signed-off-by: Felipe Balbi diff --git a/drivers/usb/gadget/net2272.c b/drivers/usb/gadget/net2272.c index 7322d29..01ae56f 100644 --- a/drivers/usb/gadget/net2272.c +++ b/drivers/usb/gadget/net2272.c @@ -385,12 +385,9 @@ net2272_done(struct net2272_ep *ep, struct net2272_request *req, int status) status = req->req.status; dev = ep->dev; - if (use_dma && req->mapped) { - dma_unmap_single(dev->dev, req->req.dma, req->req.length, - ep->is_in ? DMA_TO_DEVICE : DMA_FROM_DEVICE); - req->req.dma = DMA_ADDR_INVALID; - req->mapped = 0; - } + if (use_dma && ep->dma) + usb_gadget_unmap_request(&dev->gadget, &req->req, + ep->is_in); if (status && status != -ESHUTDOWN) dev_vdbg(dev->dev, "complete %s req %p stat %d len %u/%u buf %p\n", @@ -850,10 +847,11 @@ net2272_queue(struct usb_ep *_ep, struct usb_request *_req, gfp_t gfp_flags) return -ESHUTDOWN; /* set up dma mapping in case the caller didn't */ - if (use_dma && ep->dma && _req->dma == DMA_ADDR_INVALID) { - _req->dma = dma_map_single(dev->dev, _req->buf, _req->length, - ep->is_in ? DMA_TO_DEVICE : DMA_FROM_DEVICE); - req->mapped = 1; + if (use_dma && ep->dma) { + status = usb_gadget_map_request(&dev->gadget, _req, + ep->is_in); + if (status) + return status; } dev_vdbg(dev->dev, "%s queue req %p, len %d buf %p dma %08llx %s\n", -- cgit v0.10.2 From ae4d7933344e8bc5702cdc56e9498d3852cf3b2a Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Mon, 19 Dec 2011 12:09:56 +0200 Subject: usb: gadget: net2280: use generic map/unmap routines those routines have everything we need to map/unmap USB requests and it's better to use them. Signed-off-by: Felipe Balbi diff --git a/drivers/usb/gadget/net2280.c b/drivers/usb/gadget/net2280.c index cdedd13..a5ccabc 100644 --- a/drivers/usb/gadget/net2280.c +++ b/drivers/usb/gadget/net2280.c @@ -806,12 +806,8 @@ done (struct net2280_ep *ep, struct net2280_request *req, int status) status = req->req.status; dev = ep->dev; - if (req->mapped) { - pci_unmap_single (dev->pdev, req->req.dma, req->req.length, - ep->is_in ? PCI_DMA_TODEVICE : PCI_DMA_FROMDEVICE); - req->req.dma = DMA_ADDR_INVALID; - req->mapped = 0; - } + if (ep->dma) + usb_gadget_unmap_request(&dev->gadget, &req->req, ep->is_in); if (status && status != -ESHUTDOWN) VDEBUG (dev, "complete %s req %p stat %d len %u/%u\n", @@ -857,10 +853,13 @@ net2280_queue (struct usb_ep *_ep, struct usb_request *_req, gfp_t gfp_flags) return -EOPNOTSUPP; /* set up dma mapping in case the caller didn't */ - if (ep->dma && _req->dma == DMA_ADDR_INVALID) { - _req->dma = pci_map_single (dev->pdev, _req->buf, _req->length, - ep->is_in ? PCI_DMA_TODEVICE : PCI_DMA_FROMDEVICE); - req->mapped = 1; + if (ep->dma) { + int ret; + + ret = usb_gadget_map_request(&dev->gadget, _req, + ep->is_in); + if (ret) + return ret; } #if 0 -- cgit v0.10.2 From 6440093f5eae9842feb06e40d41c3bd569b6b461 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Mon, 19 Dec 2011 12:11:44 +0200 Subject: usb: gadget: goku: use generic map/unmap routines those routines have everything we need to map/unmap USB requests and it's better to use them. Signed-off-by: Felipe Balbi diff --git a/drivers/usb/gadget/goku_udc.c b/drivers/usb/gadget/goku_udc.c index 5977749..e1dfd32 100644 --- a/drivers/usb/gadget/goku_udc.c +++ b/drivers/usb/gadget/goku_udc.c @@ -311,12 +311,9 @@ done(struct goku_ep *ep, struct goku_request *req, int status) status = req->req.status; dev = ep->dev; - if (req->mapped) { - pci_unmap_single(dev->pdev, req->req.dma, req->req.length, - ep->is_in ? PCI_DMA_TODEVICE : PCI_DMA_FROMDEVICE); - req->req.dma = DMA_ADDR_INVALID; - req->mapped = 0; - } + + if (ep->dma) + usb_gadget_unmap_request(&dev->gadget, &req->req, ep->is_in); #ifndef USB_TRACE if (status && status != -ESHUTDOWN) @@ -737,10 +734,11 @@ goku_queue(struct usb_ep *_ep, struct usb_request *_req, gfp_t gfp_flags) return -EBUSY; /* set up dma mapping in case the caller didn't */ - if (ep->dma && _req->dma == DMA_ADDR_INVALID) { - _req->dma = pci_map_single(dev->pdev, _req->buf, _req->length, - ep->is_in ? PCI_DMA_TODEVICE : PCI_DMA_FROMDEVICE); - req->mapped = 1; + if (ep->dma) { + status = usb_gadget_map_request(&dev->gadget, &req->req, + ep->is_in); + if (status) + return status; } #ifdef USB_TRACE -- cgit v0.10.2