This patch removes the limitation of having a limited amount of only four active tds on one endpoint. We use the linked list implementation to manage all tds which get added and removed by hardware_{en,de}queue. Signed-off-by: Michael Grzeschik <m.grzeschik@xxxxxxxxxxxxxx> --- drivers/usb/chipidea/udc.c | 79 ++++++++++++++++++++++++++++++++------------ 1 file changed, 58 insertions(+), 21 deletions(-) diff --git a/drivers/usb/chipidea/udc.c b/drivers/usb/chipidea/udc.c index 98433cd..58efa49 100644 --- a/drivers/usb/chipidea/udc.c +++ b/drivers/usb/chipidea/udc.c @@ -415,7 +415,9 @@ static void setup_td_bits(struct td_node *tdnode, unsigned length) static int add_td_to_list(struct ci13xxx_ep *mEp, struct ci13xxx_req *mReq, unsigned length) { + int i; struct td_node *lastnode, *node = kzalloc(sizeof(struct td_node), GFP_ATOMIC); + u32 temp; if (node == NULL) return -ENOMEM; @@ -429,6 +431,15 @@ static int add_td_to_list(struct ci13xxx_ep *mEp, struct ci13xxx_req *mReq, unsi setup_td_bits(node, length); + temp = (u32) (mReq->req.dma + mReq->req.actual); + if (length) { + node->ptr->page[0] = temp; + for (i = 1; i <= TD_COUNT; i++) + node->ptr->page[i] = (temp + i * CI13XXX_PAGE_SIZE) & ~TD_RESERVED_MASK; + } + + mReq->req.actual += length; + /* get the last entry */ lastnode = list_entry(mReq->tds.prev, struct td_node, td); @@ -454,6 +465,7 @@ static int _hardware_enqueue(struct ci13xxx_ep *mEp, struct ci13xxx_req *mReq) int ret = 0; unsigned length = mReq->req.length; struct td_node *reqnode, *lastnode; + unsigned reqnodesize = min(length, (unsigned)(TD_COUNT * CI13XXX_PAGE_SIZE)); /* don't queue twice */ if (mReq->req.status == -EALREADY) @@ -468,13 +480,25 @@ static int _hardware_enqueue(struct ci13xxx_ep *mEp, struct ci13xxx_req *mReq) reqnode = list_first_entry(&mReq->tds, struct td_node, td); - setup_td_bits(reqnode, length); + setup_td_bits(reqnode, reqnodesize); reqnode->ptr->page[0] = mReq->req.dma; for (i = 1; i <= TD_COUNT; i++) reqnode->ptr->page[i] = (mReq->req.dma + i * CI13XXX_PAGE_SIZE) & ~TD_RESERVED_MASK; + mReq->req.actual += reqnodesize; + + if (length > TD_COUNT * CI13XXX_PAGE_SIZE) { + int rest = length - mReq->req.actual; + while (rest > 0) { + unsigned count = min(length - mReq->req.actual, + (unsigned)(TD_COUNT * CI13XXX_PAGE_SIZE)); + add_td_to_list(mEp, mReq, count); + rest -= TD_COUNT * CI13XXX_PAGE_SIZE; + } + } + if (mReq->req.zero && length && (length % mEp->ep.maxpacket == 0)) add_td_to_list(mEp, mReq, 0); @@ -485,6 +509,7 @@ static int _hardware_enqueue(struct ci13xxx_ep *mEp, struct ci13xxx_req *mReq) if (!mReq->req.no_interrupt) lastnode->ptr->token |= TD_IOC; + mReq->req.actual = 0; if (!list_empty(&mEp->qh.queue)) { struct ci13xxx_req *mReqPrev; int n = hw_ep_bit(mEp->num, mEp->dir); @@ -532,6 +557,8 @@ done: static int _hardware_dequeue(struct ci13xxx_ep *mEp, struct ci13xxx_req *mReq) { struct td_node *node, *tmp_node, *node_first; + unsigned remaining_length; + unsigned actual = mReq->req.length; if (mReq->req.status != -EALREADY) return -EINVAL; @@ -539,9 +566,37 @@ static int _hardware_dequeue(struct ci13xxx_ep *mEp, struct ci13xxx_req *mReq) node_first = list_first_entry(&mReq->tds, struct td_node, td); + mReq->req.status = 0; + list_for_each_entry_safe(node, tmp_node, &mReq->tds, td) { - if ((TD_STATUS_ACTIVE & node->ptr->token) != 0) + if ((TD_STATUS_ACTIVE & node->ptr->token) != 0) { + mReq->req.status = -EALREADY; return -EBUSY; + } + + remaining_length = (node->ptr->token & TD_TOTAL_BYTES); + remaining_length >>= ffs_nr(TD_TOTAL_BYTES); + actual -= remaining_length; + + mReq->req.status = node->ptr->token & TD_STATUS; + if ((TD_STATUS_HALTED & mReq->req.status) != 0) { + mReq->req.status = -1; + break; + } else if ((TD_STATUS_DT_ERR & mReq->req.status) != 0) { + mReq->req.status = -1; + break; + } else if ((TD_STATUS_TR_ERR & mReq->req.status) != 0) { + mReq->req.status = -1; + break; + } + + if (remaining_length) { + if (mEp->dir) { + mReq->req.status = -EPROTO; + break; + } + } + if (node != node_first) { dma_pool_free(mEp->td_pool, node->ptr, node->dma); list_del_init(&node->td); @@ -550,21 +605,9 @@ static int _hardware_dequeue(struct ci13xxx_ep *mEp, struct ci13xxx_req *mReq) } } - mReq->req.status = 0; - usb_gadget_unmap_request(&mEp->ci->gadget, &mReq->req, mEp->dir); - mReq->req.status = node_first->ptr->token & TD_STATUS; - if ((TD_STATUS_HALTED & mReq->req.status) != 0) - mReq->req.status = -1; - else if ((TD_STATUS_DT_ERR & mReq->req.status) != 0) - mReq->req.status = -1; - else if ((TD_STATUS_TR_ERR & mReq->req.status) != 0) - mReq->req.status = -1; - - mReq->req.actual = node_first->ptr->token & TD_TOTAL_BYTES; - mReq->req.actual >>= ffs_nr(TD_TOTAL_BYTES); - mReq->req.actual = mReq->req.length - mReq->req.actual; + mReq->req.actual = actual; mReq->req.actual = mReq->req.status ? 0 : mReq->req.actual; return mReq->req.actual; @@ -747,12 +790,6 @@ static int _ep_queue(struct usb_ep *ep, struct usb_request *req, goto done; } - if (req->length > TD_COUNT * CI13XXX_PAGE_SIZE) { - req->length = TD_COUNT * CI13XXX_PAGE_SIZE; - retval = -EMSGSIZE; - dev_warn(mEp->ci->dev, "request length truncated\n"); - } - dbg_queue(_usb_addr(mEp), req, retval); /* push request */ -- 1.7.10.4 -- 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