/** * This function completes the ep0 control transfer. */ static int32_t ep0_complete_request( pcd_struct_t * pcd) { deptsiz0_data_t deptsiz; int is_last = 0; dwc_ep_t* ep = &g_dwc_eps[0]; DBG("ep0_complete_request()\n"); if (pcd->ep0state == EP0_STATUS) { is_last = 1; } else if (ep->xfer_len == 0) { ep->xfer_len = 0; ep->xfer_count = 0; ep->sent_zlp = 1; dwc_otg_ep_start_transfer( ep ); return 1; } else if (ep->is_in) { deptsiz.d32 = dwc_read_reg32(DWC_REG_IN_EP_TSIZE(0) ); if (deptsiz.b.xfersize == 0) { /* Is a Zero Len Packet needed? */ do_setup_out_status_phase(pcd); } } else { /* ep0-OUT */ do_setup_in_status_phase(pcd); } /* Complete the request */ if (is_last) { ep->start_xfer_buff = 0; ep->xfer_buff = 0; ep->xfer_len = 0; return 1; } return 0; }
/** * This function starts the Zero-Length Packet for the IN status phase * of a 2 stage control transfer. */ static void do_setup_in_status_phase( pcd_struct_t *_pcd) { dwc_ep_t *ep0 = &g_dwc_eps[0]; if (_pcd->ep0state == EP0_STALL){ return; } _pcd->ep0state = EP0_STATUS; DBG( "EP0 IN ZLP\n"); ep0->xfer_len = 0; ep0->xfer_count = 0; ep0->is_in = 1; dwc_otg_ep_start_transfer( ep0 ); /* Prepare for more SETUP Packets */ ep0_out_start(); }
/** * This function is used to submit an I/O Request to an EP. * * - When the request completes the request's completion callback * is called to return the request to the driver. * - An EP, except control EPs, may have multiple requests * pending. * - Once submitted the request cannot be examined or modified. * - Each request is turned into one or more packets. * - A BULK EP can queue any amount of data; the transfer is * packetized. * - Zero length Packets are specified with the request 'zero' * flag. */ static int dwc_otg_pcd_ep_queue(struct usb_ep *_ep, struct usb_request *_req, int _gfp_flags) { int prevented = 0; dwc_otg_pcd_request_t *req; dwc_otg_pcd_ep_t *ep; dwc_otg_pcd_t *pcd; unsigned long flags = 0; DWC_DEBUGPL(DBG_PCDV,"%s(%p,%p,%d)\n", __func__, _ep, _req, _gfp_flags); req = container_of(_req, dwc_otg_pcd_request_t, req); if (!_req || !_req->complete || !_req->buf || !list_empty(&req->queue)) { if( !_req ) printk("bad _req\n"); if( !_req->complete ) printk("bad _req->complete\n"); if( !_req->buf ) printk("bad _req->buf\n"); if( !list_empty(&req->queue) ) printk("bad list_empty\n"); DWC_WARN("%s, bad params\n", __func__); return -EINVAL; } ep = container_of(_ep, dwc_otg_pcd_ep_t, ep); if (!_ep || (!ep->desc && ep->dwc_ep.num != 0)) { DWC_WARN("%s, bad ep\n", __func__); return -EINVAL; } pcd = ep->pcd; //cathy, if suspended, drop request if ( (GET_CORE_IF(pcd)->dev_if->suspended == 1) && (ep->dwc_ep.num != 0) ) { DWC_DEBUGPL(DBG_PCDV,"%s, epnum = %d, drop request\n", __func__, ep->dwc_ep.num); return -ESHUTDOWN; } if (!pcd->driver || pcd->gadget.speed == USB_SPEED_UNKNOWN) { DWC_DEBUGPL(DBG_PCDV, "gadget.speed=%d\n", pcd->gadget.speed); DWC_WARN("%s, bogus device state\n", __func__); return -ESHUTDOWN; } DWC_DEBUGPL(DBG_PCD, "%s queue req %p, len %d buf %p\n", _ep->name, _req, _req->length, _req->buf); if (!GET_CORE_IF(pcd)->core_params->opt) { if (ep->dwc_ep.num != 0) { DWC_ERROR("%s queue req %p, len %d buf %p\n", _ep->name, _req, _req->length, _req->buf); } } SPIN_LOCK_IRQSAVE(&ep->pcd->lock, flags); #if defined(DEBUG) & defined(VERBOSE) dump_msg(_req->buf, _req->length); #endif _req->status = -EINPROGRESS; _req->actual = 0; /* * For EP0 IN without premature status, zlp is required? */ if (ep->dwc_ep.num == 0 && ep->dwc_ep.is_in) { DWC_DEBUGPL(DBG_PCDV, "%s-OUT ZLP\n", _ep->name); //_req->zero = 1; } /* Start the transfer */ if (list_empty(&ep->queue) && !ep->stopped) { /* EP0 Transfer? */ if (ep->dwc_ep.num == 0) { switch (pcd->ep0state) { case EP0_IN_DATA_PHASE: DWC_DEBUGPL(DBG_PCD, "%s ep0: EP0_IN_DATA_PHASE\n", __func__); break; case EP0_OUT_DATA_PHASE: DWC_DEBUGPL(DBG_PCD, "%s ep0: EP0_OUT_DATA_PHASE\n", __func__); if (pcd->request_config) { /* Complete STATUS PHASE */ ep->dwc_ep.is_in = 1; pcd->ep0state = EP0_STATUS; } break; default: DWC_DEBUGPL(DBG_ANY, "ep0: odd state %d\n", pcd->ep0state); SPIN_UNLOCK_IRQRESTORE(&pcd->lock, flags); return -EL2HLT; } ep->dwc_ep.dma_addr = (u32)_req->buf & ~Uncache_Mask; //_req->dma; //cathy ep->dwc_ep.start_xfer_buff = _req->buf; ep->dwc_ep.xfer_buff = _req->buf; ep->dwc_ep.xfer_len = _req->length; ep->dwc_ep.xfer_count = 0; ep->dwc_ep.sent_zlp = 0; ep->dwc_ep.total_len = ep->dwc_ep.xfer_len; dwc_otg_ep0_start_transfer( GET_CORE_IF(pcd), &ep->dwc_ep ); } else { /* Setup and start the Transfer */ ep->dwc_ep.dma_addr = (u32)_req->buf & ~Uncache_Mask; //_req->dma; //cathy //ep->dwc_ep.dma_addr = _req->dma; ep->dwc_ep.start_xfer_buff = _req->buf; ep->dwc_ep.xfer_buff = _req->buf; ep->dwc_ep.xfer_len = _req->length; ep->dwc_ep.xfer_count = 0; ep->dwc_ep.sent_zlp = 0; ep->dwc_ep.total_len = ep->dwc_ep.xfer_len; dwc_otg_ep_start_transfer( GET_CORE_IF(pcd), &ep->dwc_ep ); } } if ((req != 0) || prevented) { ++pcd->request_pending; list_add_tail(&req->queue, &ep->queue); //cathy #if 0 if (ep->dwc_ep.is_in && ep->stopped && !(GET_CORE_IF(pcd)->dma_enable)) { /** @todo NGS Create a function for this. */ diepmsk_data_t diepmsk = { .d32 = 0}; diepmsk.b.intktxfemp = 1; dwc_modify_reg32( &GET_CORE_IF(pcd)->dev_if->dev_global_regs->diepmsk, 0, diepmsk.d32 ); } #endif }