dummy_hcd provides (alloc|free)_stream() callbacks but there are not doing anything. This patch changes this and implements stream allocation / de-allocation support. Dummy supports 15 streams on host side but this value has no real restrictions. Error checking is simple and limited not to allocate a streams on one endpoint twice. The check for SuperSpeed has been removed because USB-core does this already. The transfer code has been modified so we match the proper stream once we found an urb and usb_request which match. Signed-off-by: Sebastian Andrzej Siewior <bigeasy@xxxxxxxxxxxxx> --- drivers/usb/gadget/dummy_hcd.c | 107 +++++++++++++++++++++++++++++++++++----- 1 files changed, 94 insertions(+), 13 deletions(-) diff --git a/drivers/usb/gadget/dummy_hcd.c b/drivers/usb/gadget/dummy_hcd.c index 887a2d2..c84032a 100644 --- a/drivers/usb/gadget/dummy_hcd.c +++ b/drivers/usb/gadget/dummy_hcd.c @@ -142,6 +142,7 @@ static const char *const ep_name [] = { #define DUMMY_ENDPOINTS ARRAY_SIZE(ep_name) /*-------------------------------------------------------------------------*/ +#define DUMMY_MAX_STREAMS 15 struct urbp { struct urb *urb; @@ -167,6 +168,7 @@ struct dummy_hcd { struct usb_device *udev; struct list_head urbp_list; + u32 stream_en_ep; unsigned active:1; unsigned old_active:1; @@ -1030,6 +1032,16 @@ static struct platform_driver dummy_udc_driver = { /*-------------------------------------------------------------------------*/ +static u32 dummy_get_ep_idx(const struct usb_endpoint_descriptor *desc) +{ + u32 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. @@ -1051,6 +1063,7 @@ static int dummy_urb_enqueue ( struct urbp *urbp; unsigned long flags; int rc; + int ep_idx; urbp = kmalloc (sizeof *urbp, mem_flags); if (!urbp) @@ -1060,6 +1073,28 @@ static int dummy_urb_enqueue ( dum_hcd = hcd_to_dummy_hcd(hcd); spin_lock_irqsave(&dum_hcd->dum->lock, flags); + + if (usb_endpoint_xfer_bulk(&urb->ep->desc)) { + ep_idx = dummy_get_ep_idx(&urb->ep->desc); + if ((1 << ep_idx) & dum_hcd->stream_en_ep) { + if (urb->stream_id == 0) { + dev_err(dummy_dev(dum_hcd), + "Stream id 0 on stream enabled endpoint\n"); + rc = -EINVAL; + kfree(urbp); + goto done; + } + if (urb->stream_id > DUMMY_MAX_STREAMS) { + dev_err(dummy_dev(dum_hcd), + "Stream id %d is out of range.\n", + urb->stream_id); + rc = -EINVAL; + kfree(urbp); + goto done; + } + } + } + rc = usb_hcd_link_urb_to_ep(hcd, urb); if (rc) { kfree(urbp); @@ -1163,11 +1198,25 @@ static int dummy_perform_transfer(struct urb *urb, struct dummy_request *req, return trans; } +static int dummy_ep_stream_en(struct dummy_hcd *dum, 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); + if ((1 << index) & dum->stream_en_ep) + return 1; + return 0; +} + /* 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: @@ -1177,6 +1226,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). * @@ -1704,7 +1758,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; } @@ -2161,6 +2215,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; @@ -2250,11 +2305,25 @@ 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); + int max_stream; + u32 index; + int i; + + for (i = 0; i < num_eps; i++) { + if (!usb_endpoint_xfer_bulk(&eps[i]->desc)) + return -EINVAL; + index = dummy_get_ep_idx(&eps[i]->desc); + if ((1 << index) & dum_hcd->stream_en_ep) + return -EINVAL; + } + + for (i = 0; i < num_eps; i++) { + index = dummy_get_ep_idx(&eps[i]->desc); + dum_hcd->stream_en_ep |= 1 << index; + } + max_stream = min_t(u32, num_streams, DUMMY_MAX_STREAMS); + return max_stream; } /* Reverts a group of bulk endpoints back to not using stream IDs. */ @@ -2262,10 +2331,22 @@ 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__); + struct dummy_hcd *dum_hcd = hcd_to_dummy_hcd(hcd); + u32 index; + u32 i; + + for (i = 0; i < num_eps; i++) { + if (!usb_endpoint_xfer_bulk(&eps[i]->desc)) + return -EINVAL; + index = dummy_get_ep_idx(&eps[i]->desc); + if (!((1 << index) & dum_hcd->stream_en_ep)) + return -EINVAL; + } + + for (i = 0; i < num_eps; i++) { + index = dummy_get_ep_idx(&eps[i]->desc); + dum_hcd->stream_en_ep &= ~(1 << index); + } return 0; } -- 1.7.7.3 -- To unsubscribe from this list: send the line "unsubscribe linux-usb" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html