static void ci_ep_free_request(struct usb_ep *ep, struct usb_request *req)
{
- struct ci_req *ci_req;
+ struct ci_ep *ci_ep = container_of(ep, struct ci_ep, ep);
+ struct ci_req *ci_req = container_of(req, struct ci_req, req);
+ int num;
+
+ num = ci_ep->desc->bEndpointAddress & USB_ENDPOINT_NUMBER_MASK;
+ if (num == 0)
+ controller.ep0_req = 0;
- ci_req = container_of(req, struct ci_req, req);
if (ci_req->b_buf)
free(ci_req->b_buf);
free(ci_req);
ci_req = list_first_entry(&ci_ep->queue, struct ci_req, queue);
len = ci_req->req.length;
- item->next = TERMINATE;
- item->info = INFO_BYTES(len) | INFO_IOC | INFO_ACTIVE;
+ item->info = INFO_BYTES(len) | INFO_ACTIVE;
item->page0 = (uint32_t)ci_req->hw_buf;
item->page1 = ((uint32_t)ci_req->hw_buf & 0xfffff000) + 0x1000;
item->page2 = ((uint32_t)ci_req->hw_buf & 0xfffff000) + 0x2000;
item->page3 = ((uint32_t)ci_req->hw_buf & 0xfffff000) + 0x3000;
item->page4 = ((uint32_t)ci_req->hw_buf & 0xfffff000) + 0x4000;
- ci_flush_qtd(num);
head->next = (unsigned) item;
head->info = 0;
+ /*
+ * When sending the data for an IN transaction, the attached host
+ * knows that all data for the IN is sent when one of the following
+ * occurs:
+ * a) A zero-length packet is transmitted.
+ * b) A packet with length that isn't an exact multiple of the ep's
+ * maxpacket is transmitted.
+ * c) Enough data is sent to exactly fill the host's maximum expected
+ * IN transaction size.
+ *
+ * One of these conditions MUST apply at the end of an IN transaction,
+ * or the transaction will not be considered complete by the host. If
+ * none of (a)..(c) already applies, then we must force (a) to apply
+ * by explicitly sending an extra zero-length packet.
+ */
+ /* IN !a !b !c */
+ if (in && len && !(len % ci_ep->ep.maxpacket) && ci_req->req.zero) {
+ /*
+ * Each endpoint has 2 items allocated, even though typically
+ * only 1 is used at a time since either an IN or an OUT but
+ * not both is queued. For an IN transaction, item currently
+ * points at the second of these items, so we know that we
+ * can use (item - 1) to transmit the extra zero-length packet
+ */
+ item->next = (unsigned)(item - 1);
+ item--;
+ item->info = INFO_ACTIVE;
+ }
+
+ item->next = TERMINATE;
+ item->info |= INFO_IOC;
+
+ ci_flush_qtd(num);
+
DBG("ept%d %s queue len %x, req %p, buffer %p\n",
num, in ? "in" : "out", len, ci_req, ci_req->hw_buf);
ci_flush_qh(num);
return value;
}
+void udc_disconnect(void)
+{
+ struct ci_udc *udc = (struct ci_udc *)controller.ctrl->hcor;
+ /* disable pullup */
+ stop_activity();
+ writel(USBCMD_FS2, &udc->usbcmd);
+ udelay(800);
+ if (controller.driver)
+ controller.driver->disconnect(&controller.gadget);
+}
+
static int ci_pullup(struct usb_gadget *gadget, int is_on)
{
struct ci_udc *udc = (struct ci_udc *)controller.ctrl->hcor;
/* Turn on the USB connection by enabling the pullup resistor */
writel(USBCMD_ITC(MICRO_8FRAME) | USBCMD_RUN, &udc->usbcmd);
} else {
- stop_activity();
- writel(USBCMD_FS2, &udc->usbcmd);
- udelay(800);
- if (controller.driver)
- controller.driver->disconnect(gadget);
+ udc_disconnect();
}
return 0;
}
-void udc_disconnect(void)
-{
- struct ci_udc *udc = (struct ci_udc *)controller.ctrl->hcor;
- /* disable pullup */
- stop_activity();
- writel(USBCMD_FS2, &udc->usbcmd);
- udelay(800);
- if (controller.driver)
- controller.driver->disconnect(&controller.gadget);
-}
-
static int ci_udc_probe(void)
{
struct ept_queue_head *head;
ci_ep_alloc_request(&controller.ep[0].ep, 0);
if (!controller.ep0_req) {
+ free(controller.items_mem);
free(controller.epts);
return -ENOMEM;
}
int usb_gadget_unregister_driver(struct usb_gadget_driver *driver)
{
+ udc_disconnect();
+
+ ci_ep_free_request(&controller.ep[0].ep, &controller.ep0_req->req);
+ free(controller.items_mem);
+ free(controller.epts);
+
return 0;
}