📄 dwc_otg_pcd.c
字号:
ep = container_of(_ep, dwc_otg_pcd_ep_t, ep); pcd = ep->pcd; DWC_DEBUGPL(DBG_PCDV, "%s(%p,%p,%0x,%d)\n", __func__, _ep, _buf, _dma, _bytes); if (GET_CORE_IF(pcd)->dma_enable) { dma_free_coherent(NULL, _bytes, _buf, _dma); } else { kfree(_buf); }}/** * 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,#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,20) int _gfp_flags#else gfp_t _gfp_flags#endif ){ 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)) { 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) /* || ep->stopped != 0 */ ) { DWC_WARN("%s, bad ep\n", __func__); return -EINVAL; } pcd = ep->pcd; 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_IN_STATUS_PHASE; } break; case EP0_IN_STATUS_PHASE: DWC_DEBUGPL(DBG_PCD, "%s ep0: EP0_IN_STATUS_PHASE\n", __func__); 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 = _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; if (_req->zero) { if ((ep->dwc_ep.xfer_len % ep->dwc_ep.maxpacket == 0) && (ep->dwc_ep.xfer_len != 0)) { ep->dwc_ep.sent_zlp = 1; } } dwc_otg_ep0_start_transfer(GET_CORE_IF(pcd), &ep->dwc_ep); } else { /* Setup and start the Transfer */ 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; if (_req->zero) { if ((ep->dwc_ep.xfer_len % ep->dwc_ep.maxpacket == 0) && (ep->dwc_ep.xfer_len != 0)) { ep->dwc_ep.sent_zlp = 1; } } 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); 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); } } SPIN_UNLOCK_IRQRESTORE(&pcd->lock, flags); return 0;}/** * This function cancels an I/O request from an EP. */static int dwc_otg_pcd_ep_dequeue(struct usb_ep *_ep, struct usb_request *_req){ dwc_otg_pcd_request_t *req; dwc_otg_pcd_ep_t *ep; dwc_otg_pcd_t *pcd; unsigned long flags; DWC_DEBUGPL(DBG_PCDV, "%s(%p,%p)\n", __func__, _ep, _req); ep = container_of(_ep, dwc_otg_pcd_ep_t, ep); if (!_ep || !_req || (!ep->desc && ep->dwc_ep.num != 0)) { DWC_WARN("%s, bad argument\n", __func__); return -EINVAL; } pcd = ep->pcd; if (!pcd->driver || pcd->gadget.speed == USB_SPEED_UNKNOWN) { DWC_WARN("%s, bogus device state\n", __func__); return -ESHUTDOWN; } SPIN_LOCK_IRQSAVE(&pcd->lock, flags); DWC_DEBUGPL(DBG_PCDV, "%s %s %s %p\n", __func__, _ep->name, ep->dwc_ep.is_in ? "IN" : "OUT", _req); /* make sure it's actually queued on this endpoint */ list_for_each_entry(req, &ep->queue, queue) { if (&req->req == _req) { break; } } if (&req->req != _req) { SPIN_UNLOCK_IRQRESTORE(&pcd->lock, flags); return -EINVAL; } if (!list_empty(&req->queue)) { request_done(ep, req, -ECONNRESET); } else { req = 0; } SPIN_UNLOCK_IRQRESTORE(&pcd->lock, flags); return req ? 0 : -EOPNOTSUPP;}/** * usb_ep_set_halt stalls an endpoint. * * usb_ep_clear_halt clears an endpoint halt and resets its data * toggle. * * Both of these functions are implemented with the same underlying * function. The behavior depends on the value argument. * * @param[in] _ep the Endpoint to halt or clear halt. * @param[in] _value * - 0 means clear_halt. * - 1 means set_halt, * - 2 means clear stall lock flag. * - 3 means set stall lock flag. */static int dwc_otg_pcd_ep_set_halt(struct usb_ep *_ep, int _value){ int retval = 0; unsigned long flags; dwc_otg_pcd_ep_t *ep = 0; DWC_DEBUGPL(DBG_PCD, "HALT %s %d\n", _ep->name, _value); ep = container_of(_ep, dwc_otg_pcd_ep_t, ep); if (!_ep || (!ep->desc && ep != &ep->pcd->ep0) || ep->desc->bmAttributes == USB_ENDPOINT_XFER_ISOC) { DWC_WARN("%s, bad ep\n", __func__); return -EINVAL; } SPIN_LOCK_IRQSAVE(&ep->pcd->lock, flags); if (!list_empty(&ep->queue)) { DWC_WARN("%s() %s XFer In process\n", __func__, _ep->name); retval = -EAGAIN; } else if (_value == 0) { dwc_otg_ep_clear_stall(ep->pcd->otg_dev->core_if, &ep->dwc_ep); } else if (_value == 1) { if (ep->dwc_ep.is_in == 1 && ep->pcd->otg_dev->core_if->dma_desc_enable) { dtxfsts_data_t txstatus; fifosize_data_t txfifosize; txfifosize.d32 = dwc_read_reg32(&ep->pcd->otg_dev->core_if->core_global_regs-> dptxfsiz_dieptxf[ep->dwc_ep.tx_fifo_num]); txstatus.d32 = dwc_read_reg32(&ep->pcd->otg_dev->core_if->dev_if-> in_ep_regs[ep->dwc_ep.num]->dtxfsts); if (txstatus.b.txfspcavail < txfifosize.b.depth) { DWC_WARN("%s() %s Data In Tx Fifo\n", __func__, _ep->name); retval = -EAGAIN; } else { if (ep->dwc_ep.num == 0) { ep->pcd->ep0state = EP0_STALL; } ep->stopped = 1; dwc_otg_ep_set_stall(ep->pcd->otg_dev->core_if, &ep->dwc_ep); } } else { if (ep->dwc_ep.num == 0) { ep->pcd->ep0state = EP0_STALL; } ep->stopped = 1; dwc_otg_ep_set_stall(ep->pcd->otg_dev->core_if, &ep->dwc_ep); } } else if (_value == 2) { ep->dwc_ep.stall_clear_flag = 0; } else if (_value == 3) { ep->dwc_ep.stall_clear_flag = 1; } SPIN_UNLOCK_IRQRESTORE(&ep->pcd->lock, flags); return retval;}#ifdef _EN_ISOC_/** * This function is used to submit an ISOC Transfer Request to an EP. * * - Every time a sync period completes the request's completion callback * is called to provide data to the gadget driver. * - Once submitted the request cannot be modified. * - Each request is turned into periodic data packets untill ISO * Transfer is stopped.. */static int dwc_otg_pcd_iso_ep_start(struct usb_ep *_ep, struct usb_iso_request *_req,#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,20) int _gfp_flags#else gfp_t _gfp_flags#endif ){ dwc_otg_pcd_ep_t *ep; dwc_otg_pcd_t *pcd; dwc_ep_t *dwc_ep; dsts_data_t dsts = {.d32 = 0 }; depctl_data_t depctl = {.d32 = 0 }; volatile uint32_t *addr; unsigned long flags = 0; int32_t frm_data; int i, j; dwc_otg_core_if_t *core_if; dctl_data_t dctl; dcfg_data_t dcfg; if (!_req || !_req->process_buffer || !_req->buf0 || !_req->buf1) { 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; core_if = GET_CORE_IF(pcd); dctl.d32 = dwc_read_reg32(&core_if->dev_if->dev_global_regs->dctl); dctl.b.ifrmnum = 1; //dwc_write_reg32(&core_if->dev_if->dev_global_regs->dctl,dctl.d32); dcfg.d32 = dwc_read_reg32(&core_if->dev_if->dev_global_regs->dcfg); 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; } SPIN_LOCK_IRQSAVE(&ep->pcd->lock, flags); dwc_ep = &ep->dwc_ep; _req->status = -EINPROGRESS; dwc_ep->dma_addr0 = _req->dma0; dwc_ep->dma_addr1 = _req->dma1; dwc_ep->xfer_buff0 = _req->buf0; dwc_ep->xfer_buff1 = _req->buf1; ep->iso_req = _req; dwc_ep->data_per_frame = _req->data_per_frame; /* todo - pattern data support is to be implemented in the future */ dwc_ep->data_pattern_frame = _req->data_pattern_frame; dwc_ep->sync_frame = _req->sync_frame; dwc_ep->buf_proc_intrvl = _req->buf_proc_intrvl; SPIN_UNLOCK_IRQRESTORE(&pcd->lock, flags); dwc_ep->bInterval = 1 << (ep->desc->bInterval - 1); dwc_ep->proc_buf_num = 0; dwc_ep->pkt_per_frm = 0; frm_data = ep->dwc_ep.data_per_frame; while (frm_data > 0) { dwc_ep->pkt_per_frm++; frm_data -= ep->dwc_ep.maxpacket; } if (dwc_ep->is_in) dwc_ep->desc_cnt = dwc_ep->buf_proc_intrvl / dwc_ep->bInterval; else dwc_ep->desc_cnt = dwc_ep->buf_proc_intrvl * dwc_ep->pkt_per_frm / dwc_ep->bInterval; /** Allocate descriptors for double buffering */ dwc_ep->iso_desc_addr = ep_alloc_iso_desc_chain(&dwc_ep->iso_dma_desc_addr, dwc_ep->desc_cnt * 2); dsts.d32 = dwc_read_reg32(&(GET_CORE_IF(pcd)->dev_if->dev_global_regs->dsts)); if (dwc_ep->is_in == 0) {/** ISO OUT EP */ iso_out_sts_data_t sts = {.d32 = 0 }; dwc_otg_iso_dma_desc_t *dma_desc = dwc_ep->iso_desc_addr; dma_addr_t dma_ad; uint32_t data_per_desc; dwc_otg_dev_out_ep_regs_t *out_regs = core_if->dev_if->out_ep_regs[dwc_ep->num]; addr = &GET_CORE_IF(pcd)->dev_if->out_ep_regs[dwc_ep->num]->doepctl; dma_ad = (dma_addr_t) dwc_read_reg32(&(out_regs->doepdma)); /** Buffer 0 descriptors setup */ dma_ad = dwc_ep->dma_addr0; sts.b.bs = BS_HOST_READY; //0 sts.b.rxsts = 0; sts.b.l = 0; sts.b.sp = 0; sts.b.ioc = 0; sts.b.pid = 0; sts.b.framenum = 0; for (i = 0; i < dwc_ep->desc_cnt - dwc_ep->pkt_per_frm; i += dwc_ep->pkt_per_frm) { for (j = 0; j < dwc_ep->pkt_per_frm; ++j) { data_per_desc = ((j + 1) * dwc_ep->maxpacket > dwc_ep->data_per_frame) ? dwc_ep->data_per_frame - j * dwc_ep->maxpacket : dwc_ep->maxpacket; sts.b.rxbytes = data_per_desc; writel((uint32_t) dma_ad, &dma_desc->buf); writel(sts.d32, &dma_desc->status); dma_desc++; (uint32_t) dma_ad += data_per_desc; } } for (j = 0; j < dwc_ep->pkt_per_frm - 1; ++j) { data_per_desc = ((j + 1) * dwc_ep->maxpacket > dwc_ep->data_per_frame) ? dwc_ep->data_per_frame - j * dwc_ep->maxpacket : dwc_ep->maxpacket; sts.b.rxbytes = data_per_desc; writel((uint32_t) dma_ad, &dma_desc->buf); writel(sts.d32, &dma_desc->status); dma_desc++; (uint32_t) dma_ad += data_per_desc; } sts.b.ioc = 1; data_per_desc = ((j + 1) * dwc_ep->maxpacket > dwc_ep->data_per_frame) ? dwc_ep->data_per_frame - j * dwc_ep->maxpacket : dwc_ep->maxpacket; sts.b.rxbytes = data_per_desc; writel((uint32_t) dma_ad, &dma_desc->buf); writel(sts.d32, &dma_desc->status); dma_desc++; /** Buffer 1 descriptors setup */ sts.b.ioc = 0; dma_ad = dwc_ep->dma_addr1; for (i = 0; i < dwc_ep->desc_cnt - dwc_ep->pkt_per_frm; i += dwc_ep->pkt_per_frm) { for (j = 0; j < dwc_ep->pkt_per_frm; ++j) { data_per_desc = ((j + 1) * dwc_ep->maxpacket > dwc_ep->data_per_frame) ? dwc_ep->data_per_frame - j * dwc_ep->maxpacket : dwc_ep->maxpacket; sts.b.rxbytes = data_per_desc; writel((uint32_t) dma_ad, &dma_desc->buf); writel(sts.d32, &dma_desc->status); dma_desc++; (uint32_t) dma_ad += data_per_desc; } } for (j = 0; j < dwc_ep->pkt_per_frm - 1; ++j) { data_per_desc = ((j + 1) * dwc_ep->maxpacket > dwc_ep->data_per_frame) ? dwc_ep->data_per_frame - j * dwc_ep->maxpacket : dwc_ep->maxpacket; sts.b.rxbytes = data_per_desc; writel((uint32_t) dma_ad, &dma_desc->buf); writel(sts.d32, &dma_desc->status); dma_desc++; (uint32_t) dma_ad += data_per_desc; } sts.b.ioc = 1; sts.b.l = 1; data_per_desc = ((j + 1) * dwc_ep->maxpacket > dwc_ep->data_per_frame) ? dwc_ep->data_per_frame - j * dwc_ep->maxpacket : dwc_ep->maxpacket; sts.b.rxbytes = data_per_desc; writel((uint32_t) dma_ad, &dma_desc->buf); writel(sts.d32, &dma_desc->status); dwc_ep->next_frame = 0; /** Write dma_ad into DOEPDMA register */ dwc_write_reg32(&(out_regs->doepdma), (uint32_t) dwc_ep->iso_dma_desc_addr); } else {/** ISO IN EP */ iso_in_sts_data_t sts = {.d32 = 0 }; dwc_otg_iso_dma_desc_t *dma_desc = dwc_ep->iso_desc_addr; dma_addr_t dma_ad; dwc_otg_dev_in_ep_regs_t *in_regs = core_if->dev_if->in_ep_regs[dwc_ep->num]; unsigned int frmnumber; fifosize_data_t txfifosize, rxfifosize; txfifosize.d32 = dwc_read_reg32(&core_if->dev_if->in_ep_regs[dwc_ep->num]->dtxfsts); rxfifosize.d32 = dwc_read_reg32(&core_if->core_global_regs->grxfsiz); addr = &GET_CORE_IF(pcd)->dev_if->in_ep_regs[dwc_ep->num]->diepctl; dma_ad = dwc_ep->dma_addr0; dsts.d32 = dwc_read_reg32(&(GET_CORE_IF(pcd)->dev_if->dev_global_regs->dsts)); sts.b.bs = BS_HOST_READY; sts.b.txsts = 0; sts.b.sp = (dwc_ep->data_per_frame % dwc_ep->maxpacket) ? 1 : 0; sts.b.ioc = 0; sts.b.pid = dwc_ep->pkt_per_frm;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -