📄 dwc_otg_pcd.c
字号:
if (_req->flags & USB_REQ_ISO_ASAP) { frmnumber = dsts.b.soffn + 1; if (dwc_ep->bInterval != 1) { frmnumber = frmnumber + (dwc_ep->bInterval - 1 - frmnumber % dwc_ep->bInterval); } } else { frmnumber = _req->start_frame; } sts.b.framenum = frmnumber; sts.b.txbytes = dwc_ep->data_per_frame; sts.b.l = 0; /** Buffer 0 descriptors setup */ for (i = 0; i < dwc_ep->desc_cnt - 1; i++) { writel((uint32_t) dma_ad, &dma_desc->buf); writel(sts.d32, &dma_desc->status); dma_desc++; (uint32_t) dma_ad += dwc_ep->data_per_frame; sts.b.framenum += dwc_ep->bInterval; } sts.b.ioc = 1; 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) { writel((uint32_t) dma_ad, &dma_desc->buf); writel(sts.d32, &dma_desc->status); dma_desc++; (uint32_t) dma_ad += dwc_ep->data_per_frame; sts.b.framenum += dwc_ep->bInterval; sts.b.ioc = 0; } sts.b.ioc = 1; sts.b.l = 1; writel((uint32_t) dma_ad, &dma_desc->buf); writel(sts.d32, &dma_desc->status); dwc_ep->next_frame = sts.b.framenum + dwc_ep->bInterval; /** Write dma_ad into diepdma register */ dwc_write_reg32(&(in_regs->diepdma), (uint32_t) dwc_ep->iso_dma_desc_addr); } /** Enable endpoint, clear nak */ depctl.d32 = 0; depctl.b.epena = 1; depctl.b.usbactep = 1; depctl.b.cnak = 1; dwc_modify_reg32(addr, depctl.d32, depctl.d32); depctl.d32 = dwc_read_reg32(addr); return 0;}/** * This function stops ISO EP Periodic Data Transfer. */static int dwc_otg_pcd_iso_ep_stop(struct usb_ep *_ep, struct usb_iso_request *_iso_req){ dwc_otg_pcd_ep_t *ep; dwc_otg_pcd_t *pcd; dwc_ep_t *dwc_ep; unsigned long flags; depctl_data_t depctl = {.d32 = 0 }; volatile uint32_t *addr; 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; 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_ep = &ep->dwc_ep; SPIN_LOCK_IRQSAVE(&pcd->lock, flags); if (ep->iso_req != _iso_req) { return -EINVAL; } _iso_req->status = -ECONNRESET; SPIN_UNLOCK_IRQRESTORE(&pcd->lock, flags); if (ep->dwc_ep.is_in == 1) { addr = &GET_CORE_IF(pcd)->dev_if->in_ep_regs[dwc_ep->num]->diepctl; } else { addr = &GET_CORE_IF(pcd)->dev_if->out_ep_regs[dwc_ep->num]->doepctl; } depctl.d32 = dwc_read_reg32(addr); dwc_write_reg32(addr, depctl.d32); ep_free_iso_desc_chain(ep->dwc_ep.iso_desc_addr, ep->dwc_ep.iso_dma_desc_addr, ep->dwc_ep.desc_cnt * 2); /* reset varibales */ dwc_ep->dma_addr0 = 0; dwc_ep->dma_addr1 = 0; dwc_ep->xfer_buff0 = 0; dwc_ep->xfer_buff1 = 0; dwc_ep->data_per_frame = 0; dwc_ep->data_pattern_frame = 0; dwc_ep->sync_frame = 0; dwc_ep->buf_proc_intrvl = 0; dwc_ep->bInterval = 0; dwc_ep->proc_buf_num = 0; dwc_ep->pkt_per_frm = 0; dwc_ep->pkt_per_frm = 0; dwc_ep->desc_cnt = 0; dwc_ep->iso_desc_addr = 0; dwc_ep->iso_dma_desc_addr = 0; return 0;}static struct usb_iso_request *dwc_otg_pcd_alloc_iso_request(struct usb_ep *_ep, int packets,#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,20) int gfp_flags#else gfp_t gfp_flags#endif ){ struct usb_iso_request *pReq = NULL; uint32_t req_size; req_size = sizeof(struct usb_iso_request); req_size += (2 * packets * (sizeof(struct usb_gadget_iso_packet_descriptor))); pReq = kmalloc(req_size, gfp_flags); pReq->iso_packet_desc0 = (void *) (pReq + 1); pReq->iso_packet_desc1 = pReq->iso_packet_desc0 + packets; return pReq;}static void dwc_otg_pcd_free_iso_request(struct usb_ep *_ep, struct usb_iso_request *req){ kfree(req);}static struct usb_isoc_ep_ops dwc_otg_pcd_ep_ops = { .ep_ops = { .enable = dwc_otg_pcd_ep_enable, .disable = dwc_otg_pcd_ep_disable, .alloc_request = dwc_otg_pcd_alloc_request, .free_request = dwc_otg_pcd_free_request, .alloc_buffer = dwc_otg_pcd_alloc_buffer, .free_buffer = dwc_otg_pcd_free_buffer, .queue = dwc_otg_pcd_ep_queue, .dequeue = dwc_otg_pcd_ep_dequeue, .set_halt = dwc_otg_pcd_ep_set_halt, .fifo_status = 0, .fifo_flush = 0, }, .iso_ep_start = dwc_otg_pcd_iso_ep_start, .iso_ep_stop = dwc_otg_pcd_iso_ep_stop, .alloc_iso_request = dwc_otg_pcd_alloc_iso_request, .free_iso_request = dwc_otg_pcd_free_iso_request,};#elsestatic struct usb_ep_ops dwc_otg_pcd_ep_ops = { .enable = dwc_otg_pcd_ep_enable, .disable = dwc_otg_pcd_ep_disable, .alloc_request = dwc_otg_pcd_alloc_request, .free_request = dwc_otg_pcd_free_request, .alloc_buffer = dwc_otg_pcd_alloc_buffer, .free_buffer = dwc_otg_pcd_free_buffer, .queue = dwc_otg_pcd_ep_queue, .dequeue = dwc_otg_pcd_ep_dequeue, .set_halt = dwc_otg_pcd_ep_set_halt, .fifo_status = 0, .fifo_flush = 0,};#endif /* _EN_ISOC_ *//* Gadget Operations *//** * The following gadget operations will be implemented in the DWC_otg * PCD. Functions in the API that are not described below are not * implemented. * * The Gadget API provides wrapper functions for each of the function * pointers defined in usb_gadget_ops. The Gadget Driver calls the * wrapper function, which then calls the underlying PCD function. The * following sections are named according to the wrapper functions * (except for ioctl, which doesn't have a wrapper function). Within * each section, the corresponding DWC_otg PCD function name is * specified. * *//** *Gets the USB Frame number of the last SOF. */static int dwc_otg_pcd_get_frame(struct usb_gadget *_gadget){ dwc_otg_pcd_t *pcd; DWC_DEBUGPL(DBG_PCDV, "%s(%p)\n", __func__, _gadget); if (_gadget == 0) { return -ENODEV; } else { pcd = container_of(_gadget, dwc_otg_pcd_t, gadget); dwc_otg_get_frame_number(GET_CORE_IF(pcd)); } return 0;}void dwc_otg_pcd_initiate_srp(dwc_otg_pcd_t * _pcd){ uint32_t *addr = (uint32_t *) & (GET_CORE_IF(_pcd)->core_global_regs->gotgctl); gotgctl_data_t mem; gotgctl_data_t val; val.d32 = dwc_read_reg32(addr); if (val.b.sesreq) { DWC_ERROR("Session Request Already active!\n"); return; } DWC_NOTICE("Session Request Initated\n"); mem.d32 = dwc_read_reg32(addr); mem.b.sesreq = 1; dwc_write_reg32(addr, mem.d32); /* Start the SRP timer */ dwc_otg_pcd_start_srp_timer(_pcd); return;}void dwc_otg_pcd_remote_wakeup(dwc_otg_pcd_t * _pcd, int set){ dctl_data_t dctl = {.d32 = 0 }; volatile uint32_t *addr = &(GET_CORE_IF(_pcd)->dev_if->dev_global_regs->dctl); if (dwc_otg_is_device_mode(GET_CORE_IF(_pcd))) { if (_pcd->remote_wakeup_enable) { if (set) { dctl.b.rmtwkupsig = 1; dwc_modify_reg32(addr, 0, dctl.d32); DWC_DEBUGPL(DBG_PCD, "Set Remote Wakeup\n"); mdelay(1); dwc_modify_reg32(addr, dctl.d32, 0); DWC_DEBUGPL(DBG_PCD, "Clear Remote Wakeup\n"); } else { } } else { DWC_DEBUGPL(DBG_PCD, "Remote Wakeup is disabled\n"); } } return;}/** * Initiates Session Request Protocol (SRP) to wakeup the host if no * session is in progress. If a session is already in progress, but * the device is suspended, remote wakeup signaling is started. * */static int dwc_otg_pcd_wakeup(struct usb_gadget *_gadget){ unsigned long flags; dwc_otg_pcd_t *pcd; dsts_data_t dsts; gotgctl_data_t gotgctl; DWC_DEBUGPL(DBG_PCDV, "%s(%p)\n", __func__, _gadget); if (_gadget == 0) { return -ENODEV; } else { pcd = container_of(_gadget, dwc_otg_pcd_t, gadget); } SPIN_LOCK_IRQSAVE(&pcd->lock, flags); /* * This function starts the Protocol if no session is in progress. If * a session is already in progress, but the device is suspended, * remote wakeup signaling is started. */ /* Check if valid session */ gotgctl.d32 = dwc_read_reg32(&(GET_CORE_IF(pcd)->core_global_regs->gotgctl)); if (gotgctl.b.bsesvld) { /* Check if suspend state */ dsts.d32 = dwc_read_reg32(&(GET_CORE_IF(pcd)->dev_if->dev_global_regs->dsts)); if (dsts.b.suspsts) { dwc_otg_pcd_remote_wakeup(pcd, 1); } } else { dwc_otg_pcd_initiate_srp(pcd); } SPIN_UNLOCK_IRQRESTORE(&pcd->lock, flags); return 0;}static const struct usb_gadget_ops dwc_otg_pcd_ops = { .get_frame = dwc_otg_pcd_get_frame, .wakeup = dwc_otg_pcd_wakeup, // current versions must always be self-powered};/** * This function updates the otg values in the gadget structure. */void dwc_otg_pcd_update_otg(dwc_otg_pcd_t * _pcd, const unsigned _reset){ if (!_pcd->gadget.is_otg) return; if (_reset) { _pcd->b_hnp_enable = 0; _pcd->a_hnp_support = 0; _pcd->a_alt_hnp_support = 0; } _pcd->gadget.b_hnp_enable = _pcd->b_hnp_enable; _pcd->gadget.a_hnp_support = _pcd->a_hnp_support; _pcd->gadget.a_alt_hnp_support = _pcd->a_alt_hnp_support;}/** * This function is the top level PCD interrupt handler. */static irqreturn_t dwc_otg_pcd_irq(int _irq, void *_dev#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,19) , struct pt_regs *_r#endif ){ dwc_otg_pcd_t *pcd = _dev; int32_t retval = IRQ_NONE; retval = dwc_otg_pcd_handle_intr(pcd); return IRQ_RETVAL(retval);}/** * PCD Callback function for initializing the PCD when switching to * device mode. * * @param _p void pointer to the <code>dwc_otg_pcd_t</code> */static int32_t dwc_otg_pcd_start_cb(void *_p){ dwc_otg_pcd_t *pcd = (dwc_otg_pcd_t *) _p; /* * Initialized the Core for Device mode. */ if (dwc_otg_is_device_mode(GET_CORE_IF(pcd))) { dwc_otg_core_dev_init(GET_CORE_IF(pcd)); } return 1;}/** * PCD Callback function for stopping the PCD when switching to Host * mode. * * @param _p void pointer to the <code>dwc_otg_pcd_t</code> */static int32_t dwc_otg_pcd_stop_cb(void *_p){ dwc_otg_pcd_t *pcd = (dwc_otg_pcd_t *) _p; extern void dwc_otg_pcd_stop(dwc_otg_pcd_t * _pcd); dwc_otg_pcd_stop(pcd); return 1;}/** * PCD Callback function for notifying the PCD when resuming from * suspend. * * @param _p void pointer to the <code>dwc_otg_pcd_t</code> */static int32_t dwc_otg_pcd_suspend_cb(void *_p){ dwc_otg_pcd_t *pcd = (dwc_otg_pcd_t *) _p; if (pcd->driver && pcd->driver->resume) { SPIN_UNLOCK(&pcd->lock); pcd->driver->suspend(&pcd->gadget); SPIN_LOCK(&pcd->lock); } return 1;}/** * PCD Callback function for notifying the PCD when resuming from * suspend. * * @param _p void pointer to the <code>dwc_otg_pcd_t</code> */static int32_t dwc_otg_pcd_resume_cb(void *_p){ dwc_otg_pcd_t *pcd = (dwc_otg_pcd_t *) _p; if (pcd->driver && pcd->driver->resume) { SPIN_UNLOCK(&pcd->lock); pcd->driver->resume(&pcd->gadget); SPIN_LOCK(&pcd->lock); } /* Stop the SRP timeout timer. */ if ((GET_CORE_IF(pcd)->core_params->phy_type != DWC_PHY_TYPE_PARAM_FS) || (!GET_CORE_IF(pcd)->core_params->i2c_enable)) { if (GET_CORE_IF(pcd)->srp_timer_started) { GET_CORE_IF(pcd)->srp_timer_started = 0; del_timer(&pcd->srp_timer); } } return 1;}/** * PCD Callback structure for handling mode switching. */static dwc_otg_cil_callbacks_t pcd_callbacks = { .start = dwc_otg_pcd_start_cb, .stop = dwc_otg_pcd_stop_cb, .suspend = dwc_otg_pcd_suspend_cb, .resume_wakeup = dwc_otg_pcd_resume_cb, .p = 0, /* Set at registration */};/** * This function is called when the SRP timer expires. The SRP should * complete within 6 seconds. */static void srp_timeout(unsigned long _ptr){ gotgctl_data_t gotgctl; dwc_otg_core_if_t *core_if = (dwc_otg_core_if_t *) _ptr; volatile uint32_t *addr = &core_if->core_global_regs->gotgctl; gotgctl.d32 = dwc_read_reg32(addr); core_if->srp_timer_started = 0; if ((core_if->core_params->phy_type == DWC_PHY_TYPE_PARAM_FS) && (core_if->core_params->i2c_enable)) { DWC_PRINT("SRP Timeout\n"); if ((core_if->srp_success) && (gotgctl.b.bsesvld)) { if (core_if->pcd_cb && core_if->pcd_cb->resume_wakeup) { core_if->pcd_cb->resume_wakeup(core_if->pcd_cb->p); } /* Clear Session Request */ gotgctl.d32 = 0; gotgctl.b.sesreq = 1; dwc_modify_reg32(&core_if->core_global_regs->gotgctl, gotgctl.d32, 0); core_if->srp_success = 0; } else { DWC_ERROR("Device not connected/responding\n"); gotgctl.b.sesreq = 0; dwc_write_reg32(addr, gotgctl.d32); } } else if (gotgctl.b.sesreq) { DWC_PRINT("SRP Timeout\n"); DWC_ERROR("Device not connected/responding\n"); gotgctl.b.sesreq = 0; dwc_write_reg32(addr, gotgctl.d32); } else { DWC_PRINT(" SRP GOTGCTL=%0x\n", gotgctl.d32); }}/** * Start the SRP timer to detect when the SRP does not complete within * 6 seconds. * * @param _pcd the pcd structure. */void dwc_otg_pcd_start_srp_timer(dwc_otg_pcd_t * _pcd){ struct timer_list *srp_timer = &_pcd->srp_timer; GET_CORE_IF(_pcd)->srp_timer_started = 1; init_timer(srp_timer); srp_timer->function = srp_timeout; srp_timer->data = (unsigned long) GET_CORE_IF(_pcd);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -