📄 dwc_otg_hcd.c
字号:
struct usb_host_endpoint *_ep, struct urb *_urb, gfp_t _mem_flags){ int retval = 0; dwc_otg_hcd_t *dwc_otg_hcd = hcd_to_dwc_otg_hcd(_hcd); dwc_otg_qtd_t *qtd;#ifdef DEBUG if (CHK_DEBUG_LEVEL(DBG_HCDV | DBG_HCD_URB)) { dump_urb_info(_urb, "dwc_otg_hcd_urb_enqueue"); }#endif dbg_otg("\n\n\n\n#############%s()\n\n\n", __FUNCTION__); if (!dwc_otg_hcd->flags.b.port_connect_status) { /* No longer connected. */ return -ENODEV; } qtd = dwc_otg_hcd_qtd_create(_urb); if (qtd == NULL) { DWC_ERROR("DWC OTG HCD URB Enqueue failed creating QTD\n"); return -ENOMEM; } retval = dwc_otg_hcd_qtd_add(qtd, dwc_otg_hcd); if (retval < 0) { DWC_ERROR("DWC OTG HCD URB Enqueue failed adding QTD. " "Error status %d\n", retval); dwc_otg_hcd_qtd_free(qtd); } return retval;}/** Aborts/cancels a USB transfer request. Always returns 0 to indicate * success. */int dwc_otg_hcd_urb_dequeue(struct usb_hcd *_hcd, struct urb *_urb){ unsigned long flags; dwc_otg_hcd_t *dwc_otg_hcd; dwc_otg_qtd_t *urb_qtd; dwc_otg_qh_t *qh; struct usb_host_endpoint *_ep = dwc_urb_to_endpoint(_urb); DWC_DEBUGPL(DBG_HCD, "DWC OTG HCD URB Dequeue\n"); dbg_otg("DWC OTG HCD URB Dequeue\n"); local_irq_save(flags); dwc_otg_hcd = hcd_to_dwc_otg_hcd(_hcd); urb_qtd = (dwc_otg_qtd_t *) _urb->hcpriv; qh = (dwc_otg_qh_t *) _ep->hcpriv;#ifdef DEBUG if (CHK_DEBUG_LEVEL(DBG_HCDV | DBG_HCD_URB)) { dump_urb_info(_urb, "dwc_otg_hcd_urb_dequeue"); if (urb_qtd == qh->qtd_in_process) { dump_channel_info(dwc_otg_hcd, qh); } }#endif if (urb_qtd == qh->qtd_in_process) { /* The QTD is in process (it has been assigned to a channel). */ if (dwc_otg_hcd->flags.b.port_connect_status) { /* * If still connected (i.e. in host mode), halt the * channel so it can be used for other transfers. If * no longer connected, the host registers can't be * written to halt the channel since the core is in * device mode. */ dwc_otg_hc_halt(dwc_otg_hcd->core_if, qh->channel, DWC_OTG_HC_XFER_URB_DEQUEUE); } } /* * Free the QTD and clean up the associated QH. Leave the QH in the * schedule if it has any remaining QTDs. */ dwc_otg_hcd_qtd_remove_and_free(urb_qtd); if (urb_qtd == qh->qtd_in_process) { dwc_otg_hcd_qh_deactivate(dwc_otg_hcd, qh, 0); qh->channel = NULL; qh->qtd_in_process = NULL; } else if (list_empty(&qh->qtd_list)) { dwc_otg_hcd_qh_remove(dwc_otg_hcd, qh); } local_irq_restore(flags); _urb->hcpriv = NULL; /* Higher layer software sets URB status. */ usb_hcd_giveback_urb(_hcd, _urb); if (CHK_DEBUG_LEVEL(DBG_HCDV | DBG_HCD_URB)) { DWC_PRINT("Called usb_hcd_giveback_urb()\n"); DWC_PRINT(" urb->status = %d\n", _urb->status); } return 0;}/** Frees resources in the DWC_otg controller related to a given endpoint. Also * clears state in the HCD related to the endpoint. Any URBs for the endpoint * must already be dequeued. */void dwc_otg_hcd_endpoint_disable(struct usb_hcd *_hcd, struct usb_host_endpoint *_ep){ dwc_otg_hcd_t *dwc_otg_hcd = hcd_to_dwc_otg_hcd(_hcd); dwc_otg_qh_t *qh;#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,20) unsigned long flags; int retry = 0;#endif DWC_DEBUGPL(DBG_HCD, "DWC OTG HCD EP DISABLE: _bEndpointAddress=0x%02x, " "endpoint=%d\n", _ep->desc.bEndpointAddress, dwc_ep_addr_to_endpoint(_ep->desc.bEndpointAddress));#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,20) rescan: local_irq_save(flags); qh = (dwc_otg_qh_t *) (_ep->hcpriv); if (!qh) goto done; /** Check that the QTD list is really empty */ if (!list_empty(&qh->qtd_list)) { if (retry++ < 250) { local_irq_restore(flags); schedule_timeout_uninterruptible(1); goto rescan; } DWC_WARN("DWC OTG HCD EP DISABLE:" " QTD List for this endpoint is not empty\n"); } dwc_otg_hcd_qh_remove_and_free(dwc_otg_hcd, qh); _ep->hcpriv = NULL; done: local_irq_restore(flags);#else // LINUX_VERSION_CODE qh = (dwc_otg_qh_t *) (_ep->hcpriv); if (qh != NULL) {#ifdef DEBUG /** Check that the QTD list is really empty */ if (!list_empty(&qh->qtd_list)) { DWC_WARN("DWC OTG HCD EP DISABLE:" " QTD List for this endpoint is not empty\n"); }#endif dwc_otg_hcd_qh_remove_and_free(dwc_otg_hcd, qh); _ep->hcpriv = NULL; }#endif // LINUX_VERSION_CODE return;}/** Handles host mode interrupts for the DWC_otg controller. Returns IRQ_NONE if * there was no interrupt to handle. Returns IRQ_HANDLED if there was a valid * interrupt. * * This function is called by the USB core when an interrupt occurs */irqreturn_t dwc_otg_hcd_irq(struct usb_hcd * _hcd){ dwc_otg_hcd_t *dwc_otg_hcd = hcd_to_dwc_otg_hcd(_hcd); return IRQ_RETVAL(dwc_otg_hcd_handle_intr(dwc_otg_hcd));}/** Creates Status Change bitmap for the root hub and root port. The bitmap is * returned in buf. Bit 0 is the status change indicator for the root hub. Bit 1 * is the status change indicator for the single root port. Returns 1 if either * change indicator is 1, otherwise returns 0. */int dwc_otg_hcd_hub_status_data(struct usb_hcd *_hcd, char *_buf){ dwc_otg_hcd_t *dwc_otg_hcd = hcd_to_dwc_otg_hcd(_hcd); _buf[0] = 0; _buf[0] |= (dwc_otg_hcd->flags.b.port_connect_status_change || dwc_otg_hcd->flags.b.port_reset_change || dwc_otg_hcd->flags.b.port_enable_change || dwc_otg_hcd->flags.b.port_suspend_change || dwc_otg_hcd->flags.b.port_over_current_change) << 1;#ifdef DEBUG if (_buf[0]) { DWC_DEBUGPL(DBG_HCD, "DWC OTG HCD HUB STATUS DATA:" " Root port status changed\n"); DWC_DEBUGPL(DBG_HCDV, " port_connect_status_change: %d\n", dwc_otg_hcd->flags.b.port_connect_status_change); DWC_DEBUGPL(DBG_HCDV, " port_reset_change: %d\n", dwc_otg_hcd->flags.b.port_reset_change); DWC_DEBUGPL(DBG_HCDV, " port_enable_change: %d\n", dwc_otg_hcd->flags.b.port_enable_change); DWC_DEBUGPL(DBG_HCDV, " port_suspend_change: %d\n", dwc_otg_hcd->flags.b.port_suspend_change); DWC_DEBUGPL(DBG_HCDV, " port_over_current_change: %d\n", dwc_otg_hcd->flags.b.port_over_current_change); }#endif return (_buf[0] != 0);}#ifdef DWC_HS_ELECT_TST/* * Quick and dirty hack to implement the HS Electrical Test * SINGLE_STEP_GET_DEVICE_DESCRIPTOR feature. * * This code was copied from our userspace app "hset". It sends a * Get Device Descriptor control sequence in two parts, first the * Setup packet by itself, followed some time later by the In and * Ack packets. Rather than trying to figure out how to add this * functionality to the normal driver code, we just hijack the * hardware, using these two function to drive the hardware * directly. */dwc_otg_core_global_regs_t *global_regs;dwc_otg_host_global_regs_t *hc_global_regs;dwc_otg_hc_regs_t *hc_regs;uint32_t *data_fifo;static void do_setup(void){ gintsts_data_t gintsts; hctsiz_data_t hctsiz; hcchar_data_t hcchar; haint_data_t haint; hcint_data_t hcint; dbg_otg("%s\n", __FUNCTION__); /* Enable HAINTs */ dwc_write_reg32(&hc_global_regs->haintmsk, 0x0001); /* Enable HCINTs */ dwc_write_reg32(&hc_regs->hcintmsk, 0x04a3); /* Read GINTSTS */ gintsts.d32 = dwc_read_reg32(&global_regs->gintsts); //fprintf(stderr, "GINTSTS: %08x\n", gintsts.d32); /* Read HAINT */ haint.d32 = dwc_read_reg32(&hc_global_regs->haint); //fprintf(stderr, "HAINT: %08x\n", haint.d32); /* Read HCINT */ hcint.d32 = dwc_read_reg32(&hc_regs->hcint); //fprintf(stderr, "HCINT: %08x\n", hcint.d32); /* Read HCCHAR */ hcchar.d32 = dwc_read_reg32(&hc_regs->hcchar); //fprintf(stderr, "HCCHAR: %08x\n", hcchar.d32); /* Clear HCINT */ dwc_write_reg32(&hc_regs->hcint, hcint.d32); /* Clear HAINT */ dwc_write_reg32(&hc_global_regs->haint, haint.d32); /* Clear GINTSTS */ dwc_write_reg32(&global_regs->gintsts, gintsts.d32); /* Read GINTSTS */ gintsts.d32 = dwc_read_reg32(&global_regs->gintsts); //fprintf(stderr, "GINTSTS: %08x\n", gintsts.d32); /* * Send Setup packet (Get Device Descriptor) */ /* Make sure channel is disabled */ hcchar.d32 = dwc_read_reg32(&hc_regs->hcchar); if (hcchar.b.chen) { //fprintf(stderr, "Channel already enabled 1, HCCHAR = %08x\n", hcchar.d32); hcchar.b.chdis = 1;// hcchar.b.chen = 1; dwc_write_reg32(&hc_regs->hcchar, hcchar.d32); //sleep(1); mdelay(1000); /* Read GINTSTS */ gintsts.d32 = dwc_read_reg32(&global_regs->gintsts); //fprintf(stderr, "GINTSTS: %08x\n", gintsts.d32); /* Read HAINT */ haint.d32 = dwc_read_reg32(&hc_global_regs->haint); //fprintf(stderr, "HAINT: %08x\n", haint.d32); /* Read HCINT */ hcint.d32 = dwc_read_reg32(&hc_regs->hcint); //fprintf(stderr, "HCINT: %08x\n", hcint.d32); /* Read HCCHAR */ hcchar.d32 = dwc_read_reg32(&hc_regs->hcchar); //fprintf(stderr, "HCCHAR: %08x\n", hcchar.d32); /* Clear HCINT */ dwc_write_reg32(&hc_regs->hcint, hcint.d32); /* Clear HAINT */ dwc_write_reg32(&hc_global_regs->haint, haint.d32); /* Clear GINTSTS */ dwc_write_reg32(&global_regs->gintsts, gintsts.d32); hcchar.d32 = dwc_read_reg32(&hc_regs->hcchar); //if (hcchar.b.chen) { // fprintf(stderr, "** Channel _still_ enabled 1, HCCHAR = %08x **\n", hcchar.d32); //} } /* Set HCTSIZ */ hctsiz.d32 = 0; hctsiz.b.xfersize = 8; hctsiz.b.pktcnt = 1; hctsiz.b.pid = DWC_OTG_HC_PID_SETUP; dwc_write_reg32(&hc_regs->hctsiz, hctsiz.d32); /* Set HCCHAR */ hcchar.d32 = dwc_read_reg32(&hc_regs->hcchar); hcchar.b.eptype = DWC_OTG_EP_TYPE_CONTROL; hcchar.b.epdir = 0; hcchar.b.epnum = 0; hcchar.b.mps = 8; hcchar.b.chen = 1; dwc_write_reg32(&hc_regs->hcchar, hcchar.d32); /* Fill FIFO with Setup data for Get Device Descriptor */ data_fifo = (uint32_t *) ((char *) global_regs + 0x1000); dwc_write_reg32(data_fifo++, 0x01000680); dwc_write_reg32(data_fifo++, 0x00080000); gintsts.d32 = dwc_read_reg32(&global_regs->gintsts); //fprintf(stderr, "Waiting for HCINTR intr 1, GINTSTS = %08x\n", gintsts.d32); /* Wait for host channel interrupt */ do { gintsts.d32 = dwc_read_reg32(&global_regs->gintsts); } while (gintsts.b.hcintr == 0); //fprintf(stderr, "Got HCINTR intr 1, GINTSTS = %08x\n", gintsts.d32); /* Disable HCINTs */ dwc_write_reg32(&hc_regs->hcintmsk, 0x0000); /* Disable HAINTs */ dwc_write_reg32(&hc_global_regs->haintmsk, 0x0000); /* Read HAINT */ haint.d32 = dwc_read_reg32(&hc_global_regs->haint); //fprintf(stderr, "HAINT: %08x\n", haint.d32); /* Read HCINT */ hcint.d32 = dwc_read_reg32(&hc_regs->hcint); //fprintf(stderr, "HCINT: %08x\n", hcint.d32); /* Read HCCHAR */ hcchar.d32 = dwc_read_reg32(&hc_regs->hcchar); //fprintf(stderr, "HCCHAR: %08x\n", hcchar.d32); /* Clear HCINT */ dwc_write_reg32(&hc_regs->hcint, hcint.d32); /* Clear HAINT */ dwc_write_reg32(&hc_global_regs->haint, haint.d32); /* Clear GINTSTS */ dwc_write_reg32(&global_regs->gintsts, gintsts.d32); /* Read GINTSTS */ gintsts.d32 = dwc_read_reg32(&global_regs->gintsts); //fprintf(stderr, "GINTSTS: %08x\n", gintsts.d32);}static void do_in_ack(void){ gintsts_data_t gintsts; hctsiz_data_t hctsiz; hcchar_data_t hcchar; haint_data_t haint; hcint_data_t hcint; host_grxsts_data_t grxsts; dbg_otg("%s\n", __FUNCTION__); /* Enable HAINTs */ dwc_write_reg32(&hc_global_regs->haintmsk, 0x0001); /* Enable HCINTs */ dwc_write_reg32(&hc_regs->hcintmsk, 0x04a3); /* Read GINTSTS */ gintsts.d32 = dwc_read_reg32(&global_regs->gintsts); //fprintf(stderr, "GINTSTS: %08x\n", gintsts.d32); /* Read HAINT */ haint.d32 = dwc_read_reg32(&hc_global_regs->haint); //fprintf(stderr, "HAINT: %08x\n", haint.d32); /* Read HCINT */ hcint.d32 = dwc_read_reg32(&hc_regs->hcint); //fprintf(stderr, "HCINT: %08x\n", hcint.d32); /* Read HCCHAR */ hcchar.d32 = dwc_read_reg32(&hc_regs->hcchar); //fprintf(stderr, "HCCHAR: %08x\n", hcchar.d32); /* Clear HCINT */ dwc_write_reg32(&hc_regs->hcint, hcint.d32); /* Clear HAINT */ dwc_write_reg32(&hc_global_regs->haint, haint.d32); /* Clear GINTSTS */ dwc_write_reg32(&global_regs->gintsts, gintsts.d32); /* Read GINTSTS */ gintsts.d32 = dwc_read_reg32(&global_regs->gintsts); //fprintf(stderr, "GINTSTS: %08x\n", gintsts.d32); /* * Receive Control In packet */ /* Make sure channel is disabled */ hcchar.d32 = dwc_read_reg32(&hc_regs->hcchar); if (hcchar.b.chen) { //fprintf(stderr, "Channel already enabled 2, HCCHAR = %08x\n", hcchar.d32); hcchar.b.chdis = 1; hcchar.b.chen = 1; dwc_write_reg32(&hc_regs->hcchar, hcchar.d32); //sleep(1); mdelay(1000); /* Read GINTSTS */ gintsts.d32 = dwc_read_reg32(&global_regs->gintsts); //fprintf(stderr, "GINTSTS: %08x\n", gintsts.d32); /* Read HAINT */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -