int usb_ept_queue_xfer(struct msm_endpoint *ept, struct usb_request *_req) { unsigned long flags; struct msm_request *req = to_msm_request(_req); struct msm_request *last; struct usb_info *ui = ept->ui; struct ept_queue_item *item = req->item; unsigned length = req->req.length; if (length > 0x4000) return -EMSGSIZE; spin_lock_irqsave(&ui->lock, flags); if (req->busy) { req->req.status = -EBUSY; spin_unlock_irqrestore(&ui->lock, flags); INFO("usb_ept_queue_xfer() tried to queue busy request\n"); return -EBUSY; } if (!ui->online && (ept->num != 0)) { req->req.status = -ESHUTDOWN; spin_unlock_irqrestore(&ui->lock, flags); INFO("usb_ept_queue_xfer() called while offline\n"); return -ESHUTDOWN; } req->busy = 1; req->live = 0; req->next = 0; req->req.status = -EBUSY; req->dma = dma_map_single(NULL, req->req.buf, length, (ept->flags & EPT_FLAG_IN) ? DMA_TO_DEVICE : DMA_FROM_DEVICE); /* prepare the transaction descriptor item for the hardware */ item->next = TERMINATE; item->info = INFO_BYTES(length) | INFO_IOC | INFO_ACTIVE; item->page0 = req->dma; item->page1 = (req->dma + 0x1000) & 0xfffff000; item->page2 = (req->dma + 0x2000) & 0xfffff000; item->page3 = (req->dma + 0x3000) & 0xfffff000; /* Add the new request to the end of the queue */ last = ept->last; if (last) { /* Already requests in the queue. add us to the * end, but let the completion interrupt actually * start things going, to avoid hw issues */ last->next = req; /* only modify the hw transaction next pointer if * that request is not live */ if (!last->live) last->item->next = req->item_dma; } else { /* queue was empty -- kick the hardware */ ept->req = req; usb_ept_start(ept); } ept->last = req; spin_unlock_irqrestore(&ui->lock, flags); return 0; }
/* * Assumes that TDs allocated already are not freed. * But it can handle case where TDs are freed as well. */ int udc_request_queue(struct udc_endpoint *ept, struct udc_request *_req) { unsigned xfer = 0; struct ept_queue_item *item, *curr_item; struct usb_request *req = (struct usb_request *)_req; unsigned phys = (unsigned)req->req.buf; unsigned len = req->req.length; unsigned int count = 0; curr_item = NULL; xfer = (len > MAX_TD_XFER_SIZE) ? MAX_TD_XFER_SIZE : len; /* * First TD allocated during request allocation */ item = req->item; item->info = INFO_BYTES(xfer) | INFO_ACTIVE; item->page0 = phys; item->page1 = (phys & 0xfffff000) + 0x1000; item->page2 = (phys & 0xfffff000) + 0x2000; item->page3 = (phys & 0xfffff000) + 0x3000; item->page4 = (phys & 0xfffff000) + 0x4000; phys += xfer; curr_item = item; len -= xfer; /* * If transfer length is more then * accomodate by 1 TD * we add more transfer descriptors */ while (len > 0) { xfer = (len > MAX_TD_XFER_SIZE) ? MAX_TD_XFER_SIZE : len; if (curr_item->next == TERMINATE) { /* * Allocate new TD only if chain doesnot * exist already */ item = memalign(CACHE_LINE, ROUNDUP(sizeof(struct ept_queue_item), CACHE_LINE)); if (!item) { dprintf(ALWAYS, "allocate USB item fail ept%d" "%s queue\n", "td count = %d\n", ept->num, ept->in ? "in" : "out", count); return -1; } else { count ++; curr_item->next = PA(item); item->next = TERMINATE; } } else /* Since next TD in chain already exists */ item = VA(curr_item->next); /* Update TD with transfer information */ item->info = INFO_BYTES(xfer) | INFO_ACTIVE; item->page0 = phys; item->page1 = (phys & 0xfffff000) + 0x1000; item->page2 = (phys & 0xfffff000) + 0x2000; item->page3 = (phys & 0xfffff000) + 0x3000; item->page4 = (phys & 0xfffff000) + 0x4000; curr_item = item; len -= xfer; phys += xfer; } /* Terminate and set interrupt for last TD */ curr_item->next = TERMINATE; curr_item->info |= INFO_IOC; enter_critical_section(); ept->head->next = PA(req->item); ept->head->info = 0; ept->req = req; arch_clean_invalidate_cache_range((addr_t) ept, sizeof(struct udc_endpoint)); arch_clean_invalidate_cache_range((addr_t) ept->head, sizeof(struct ept_queue_head)); arch_clean_invalidate_cache_range((addr_t) ept->req, sizeof(struct usb_request)); arch_clean_invalidate_cache_range((addr_t) VA(req->req.buf), req->req.length); item = req->item; /* Write all TD's to memory from cache */ while (item != NULL) { curr_item = item; if (curr_item->next == TERMINATE) item = NULL; else item = curr_item->next; arch_clean_invalidate_cache_range((addr_t) curr_item, sizeof(struct ept_queue_item)); } DBG("ept%d %s queue req=%p\n", ept->num, ept->in ? "in" : "out", req); writel(ept->bit, USB_ENDPTPRIME); exit_critical_section(); return 0; }