📄 dwc_otg_pcd_intr.c
字号:
/** * This interrupt indicates that the ISO OUT Packet was dropped due to * Rx FIFO full or Rx Status Queue Full. If this interrupt occurs * read all the data from the Rx FIFO. */int32_t dwc_otg_pcd_handle_isoc_out_packet_dropped_intr(dwc_otg_pcd_t *_pcd ){ gintmsk_data_t intr_mask = { .d32 = 0}; gintsts_data_t gintsts; DWC_PRINT("INTERRUPT Handler not implemented for %s\n", "ISOC Out Dropped"); intr_mask.b.isooutdrop = 1; dwc_modify_reg32( &GET_CORE_IF(_pcd)->core_global_regs->gintmsk, intr_mask.d32, 0 ); /* Clear interrupt */ gintsts.d32 = 0; gintsts.b.isooutdrop = 1; dwc_write_reg32 (&GET_CORE_IF(_pcd)->core_global_regs->gintsts, gintsts.d32); return 1;}/** * This interrupt indicates the end of the portion of the micro-frame * for periodic transactions. If there is a periodic transaction for * the next frame, load the packets into the EP periodic Tx FIFO. */int32_t dwc_otg_pcd_handle_end_periodic_frame_intr(dwc_otg_pcd_t *_pcd ){ gintmsk_data_t intr_mask = { .d32 = 0}; gintsts_data_t gintsts; DWC_PRINT("INTERRUPT Handler not implemented for %s\n", "EOP"); intr_mask.b.eopframe = 1; dwc_modify_reg32( &GET_CORE_IF(_pcd)->core_global_regs->gintmsk, intr_mask.d32, 0 ); /* Clear interrupt */ gintsts.d32 = 0; gintsts.b.eopframe = 1; dwc_write_reg32( &GET_CORE_IF(_pcd)->core_global_regs->gintsts, gintsts.d32); return 1;}/** * This interrupt indicates that EP of the packet on the top of the * non-periodic Tx FIFO does not match EP of the IN Token received. * * The "Device IN Token Queue" Registers are read to determine the * order the IN Tokens have been received. The non-periodic Tx FIFO * is flushed, so it can be reloaded in the order seen in the IN Token * Queue. */int32_t dwc_otg_pcd_handle_ep_mismatch_intr(dwc_otg_core_if_t *_core_if){ gintsts_data_t gintsts; DWC_DEBUGPL(DBG_PCDV, "%s(%p)\n", __func__, _core_if); /* Clear interrupt */ gintsts.d32 = 0; gintsts.b.epmismatch = 1; dwc_write_reg32 (&_core_if->core_global_regs->gintsts, gintsts.d32); return 1;}/** * This funcion stalls EP0. */static inline void ep0_do_stall( dwc_otg_pcd_t *_pcd, const int err_val ){ dwc_otg_pcd_ep_t *ep0 = &_pcd->ep0; struct usb_ctrlrequest *ctrl = &_pcd->setup_pkt->req; DWC_WARN("req %02x.%02x protocol STALL; err %d\n", ctrl->bRequestType, ctrl->bRequest, err_val); ep0->dwc_ep.is_in = 1; dwc_otg_ep_set_stall( _pcd->otg_dev->core_if, &ep0->dwc_ep ); _pcd->ep0.stopped = 1; _pcd->ep0state = EP0_IDLE; ep0_out_start( GET_CORE_IF(_pcd), _pcd );}/** * This functions delegates the setup command to the gadget driver. */static inline void do_gadget_setup( dwc_otg_pcd_t *_pcd, struct usb_ctrlrequest * _ctrl){ int ret = 0; if (_pcd->driver && _pcd->driver->setup) { SPIN_UNLOCK(&_pcd->lock); ret = _pcd->driver->setup(&_pcd->gadget, _ctrl); SPIN_LOCK(&_pcd->lock); if (ret < 0) { ep0_do_stall( _pcd, ret ); } /** @todo This is a g_file_storage gadget driver specific * workaround: a DELAYED_STATUS result from the fsg_setup * routine will result in the gadget queueing a EP0 IN status * phase for a two-stage control transfer. Exactly the same as * a SET_CONFIGURATION/SET_INTERFACE except that this is a class * specific request. Need a generic way to know when the gadget * driver will queue the status phase. Can we assume when we * call the gadget driver setup() function that it will always * queue and require the following flag? Need to look into * this. */ if (ret == 256 + 999) { _pcd->request_config = 1; } }}/** * This function starts the Zero-Length Packet for the IN status phase * of a 2 stage control transfer. */static inline void do_setup_in_status_phase( dwc_otg_pcd_t *_pcd){ dwc_otg_pcd_ep_t *ep0 = &_pcd->ep0; if (_pcd->ep0state == EP0_STALL) { return; } _pcd->ep0state = EP0_IN_STATUS_PHASE; /* Prepare for more SETUP Packets */ DWC_DEBUGPL(DBG_PCD, "EP0 IN ZLP\n"); ep0->dwc_ep.xfer_len = 0; ep0->dwc_ep.xfer_count = 0; ep0->dwc_ep.is_in = 1; ep0->dwc_ep.dma_addr = _pcd->setup_pkt_dma_handle; dwc_otg_ep0_start_transfer( GET_CORE_IF(_pcd), &ep0->dwc_ep ); /* Prepare for more SETUP Packets */ ep0_out_start( GET_CORE_IF(_pcd), _pcd );}/** * This function starts the Zero-Length Packet for the OUT status phase * of a 2 stage control transfer. */static inline void do_setup_out_status_phase( dwc_otg_pcd_t *_pcd){ dwc_otg_pcd_ep_t *ep0 = &_pcd->ep0; if (_pcd->ep0state == EP0_STALL) { DWC_DEBUGPL(DBG_PCD, "EP0 STALLED\n"); return; } _pcd->ep0state = EP0_OUT_STATUS_PHASE; DWC_DEBUGPL(DBG_PCD, "EP0 OUT ZLP\n"); ep0->dwc_ep.xfer_len = 0; ep0->dwc_ep.xfer_count = 0; ep0->dwc_ep.is_in = 0; ep0->dwc_ep.dma_addr = _pcd->setup_pkt_dma_handle; dwc_otg_ep0_start_transfer( GET_CORE_IF(_pcd), &ep0->dwc_ep ); /* Prepare for more SETUP Packets */ ep0_out_start( GET_CORE_IF(_pcd), _pcd );}/** * Clear the EP halt (STALL) and if pending requests start the * transfer. */static inline void pcd_clear_halt( dwc_otg_pcd_t *_pcd, dwc_otg_pcd_ep_t *_ep ){ if(_ep->dwc_ep.stall_clear_flag == 0) dwc_otg_ep_clear_stall( GET_CORE_IF(_pcd), &_ep->dwc_ep ); /* Reactive the EP */ dwc_otg_ep_activate( GET_CORE_IF(_pcd), &_ep->dwc_ep ); if (_ep->stopped) { _ep->stopped = 0; /* If there is a request in the EP queue start it */ /** @todo FIXME: this causes an EP mismatch in DMA mode. * epmismatch not yet implemented. */ /* * Above fixme is solved by implmenting a tasklet to call the * start_next_request(), outside of interrupt context at some * time after the current time, after a clear-halt setup packet. * Still need to implement ep mismatch in the future if a gadget * ever uses more than one endpoint at once */ if (GET_CORE_IF(_pcd)->dma_enable) { _ep->queue_sof = 1; tasklet_schedule (_pcd->start_xfer_tasklet); } else {#if 0 _ep->queue_sof = 1; DWC_ERROR("tasklet schedule\n"); tasklet_schedule (_pcd->start_xfer_tasklet); if (GET_CORE_IF(_pcd)->core_params->opt) { start_next_request( _ep ); }#endif } } /* Start Control Status Phase */ do_setup_in_status_phase( _pcd );}/** * This function is called when the SET_FEATURE TEST_MODE Setup packet * is sent from the host. The Device Control register is written with * the Test Mode bits set to the specified Test Mode. This is done as * a tasklet so that the "Status" phase of the control transfer * completes before transmitting the TEST packets. * * @todo This has not been tested since the tasklet struct was put * into the PCD struct! * */static void do_test_mode( unsigned long _data ){ dctl_data_t dctl; dwc_otg_pcd_t *pcd = (dwc_otg_pcd_t *)_data; dwc_otg_core_if_t *core_if = GET_CORE_IF(pcd); int test_mode = pcd->test_mode;// DWC_WARN("%s() has not been tested since being rewritten!\n", __func__); dctl.d32 = dwc_read_reg32(&core_if->dev_if->dev_global_regs->dctl); switch (test_mode) { case 1: // TEST_J dctl.b.tstctl = 1; break; case 2: // TEST_K dctl.b.tstctl = 2; break; case 3: // TEST_SE0_NAK dctl.b.tstctl = 3; break; case 4: // TEST_PACKET dctl.b.tstctl = 4; break; case 5: // TEST_FORCE_ENABLE dctl.b.tstctl = 5; break; } dwc_write_reg32(&core_if->dev_if->dev_global_regs->dctl, dctl.d32);}/** * This function process the GET_STATUS Setup Commands. */static inline void do_get_status( dwc_otg_pcd_t *_pcd ){ struct usb_ctrlrequest ctrl = _pcd->setup_pkt->req; dwc_otg_pcd_ep_t *ep; dwc_otg_pcd_ep_t *ep0 = &_pcd->ep0; uint16_t *status = _pcd->status_buf;#ifdef DEBUG_EP0 DWC_DEBUGPL(DBG_PCD, "GET_STATUS %02x.%02x v%04x i%04x l%04x\n", ctrl.bRequestType, ctrl.bRequest, ctrl.wValue, ctrl.wIndex, ctrl.wLength);#endif switch (ctrl.bRequestType & USB_RECIP_MASK) { case USB_RECIP_DEVICE: *status = 0x1; /* Self powered */ *status |= _pcd->remote_wakeup_enable << 1; break; case USB_RECIP_INTERFACE: *status = 0; break; case USB_RECIP_ENDPOINT: ep = get_ep_by_addr(_pcd, ctrl.wIndex); if ( ep == 0 || ctrl.wLength > 2) { ep0_do_stall(_pcd, -EOPNOTSUPP); return; } /** @todo check for EP stall */ *status = ep->stopped; break; } _pcd->ep0_pending = 1; ep0->dwc_ep.start_xfer_buff = (uint8_t *)status; ep0->dwc_ep.xfer_buff = (uint8_t *)status; ep0->dwc_ep.dma_addr = _pcd->status_buf_dma_handle; ep0->dwc_ep.xfer_len = 2; ep0->dwc_ep.xfer_count = 0; ep0->dwc_ep.total_len = ep0->dwc_ep.xfer_len; dwc_otg_ep0_start_transfer( GET_CORE_IF(_pcd), &ep0->dwc_ep );}/** * This function process the SET_FEATURE Setup Commands. */static inline void do_set_feature( dwc_otg_pcd_t *_pcd ){ dwc_otg_core_if_t *core_if = GET_CORE_IF(_pcd); dwc_otg_core_global_regs_t *global_regs = core_if->core_global_regs; struct usb_ctrlrequest ctrl = _pcd->setup_pkt->req; dwc_otg_pcd_ep_t *ep = 0; int32_t otg_cap_param = core_if->core_params->otg_cap; gotgctl_data_t gotgctl = { .d32 = 0 }; DWC_DEBUGPL(DBG_PCD, "SET_FEATURE:%02x.%02x v%04x i%04x l%04x\n", ctrl.bRequestType, ctrl.bRequest, ctrl.wValue, ctrl.wIndex, ctrl.wLength); DWC_DEBUGPL(DBG_PCD,"otg_cap=%d\n", otg_cap_param); switch (ctrl.bRequestType & USB_RECIP_MASK) { case USB_RECIP_DEVICE: switch (ctrl.wValue) { case USB_DEVICE_REMOTE_WAKEUP: _pcd->remote_wakeup_enable = 1; break; case USB_DEVICE_TEST_MODE: /* Setup the Test Mode tasklet to do the Test * Packet generation after the SETUP Status * phase has completed. */ /** @todo This has not been tested since the * tasklet struct was put into the PCD * struct! */ _pcd->test_mode_tasklet.next = 0; _pcd->test_mode_tasklet.state = 0; atomic_set( &_pcd->test_mode_tasklet.count, 0); _pcd->test_mode_tasklet.func = do_test_mode; _pcd->test_mode_tasklet.data = (unsigned long)_pcd; _pcd->test_mode = ctrl.wIndex >> 8; tasklet_schedule(&_pcd->test_mode_tasklet); break; case USB_DEVICE_B_HNP_ENABLE: DWC_DEBUGPL(DBG_PCDV, "SET_FEATURE: USB_DEVICE_B_HNP_ENABLE\n"); /* dev may initiate HNP */ if (otg_cap_param == DWC_OTG_CAP_PARAM_HNP_SRP_CAPABLE) { _pcd->b_hnp_enable = 1; dwc_otg_pcd_update_otg( _pcd, 0 ); DWC_DEBUGPL(DBG_PCD, "Request B HNP\n"); /**@todo Is the gotgctl.devhnpen cleared * by a USB Reset? */ gotgctl.b.devhnpen = 1; gotgctl.b.hnpreq = 1; dwc_write_reg32( &global_regs->gotgctl, gotgctl.d32 ); } else { ep0_do_stall( _pcd, -EOPNOTSUPP); } break; case USB_DEVICE_A_HNP_SUPPORT: /* RH port supports HNP */ DWC_DEBUGPL(DBG_PCDV, "SET_FEATURE: USB_DEVICE_A_HNP_SUPPORT\n"); if (otg_cap_param == DWC_OTG_CAP_PARAM_HNP_SRP_CAPABLE) { _pcd->a_hnp_support = 1; dwc_otg_pcd_update_otg( _pcd, 0 ); } else { ep0_do_stall( _pcd, -EOPNOTSUPP); } break; case USB_DEVICE_A_ALT_HNP_SUPPORT: /* other RH port does */ DWC_DEBUGPL(DBG_PCDV, "SET_FEATURE: USB_DEVICE_A_ALT_HNP_SUPPORT\n"); if (otg_cap_param == DWC_OTG_CAP_PARAM_HNP_SRP_CAPABLE) { _pcd->a_alt_hnp_support = 1; dwc_otg_pcd_update_otg( _pcd, 0 ); } else { ep0_do_stall( _pcd, -EOPNOTSUPP); } break; } do_setup_in_status_phase( _pcd ); break; case USB_RECIP_INTERFACE: do_gadget_setup(_pcd, &ctrl ); break; case USB_RECIP_ENDPOINT: if (ctrl.wValue == USB_ENDPOINT_HALT) { ep = get_ep_by_addr(_pcd, ctrl.wIndex); if (ep == 0) { ep0_do_stall(_pcd, -EOPNOTSUPP); return; } ep->stopped = 1; dwc_otg_ep_set_stall( core_if, &ep->dwc_ep ); } do_setup_in_status_phase( _pcd ); break; }}/** * This function process the CLEAR_FEATURE Setup Commands. */static inline void do_clear_feature( dwc_otg_pcd_t *_pcd ){ struct usb_ctrlrequest ctrl = _pcd->setup_pkt->req; dwc_otg_pcd_ep_t *ep = 0; DWC_DEBUGPL(DBG_PCD, "CLEAR_FEATURE:%02x.%02x v%04x i%04x l%04x\n", ctrl.bRequestType, ctrl.bRequest, ctrl.wValue, ctrl.wIndex, ctrl.wLength); switch (ctrl.bRequestType & USB_RECIP_MASK) { case USB_RECIP_DEVICE: switch (ctrl.wValue) { case USB_DEVICE_REMOTE_WAKEUP: _pcd->remote_wakeup_enable = 0; break; case USB_DEVICE_TEST_MODE: /** @todo Add CLEAR_FEATURE for TEST modes. */ break; } do_setup_in_status_phase( _pcd ); break; case USB_RECIP_ENDPOINT: ep = get_ep_by_addr(_pcd, ctrl.wIndex); if (ep == 0) { ep0_do_stall(_pcd, -EOPNOTSUPP); return; } pcd_clear_halt(_pcd, ep ); break; }}/** * This function process the SET_ADDRESS Setup Commands. */static inline void do_set_address( dwc_otg_pcd_t *_pcd ){ dwc_otg_dev_if_t *dev_if = GET_CORE_IF(_pcd)->dev_if; struct usb_ctrlrequest ctrl = _pcd->setup_pkt->req; if (ctrl.bRequestType == USB_RECIP_DEVICE) { dcfg_data_t dcfg = {.d32=0};#ifdef DEBUG_EP0// DWC_DEBUGPL(DBG_PCDV, "SET_ADDRESS:%d\n", ctrl.wValue);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -