📄 usb-ohci.c
字号:
/*-------------------------------------------------------------------------*/ /* prepare all TDs of a transfer *//*********************************************************************************************************** Function name: ** Descriptions: ** Input:** Output :** Created by:** Created Date: **-------------------------------------------------------------------------------------------------------** Modified by:** Modified Date: **------------------------------------------------------------------------------------------------------********************************************************************************************************/static void td_submit_urb (urb_t * urb){ urb_priv_t * urb_priv = urb->hcpriv; ohci_t * ohci = (ohci_t *) urb->dev->bus->hcpriv; dma_addr_t data; int data_len = urb->transfer_buffer_length; int maxps = usb_maxpacket (urb->dev, urb->pipe, usb_pipeout (urb->pipe)); int cnt = 0; __u32 info = 0; unsigned int toggle = 0; /* OHCI handles the DATA-toggles itself, we just use the USB-toggle bits for reseting */ if(usb_gettoggle(urb->dev, usb_pipeendpoint(urb->pipe), usb_pipeout(urb->pipe))) { toggle = TD_T_TOGGLE; } else { toggle = TD_T_DATA0; usb_settoggle(urb->dev, usb_pipeendpoint(urb->pipe), usb_pipeout(urb->pipe), 1); } urb_priv->td_cnt = 0; if (data_len) { data = pci_map_single (ohci->ohci_dev, urb->transfer_buffer, data_len, usb_pipeout (urb->pipe) ? PCI_DMA_TODEVICE : PCI_DMA_FROMDEVICE ); } else data = 0; switch (usb_pipetype (urb->pipe)) { case PIPE_BULK: info = usb_pipeout (urb->pipe)? TD_CC | TD_DP_OUT : TD_CC | TD_DP_IN ; while(data_len > 4096) { td_fill (ohci, info | (cnt? TD_T_TOGGLE:toggle), data, 4096, urb, cnt); data += 4096; data_len -= 4096; cnt++; } info = usb_pipeout (urb->pipe)? TD_CC | TD_DP_OUT : TD_CC | TD_R | TD_DP_IN ; td_fill (ohci, info | (cnt? TD_T_TOGGLE:toggle), data, data_len, urb, cnt); cnt++; /* If the transfer size is multiple of the pipe mtu, * we may need an extra TD to create a empty frame * Note : another way to check this condition is * to test if(urb_priv->length > cnt) - Jean II */ if ((urb->transfer_flags & USB_ZERO_PACKET) && usb_pipeout (urb->pipe) && (urb->transfer_buffer_length != 0) && ((urb->transfer_buffer_length % maxps) == 0)) { td_fill (ohci, info | (cnt? TD_T_TOGGLE:toggle), 0, 0, urb, cnt); cnt++; } if (!ohci->sleeping) {// BUSHI wmb(); writel (OHCI_BLF, &ohci->regs->cmdstatus); /* start bulk list */ } break; case PIPE_INTERRUPT: info = usb_pipeout (urb->pipe)? TD_CC | TD_DP_OUT | toggle: TD_CC | TD_R | TD_DP_IN | toggle; td_fill (ohci, info, data, data_len, urb, cnt++); break; case PIPE_CONTROL: info = TD_CC | TD_DP_SETUP | TD_T_DATA0; td_fill (ohci, info, pci_map_single (ohci->ohci_dev, urb->setup_packet, 8, PCI_DMA_TODEVICE), 8, urb, cnt++); if (data_len > 0) { info = usb_pipeout (urb->pipe)? TD_CC | TD_R | TD_DP_OUT | TD_T_DATA1 : TD_CC | TD_R | TD_DP_IN | TD_T_DATA1; /* NOTE: mishandles transfers >8K, some >4K */ td_fill (ohci, info, data, data_len, urb, cnt++); } info = usb_pipeout (urb->pipe)? TD_CC | TD_DP_IN | TD_T_DATA1: TD_CC | TD_DP_OUT | TD_T_DATA1; td_fill (ohci, info, data, 0, urb, cnt++); if (!ohci->sleeping) { // BUSHI wmb(); writel (OHCI_CLF, &ohci->regs->cmdstatus); /* start Control list */ } break; case PIPE_ISOCHRONOUS: for (cnt = 0; cnt < urb->number_of_packets; cnt++) { td_fill (ohci, TD_CC|TD_ISO | ((urb->start_frame + cnt) & 0xffff), data + urb->iso_frame_desc[cnt].offset, urb->iso_frame_desc[cnt].length, urb, cnt); } break; } // if (urb_priv->length != cnt) // dbg("TD LENGTH %d != CNT %d", urb_priv->length, cnt);}/*-------------------------------------------------------------------------* * Done List handling functions *-------------------------------------------------------------------------*//* calculate the transfer length and update the urb */static void dl_transfer_length(td_t * td){ __u32 tdINFO, tdBE, tdCBP; __u16 tdPSW; urb_t * urb = td->urb; urb_priv_t * urb_priv = urb->hcpriv; int dlen = 0; int cc = 0; tdINFO = le32_to_cpup (&td->hwINFO); tdBE = le32_to_cpup (&td->hwBE); tdCBP = le32_to_cpup (&td->hwCBP); if (tdINFO & TD_ISO) { tdPSW = le16_to_cpu (td->hwPSW[0]); cc = (tdPSW >> 12) & 0xF; if (cc < 0xE) { if (usb_pipeout(urb->pipe)) { dlen = urb->iso_frame_desc[td->index].length; } else { dlen = tdPSW & 0x3ff; } urb->actual_length += dlen; urb->iso_frame_desc[td->index].actual_length = dlen; if (!(urb->transfer_flags & USB_DISABLE_SPD) && (cc == TD_DATAUNDERRUN)) cc = TD_CC_NOERROR; urb->iso_frame_desc[td->index].status = cc_to_error[cc]; } } else { /* BULK, INT, CONTROL DATA */ if (!(usb_pipetype (urb->pipe) == PIPE_CONTROL && ((td->index == 0) || (td->index == urb_priv->length - 1)))) { if (tdBE != 0) { if (td->hwCBP == 0) urb->actual_length += tdBE - td->data_dma + 1; else urb->actual_length += tdCBP - td->data_dma; } } }}/* handle an urb that is being unlinked *//*********************************************************************************************************** Function name: ** Descriptions: ** Input:** Output :** Created by:** Created Date: **-------------------------------------------------------------------------------------------------------** Modified by:** Modified Date: **------------------------------------------------------------------------------------------------------********************************************************************************************************/static void dl_del_urb (urb_t * urb){ wait_queue_head_t * wait_head = ((urb_priv_t *)(urb->hcpriv))->wait; urb_rm_priv_locked (urb); if (urb->transfer_flags & USB_ASYNC_UNLINK) { urb->status = -ECONNRESET; if (urb->complete) urb->complete (urb); } else { urb->status = -ENOENT; /* unblock sohci_unlink_urb */ if (wait_head) wake_up (wait_head); }}/*-------------------------------------------------------------------------*//* replies to the request have to be on a FIFO basis so * we reverse the reversed done-list */// 返转链表项顺序 static td_t * dl_reverse_done_list (ohci_t * ohci){ __u32 td_list_hc; td_t * td_rev = NULL; td_t * td_list = NULL; urb_priv_t * urb_priv = NULL; unsigned long flags; spin_lock_irqsave (&usb_ed_lock, flags); td_list_hc = le32_to_cpup (&ohci->hcca->done_head) & 0xfffffff0; ohci->hcca->done_head = 0; while (td_list_hc) { td_list = dma_to_td (ohci, td_list_hc); // 从传输描述符列表中找到 if (TD_CC_GET (le32_to_cpup (&td_list->hwINFO))) { // 获取完成代码,正常完成代码为0,其它值为出错 urb_priv = (urb_priv_t *) td_list->urb->hcpriv; // URB块中的私有数据 // dbg(" USB-error/status: %x : %p", // TD_CC_GET (le32_to_cpup (&td_list->hwINFO)), td_list); if (td_list->ed->hwHeadP & cpu_to_le32 (0x1)) { if (urb_priv && ((td_list->index + 1) < urb_priv->length)) { td_list->ed->hwHeadP = (urb_priv->td[urb_priv->length - 1]->hwNextTD & cpu_to_le32 (0xfffffff0)) | (td_list->ed->hwHeadP & cpu_to_le32 (0x2)); urb_priv->td_cnt += urb_priv->length - td_list->index - 1; } else td_list->ed->hwHeadP &= cpu_to_le32 (0xfffffff2); } } td_list->next_dl_td = td_rev; td_rev = td_list; td_list_hc = le32_to_cpup (&td_list->hwNextTD) & 0xfffffff0; // 获取下一个传输描述符指针 } spin_unlock_irqrestore (&usb_ed_lock, flags); return td_list;}/*-------------------------------------------------------------------------*//* there are some pending requests to remove * - some of the eds (if ed->state & ED_DEL (set by sohci_free_dev) * - some URBs/TDs if urb_priv->state == URB_DEL *//*********************************************************************************************************** Function name: ** Descriptions: ** Input:** Output :** Created by:** Created Date: **-------------------------------------------------------------------------------------------------------** Modified by:** Modified Date: **------------------------------------------------------------------------------------------------------********************************************************************************************************/ static void dl_del_list (ohci_t * ohci, unsigned int frame){ unsigned long flags; ed_t * ed; __u32 edINFO; __u32 tdINFO; td_t * td = NULL, * td_next = NULL, * tdHeadP = NULL, * tdTailP; __u32 * td_p; int ctrl = 0, bulk = 0; spin_lock_irqsave (&usb_ed_lock, flags); for (ed = ohci->ed_rm_list[frame]; ed != NULL; ed = ed->ed_rm_list) { tdTailP = dma_to_td (ohci, le32_to_cpup (&ed->hwTailP) & 0xfffffff0); tdHeadP = dma_to_td (ohci, le32_to_cpup (&ed->hwHeadP) & 0xfffffff0); edINFO = le32_to_cpup (&ed->hwINFO); td_p = &ed->hwHeadP; for (td = tdHeadP; td != tdTailP; td = td_next) { urb_t * urb = td->urb; urb_priv_t * urb_priv = td->urb->hcpriv; td_next = dma_to_td (ohci, le32_to_cpup (&td->hwNextTD) & 0xfffffff0); if ((urb_priv->state == URB_DEL) || (ed->state & ED_DEL)) { tdINFO = le32_to_cpup (&td->hwINFO); if (TD_CC_GET (tdINFO) < 0xE) dl_transfer_length (td); *td_p = td->hwNextTD | (*td_p & cpu_to_le32 (0x3)); /* URB is done; clean up */ if (++(urb_priv->td_cnt) == urb_priv->length) dl_del_urb (urb); } else { td_p = &td->hwNextTD; } } if (ed->state & ED_DEL) { /* set by sohci_free_dev */ struct ohci_device * dev = usb_to_ohci (ohci->dev[edINFO & 0x7F]); td_free (ohci, tdTailP); /* free dummy td */ ed->hwINFO = cpu_to_le32 (OHCI_ED_SKIP); ed->state = ED_NEW; hash_free_ed(ohci, ed); /* if all eds are removed wake up sohci_free_dev */ if (!--dev->ed_cnt) { wait_queue_head_t *wait_head = dev->wait; dev->wait = 0; if (wait_head) wake_up (wait_head); } } else { ed->state &= ~ED_URB_DEL; tdHeadP = dma_to_td (ohci, le32_to_cpup (&ed->hwHeadP) & 0xfffffff0); if (tdHeadP == tdTailP) { if (ed->state == ED_OPER) ep_unlink(ohci, ed); td_free (ohci, tdTailP); ed->hwINFO = cpu_to_le32 (OHCI_ED_SKIP); ed->state = ED_NEW; hash_free_ed(ohci, ed); --(usb_to_ohci (ohci->dev[edINFO & 0x7F]))->ed_cnt; } else ed->hwINFO &= ~cpu_to_le32 (OHCI_ED_SKIP); } switch (ed->type) { case PIPE_CONTROL: ctrl = 1; break; case PIPE_BULK: bulk = 1; break; } } /* maybe reenable control and bulk lists */ if (!ohci->disabled) { if (ctrl) /* reset control list */ writel (0, &ohci->regs->ed_controlcurrent); if (bulk) /* reset bulk list */ writel (0, &ohci->regs->ed_bulkcurrent); if (!ohci->ed_rm_list[!frame] && !ohci->sleeping) { if (ohci->ed_controltail) ohci->hc_control |= OHCI_CTRL_CLE; if (ohci->ed_bulktail) ohci->hc_control |= OHCI_CTRL_BLE; writel (ohci->hc_control, &ohci->regs->control); } } ohci->ed_rm_list[frame] = NULL; spin_unlock_irqrestore (&usb_ed_lock, flags);} /*-------------------------------------------------------------------------*//* td done list */// 已完成的传输描述符处理/*********************************************************************************************************** Function name: dl_done_list** Descriptions: 已完成的传输描述符处理** Input:* ohci:** * td_list:** Output 0:** 1:** Created by:** Created Date: **-------------------------------------------------------------------------------------------------------** Modified by:** Modified Date: **------------------------------------------------------------------------------------------------------********************************************************************************************************/static void dl_done_list (ohci_t * ohci, td_t * td_list){ td_t * td_list_next = NULL; ed_t * ed; int cc = 0; urb_t * urb; urb_priv_t * urb_priv; __u32 tdINFO, edHeadP, edTailP; unsigned long flags; while (td_list) { td_list_next = td_list->next_dl_td; // 获取下一个已完成的传输列表项 urb = td_list->urb; // 获取URB块描述数据结构指针 urb_priv = urb->hcpriv; // 获取URB与系统相关的私有数据指针 tdINFO = le32_to_cpup (&td_list->hwINFO); // 传输描述符的状态信息 ed = td_list->ed; // 传输描述符对应的端点描述符指针 dl_transfer_length(td_list); // ********************************* /* error code of transfer */ cc = TD_CC_GET (tdINFO); if (cc == TD_CC_STALL) // 端点停止 usb_endpoint_halt(urb->dev, // 执行端点停止处理函数 usb_pipeendpoint(urb->pipe), usb_pipeout(urb->pipe)); if (!(
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -