📄 dwc_otg_hcd.c
字号:
if (hcd == NULL) { retval = -ENOMEM; goto error1; } hcd->regs = otg_dev->base; hcd->self.otg_port = 1; /* Initialize the DWC OTG HCD. */ dwc_otg_hcd = hcd_to_dwc_otg_hcd(hcd); dwc_otg_hcd->core_if = otg_dev->core_if; otg_dev->hcd = dwc_otg_hcd; /* Register the HCD CIL Callbacks */ dwc_otg_cil_register_hcd_callbacks(otg_dev->core_if, &hcd_cil_callbacks, hcd); /* Initialize the non-periodic schedule. */ INIT_LIST_HEAD(&dwc_otg_hcd->non_periodic_sched_inactive); INIT_LIST_HEAD(&dwc_otg_hcd->non_periodic_sched_active); /* Initialize the periodic schedule. */ INIT_LIST_HEAD(&dwc_otg_hcd->periodic_sched_inactive); INIT_LIST_HEAD(&dwc_otg_hcd->periodic_sched_ready); INIT_LIST_HEAD(&dwc_otg_hcd->periodic_sched_assigned); INIT_LIST_HEAD(&dwc_otg_hcd->periodic_sched_queued); /* * Create a host channel descriptor for each host channel implemented * in the controller. Initialize the channel descriptor array. */ INIT_LIST_HEAD(&dwc_otg_hcd->free_hc_list); num_channels = dwc_otg_hcd->core_if->core_params->host_channels; for (i = 0; i < num_channels; i++) { channel = kmalloc(sizeof(dwc_hc_t), GFP_KERNEL); if (channel == NULL) { retval = -ENOMEM; printk("%s: host channel allocation failed\n", __func__); goto error2; } memset(channel, 0, sizeof(dwc_hc_t)); channel->hc_num = i; dwc_otg_hcd->hc_ptr_array[i] = channel;#ifdef DEBUG init_timer(&dwc_otg_hcd->core_if->hc_xfer_timer[i]);#endif dbg_otg("HCD Added channel #%d, hc=%p\n", i, channel); } /* Initialize the Connection timeout timer. */ init_timer(&dwc_otg_hcd->conn_timer); /* Initialize reset tasklet. */ reset_tasklet.data = (unsigned long) dwc_otg_hcd; dwc_otg_hcd->reset_tasklet = &reset_tasklet;#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,20) /* Set device flags indicating whether the HCD supports DMA. */ if (otg_dev->core_if->dma_enable) { DWC_PRINT("Using DMA mode\n"); pdev->dev.dma_mask = (void *) ~0; pdev->dev.coherent_dma_mask = ~0; if (otg_dev->core_if->dma_desc_enable) { DWC_PRINT("Device using Descriptor DMA mode\n"); } else { DWC_PRINT("Device using Buffer DMA mode\n"); } } else { DWC_PRINT("Using Slave mode\n"); pdev->dev.dma_mask = (void *) 0; pdev->dev.coherent_dma_mask = 0; }#endif /* * Finish generic HCD initialization and start the HCD. This function * allocates the DMA buffer pool, registers the USB bus, requests the * IRQ line, and calls dwc_otg_hcd_start method. * 2nd contact point of USB Core by scsuh. */ retval = usb_add_hcd(hcd, IRQ_OTG, SA_SHIRQ); if (retval < 0) { goto error2; } /* * Allocate space for storing data on status transactions. Normally no * data is sent, but this space acts as a bit bucket. This must be * done after usb_add_hcd since that function allocates the DMA buffer * pool. */ if (otg_dev->core_if->dma_enable) { dwc_otg_hcd->status_buf = dma_alloc_coherent(&pdev->dev, DWC_OTG_HCD_STATUS_BUF_SIZE, &dwc_otg_hcd->status_buf_dma, GFP_KERNEL | GFP_DMA); } else { dwc_otg_hcd->status_buf = kmalloc(DWC_OTG_HCD_STATUS_BUF_SIZE, GFP_KERNEL); } if (dwc_otg_hcd->status_buf == NULL) { retval = -ENOMEM; DWC_ERROR("%s: status_buf allocation failed\n", __func__); goto error3; } dbg_otg("DWC OTG HCD Initialized HCD, bus=%s, usbbus=%d\n", pdev->dev.bus_id, hcd->self.busnum); return 0; /* Error conditions */ error3: usb_remove_hcd(hcd); error2: dwc_otg_hcd_free(hcd); usb_put_hcd(hcd); error1: return retval;}/** * Removes the HCD. * Frees memory and resources associated with the HCD and deregisters the bus. */void dwc_otg_hcd_remove(struct platform_device *_lmdev){ dwc_otg_device_t *otg_dev = platform_get_drvdata(_lmdev); dwc_otg_hcd_t *dwc_otg_hcd; struct usb_hcd *hcd; DWC_DEBUGPL(DBG_HCD, "DWC OTG HCD REMOVE\n"); if (!otg_dev) { DWC_DEBUGPL(DBG_ANY, "%s: otg_dev NULL!\n", __func__); return; } dwc_otg_hcd = otg_dev->hcd; if (!dwc_otg_hcd) { DWC_DEBUGPL(DBG_ANY, "%s: otg_dev->hcd NULL!\n", __func__); return; } hcd = dwc_otg_hcd_to_hcd(dwc_otg_hcd); if (!hcd) { DWC_DEBUGPL(DBG_ANY, "%s: dwc_otg_hcd_to_hcd(dwc_otg_hcd) NULL!\n", __func__); return; } /* Turn off all interrupts */ dwc_write_reg32(&dwc_otg_hcd->core_if->core_global_regs->gintmsk, 0); dwc_modify_reg32(&dwc_otg_hcd->core_if->core_global_regs->gahbcfg, 1, 0); usb_remove_hcd(hcd); dwc_otg_hcd_free(hcd); usb_put_hcd(hcd); return;}/* ========================================================================= * Linux HC Driver Functions * ========================================================================= *//** * Initializes dynamic portions of the DWC_otg HCD state. */static void hcd_reinit(dwc_otg_hcd_t * _hcd){ struct list_head *item; int num_channels; int i; dwc_hc_t *channel; _hcd->flags.d32 = 0; _hcd->non_periodic_qh_ptr = &_hcd->non_periodic_sched_active; _hcd->non_periodic_channels = 0; _hcd->periodic_channels = 0; /* * Put all channels in the free channel list and clean up channel * states. */ item = _hcd->free_hc_list.next; while (item != &_hcd->free_hc_list) { list_del(item); item = _hcd->free_hc_list.next; } num_channels = _hcd->core_if->core_params->host_channels; for (i = 0; i < num_channels; i++) { channel = _hcd->hc_ptr_array[i]; list_add_tail(&channel->hc_list_entry, &_hcd->free_hc_list); dwc_otg_hc_cleanup(_hcd->core_if, channel); } /* Initialize the DWC core for host mode operation. */ dwc_otg_core_host_init(_hcd->core_if);}/** Initializes the DWC_otg controller and its root hub and prepares it for host * mode operation. Activates the root port. Returns 0 on success and a negative * error code on failure. */int dwc_otg_hcd_start(struct usb_hcd *_hcd){ dwc_otg_hcd_t *dwc_otg_hcd = hcd_to_dwc_otg_hcd(_hcd); dwc_otg_core_if_t *core_if = dwc_otg_hcd->core_if; struct usb_bus *bus; dbg_otg("\n\n\nDWC OTG HCD START\n\n\n\n"); bus = hcd_to_bus(_hcd); /* Initialize the bus state. If the core is in Device Mode * HALT the USB bus and return. */ if (dwc_otg_is_device_mode(core_if)) { _hcd->state = HC_STATE_RUNNING; return 0; } _hcd->state = HC_STATE_RUNNING; /* Initialize and connect root hub if one is not already attached */ if (bus->root_hub) { DWC_DEBUGPL(DBG_HCD, "DWC OTG HCD Has Root Hub\n"); /* Inform the HUB driver to resume. */ usb_hcd_resume_root_hub(_hcd); } else { DWC_DEBUGPL(DBG_HCD, "DWC OTG HCD Does Not Have Root Hub\n"); } hcd_reinit(dwc_otg_hcd); return 0;}static void qh_list_free(dwc_otg_hcd_t * _hcd, struct list_head *_qh_list){ struct list_head *item; dwc_otg_qh_t *qh; if (_qh_list->next == NULL) { /* The list hasn't been initialized yet. */ return; } /* Ensure there are no QTDs or URBs left. */ kill_urbs_in_qh_list(_hcd, _qh_list); for (item = _qh_list->next; item != _qh_list; item = _qh_list->next) { qh = list_entry(item, dwc_otg_qh_t, qh_list_entry); dwc_otg_hcd_qh_remove_and_free(_hcd, qh); }}/** * Halts the DWC_otg host mode operations in a clean manner. USB transfers are * stopped. */void dwc_otg_hcd_stop(struct usb_hcd *_hcd){ dwc_otg_hcd_t *dwc_otg_hcd = hcd_to_dwc_otg_hcd(_hcd); hprt0_data_t hprt0 = {.d32 = 0 }; DWC_DEBUGPL(DBG_HCD, "DWC OTG HCD STOP\n"); /* Turn off all host-specific interrupts. */ dwc_otg_disable_host_interrupts(dwc_otg_hcd->core_if); /* * The root hub should be disconnected before this function is called. * The disconnect will clear the QTD lists (via ..._hcd_urb_dequeue) * and the QH lists (via ..._hcd_endpoint_disable). */ /* Turn off the vbus power */ DWC_PRINT("PortPower off\n"); hprt0.b.prtpwr = 0; dwc_write_reg32(dwc_otg_hcd->core_if->host_if->hprt0, hprt0.d32); return;}/** Returns the current frame number. */int dwc_otg_hcd_get_frame_number(struct usb_hcd *_hcd){#if 0 dwc_otg_hcd_t *dwc_otg_hcd = hcd_to_dwc_otg_hcd(_hcd); hfnum_data_t hfnum; hfnum.d32 = dwc_read_reg32(&dwc_otg_hcd->core_if->host_if->host_global_regs->hfnum);#ifdef DEBUG_SOF DWC_DEBUGPL(DBG_HCDV, "DWC OTG HCD GET FRAME NUMBER %d\n", hfnum.b.frnum);#endif return hfnum.b.frnum;#else return (readl(S3C_UDC_OTG_HFNUM) & 0x0000ffff);#endif}/** * Frees secondary storage associated with the dwc_otg_hcd structure contained * in the struct usb_hcd field. */void dwc_otg_hcd_free(struct usb_hcd *_hcd){ dwc_otg_hcd_t *dwc_otg_hcd = hcd_to_dwc_otg_hcd(_hcd); int i; DWC_DEBUGPL(DBG_HCD, "DWC OTG HCD FREE\n"); del_timers(dwc_otg_hcd); /* Free memory for QH/QTD lists */ qh_list_free(dwc_otg_hcd, &dwc_otg_hcd->non_periodic_sched_inactive); qh_list_free(dwc_otg_hcd, &dwc_otg_hcd->non_periodic_sched_active); qh_list_free(dwc_otg_hcd, &dwc_otg_hcd->periodic_sched_inactive); qh_list_free(dwc_otg_hcd, &dwc_otg_hcd->periodic_sched_ready); qh_list_free(dwc_otg_hcd, &dwc_otg_hcd->periodic_sched_assigned); qh_list_free(dwc_otg_hcd, &dwc_otg_hcd->periodic_sched_queued); /* Free memory for the host channels. */ for (i = 0; i < MAX_EPS_CHANNELS; i++) { dwc_hc_t *hc = dwc_otg_hcd->hc_ptr_array[i]; if (hc != NULL) { DWC_DEBUGPL(DBG_HCDV, "HCD Free channel #%i, hc=%p\n", i, hc); kfree(hc); } } if (dwc_otg_hcd->core_if->dma_enable) { if (dwc_otg_hcd->status_buf_dma) { dma_free_coherent(_hcd->self.controller, DWC_OTG_HCD_STATUS_BUF_SIZE, dwc_otg_hcd->status_buf, dwc_otg_hcd->status_buf_dma); } } else if (dwc_otg_hcd->status_buf != NULL) { kfree(dwc_otg_hcd->status_buf); } return;}#ifdef DEBUGstatic void dump_urb_info(struct urb *_urb, char *_fn_name){ DWC_PRINT("%s, urb %p\n", _fn_name, _urb); DWC_PRINT(" Device address: %d\n", usb_pipedevice(_urb->pipe)); DWC_PRINT(" Endpoint: %d, %s\n", usb_pipeendpoint(_urb->pipe), (usb_pipein(_urb->pipe) ? "IN" : "OUT")); DWC_PRINT(" Endpoint type: %s\n", ( { char *pipetype; switch (usb_pipetype(_urb->pipe)) {case PIPE_CONTROL:pipetype = "CONTROL"; break; case PIPE_BULK:pipetype = "BULK"; break; case PIPE_INTERRUPT:pipetype = "INTERRUPT"; break; case PIPE_ISOCHRONOUS:pipetype = "ISOCHRONOUS"; break; default: pipetype = "UNKNOWN"; break;}; pipetype;} )); DWC_PRINT(" Speed: %s\n", ( { char *speed; switch (_urb->dev->speed) {case USB_SPEED_HIGH:speed = "HIGH"; break; case USB_SPEED_FULL:speed = "FULL"; break; case USB_SPEED_LOW:speed = "LOW"; break; default: speed = "UNKNOWN"; break;}; speed;} )); DWC_PRINT(" Max packet size: %d\n", usb_maxpacket(_urb->dev, _urb->pipe, usb_pipeout(_urb->pipe))); DWC_PRINT(" Data buffer length: %d\n", _urb->transfer_buffer_length); DWC_PRINT(" Transfer buffer: %p, Transfer DMA: %p\n", _urb->transfer_buffer, (void *) _urb->transfer_dma); DWC_PRINT(" Setup buffer: %p, Setup DMA: %p\n", _urb->setup_packet, (void *) _urb->setup_dma); DWC_PRINT(" Interval: %d\n", _urb->interval); if (usb_pipetype(_urb->pipe) == PIPE_ISOCHRONOUS) { int i; for (i = 0; i < _urb->number_of_packets; i++) { DWC_PRINT(" ISO Desc %d:\n", i); DWC_PRINT(" offset: %d, length %d\n", _urb->iso_frame_desc[i].offset, _urb->iso_frame_desc[i].length); } }}static void dump_channel_info(dwc_otg_hcd_t * _hcd, dwc_otg_qh_t * qh){ if (qh->channel != NULL) { dwc_hc_t *hc = qh->channel; struct list_head *item; dwc_otg_qh_t *qh_item; int num_channels = _hcd->core_if->core_params->host_channels; int i; dwc_otg_hc_regs_t *hc_regs; hcchar_data_t hcchar; hcsplt_data_t hcsplt; hctsiz_data_t hctsiz; uint32_t hcdma; hc_regs = _hcd->core_if->host_if->hc_regs[hc->hc_num]; hcchar.d32 = dwc_read_reg32(&hc_regs->hcchar); hcsplt.d32 = dwc_read_reg32(&hc_regs->hcsplt); hctsiz.d32 = dwc_read_reg32(&hc_regs->hctsiz); hcdma = dwc_read_reg32(&hc_regs->hcdma); DWC_PRINT(" Assigned to channel %p:\n", hc); DWC_PRINT(" hcchar 0x%08x, hcsplt 0x%08x\n", hcchar.d32, hcsplt.d32); DWC_PRINT(" hctsiz 0x%08x, hcdma 0x%08x\n", hctsiz.d32, hcdma); DWC_PRINT(" dev_addr: %d, ep_num: %d, ep_is_in: %d\n", hc->dev_addr, hc->ep_num, hc->ep_is_in); DWC_PRINT(" ep_type: %d\n", hc->ep_type); DWC_PRINT(" max_packet: %d\n", hc->max_packet); DWC_PRINT(" data_pid_start: %d\n", hc->data_pid_start); DWC_PRINT(" xfer_started: %d\n", hc->xfer_started); DWC_PRINT(" halt_status: %d\n", hc->halt_status); DWC_PRINT(" xfer_buff: %p\n", hc->xfer_buff); DWC_PRINT(" xfer_len: %d\n", hc->xfer_len); DWC_PRINT(" qh: %p\n", hc->qh); DWC_PRINT(" NP inactive sched:\n"); list_for_each(item, &_hcd->non_periodic_sched_inactive) { qh_item = list_entry(item, dwc_otg_qh_t, qh_list_entry); DWC_PRINT(" %p\n", qh_item); } DWC_PRINT(" NP active sched:\n"); list_for_each(item, &_hcd->non_periodic_sched_active) { qh_item = list_entry(item, dwc_otg_qh_t, qh_list_entry); DWC_PRINT(" %p\n", qh_item); } DWC_PRINT(" Channels: \n"); for (i = 0; i < num_channels; i++) { dwc_hc_t *hc = _hcd->hc_ptr_array[i]; DWC_PRINT(" %2d: %p\n", i, hc); } }}#endif/** Starts processing a USB transfer request specified by a USB Request Block * (URB). mem_flags indicates the type of memory allocation to use while * processing this URB. */int dwc_otg_hcd_urb_enqueue(struct usb_hcd *_hcd,
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -