📄 usb-ohci.c
字号:
} return 0;}/*-------------------------------------------------------------------------*//* unlink an ed from one of the HC chains. * just the link to the ed is unlinked. * the link from the ed still points to another operational ed or 0 * so the HC can eventually finish the processing of the unlinked ed */static int ep_unlink (ohci_t * ohci, ed_t * ed) { int int_branch; int i; int inter; int interval; __u32 * ed_p; ed->hwINFO |= cpu_to_le32 (OHCI_ED_SKIP); switch (ed->type) { case PIPE_CONTROL: if (ed->ed_prev == NULL) { if (!ed->hwNextED) { ohci->hc_control &= ~OHCI_CTRL_CLE; writel (ohci->hc_control, &ohci->regs->control); } writel (le32_to_cpup (&ed->hwNextED), &ohci->regs->ed_controlhead); } else { ed->ed_prev->hwNextED = ed->hwNextED; } if (ohci->ed_controltail == ed) { ohci->ed_controltail = ed->ed_prev; } else { ((ed_t *) bus_to_virt (le32_to_cpup (&ed->hwNextED)))->ed_prev = ed->ed_prev; } break; case PIPE_BULK: if (ed->ed_prev == NULL) { if (!ed->hwNextED) { ohci->hc_control &= ~OHCI_CTRL_BLE; writel (ohci->hc_control, &ohci->regs->control); } writel (le32_to_cpup (&ed->hwNextED), &ohci->regs->ed_bulkhead); } else { ed->ed_prev->hwNextED = ed->hwNextED; } if (ohci->ed_bulktail == ed) { ohci->ed_bulktail = ed->ed_prev; } else { ((ed_t *) bus_to_virt (le32_to_cpup (&ed->hwNextED)))->ed_prev = ed->ed_prev; } break; case PIPE_INTERRUPT: int_branch = ed->int_branch; interval = ed->int_interval; for (i = 0; i < ep_rev (6, interval); i += inter) { for (ed_p = &(ohci->hcca.int_table[ep_rev (5, i) + int_branch]), inter = 1; (*ed_p != 0) && (*ed_p != ed->hwNextED); ed_p = &(((ed_t *) bus_to_virt (le32_to_cpup (ed_p)))->hwNextED), inter = ep_rev (6, ((ed_t *) bus_to_virt (le32_to_cpup (ed_p)))->int_interval)) { if(((ed_t *) bus_to_virt (le32_to_cpup (ed_p))) == ed) { *ed_p = ed->hwNextED; break; } } } for (i = int_branch; i < 32; i += interval) ohci->ohci_int_load[i] -= ed->int_load;#ifdef DEBUG ep_print_int_eds (ohci, "UNLINK_INT");#endif break; case PIPE_ISOCHRONOUS: if (ohci->ed_isotail == ed) ohci->ed_isotail = ed->ed_prev; if (ed->hwNextED != 0) ((ed_t *) bus_to_virt (le32_to_cpup (&ed->hwNextED)))->ed_prev = ed->ed_prev; if (ed->ed_prev != NULL) { ed->ed_prev->hwNextED = ed->hwNextED; } else { for (i = 0; i < 32; i++) { for (ed_p = &(ohci->hcca.int_table[ep_rev (5, i)]); *ed_p != 0; ed_p = &(((ed_t *) bus_to_virt (le32_to_cpup (ed_p)))->hwNextED)) { // inter = ep_rev (6, ((ed_t *) bus_to_virt (le32_to_cpup (ed_p)))->int_interval); if(((ed_t *) bus_to_virt (le32_to_cpup (ed_p))) == ed) { *ed_p = ed->hwNextED; break; } } } } #ifdef DEBUG ep_print_int_eds (ohci, "UNLINK_ISO");#endif break; } ed->state = ED_UNLINK; return 0;}/*-------------------------------------------------------------------------*//* add/reinit an endpoint; this should be done once at the usb_set_configuration command, * but the USB stack is a little bit stateless so we do it at every transaction * if the state of the ed is ED_NEW then a dummy td is added and the state is changed to ED_UNLINK * in all other cases the state is left unchanged * the ed info fields are setted anyway even though most of them should not change */ static ed_t * ep_add_ed (struct usb_device * usb_dev, unsigned int pipe, int interval, int load){ ohci_t * ohci = usb_dev->bus->hcpriv; td_t * td; ed_t * ed_ret; volatile ed_t * ed; unsigned long flags; spin_lock_irqsave (&usb_ed_lock, flags); ed = ed_ret = &(usb_to_ohci (usb_dev)->ed[(usb_pipeendpoint (pipe) << 1) | (usb_pipecontrol (pipe)? 0: usb_pipeout (pipe))]); if ((ed->state & ED_DEL) || (ed->state & ED_URB_DEL)) { /* pending delete request */ spin_unlock_irqrestore (&usb_ed_lock, flags); return NULL; } if (ed->state == ED_NEW) { ed->hwINFO = cpu_to_le32 (OHCI_ED_SKIP); /* skip ed */ /* dummy td; end of td list for ed */ td = td_alloc (ohci); if (!td) { /* out of memory */ spin_unlock_irqrestore (&usb_ed_lock, flags); return NULL; } ed->hwTailP = cpu_to_le32 (virt_to_bus (td)); ed->hwHeadP = ed->hwTailP; ed->state = ED_UNLINK; ed->type = usb_pipetype (pipe); usb_to_ohci (usb_dev)->ed_cnt++; } ohci->dev[usb_pipedevice (pipe)] = usb_dev; ed->hwINFO = cpu_to_le32 (usb_pipedevice (pipe) | usb_pipeendpoint (pipe) << 7 | (usb_pipeisoc (pipe)? 0x8000: 0) | (usb_pipecontrol (pipe)? 0: (usb_pipeout (pipe)? 0x800: 0x1000)) | usb_pipeslow (pipe) << 13 | usb_maxpacket (usb_dev, pipe, usb_pipeout (pipe)) << 16); if (ed->type == PIPE_INTERRUPT && ed->state == ED_UNLINK) { ed->int_period = interval; ed->int_load = load; } spin_unlock_irqrestore (&usb_ed_lock, flags); return ed_ret; }/*-------------------------------------------------------------------------*/ /* request the removal of an endpoint * put the ep on the rm_list and request a stop of the bulk or ctrl list * real removal is done at the next start frame (SF) hardware interrupt */ static void ep_rm_ed (struct usb_device * usb_dev, ed_t * ed){ unsigned int frame; ohci_t * ohci = usb_dev->bus->hcpriv; if ((ed->state & ED_DEL) || (ed->state & ED_URB_DEL)) return; ed->hwINFO |= cpu_to_le32 (OHCI_ED_SKIP); if (!ohci->disabled) { switch (ed->type) { case PIPE_CONTROL: /* stop control list */ ohci->hc_control &= ~OHCI_CTRL_CLE; writel (ohci->hc_control, &ohci->regs->control); break; case PIPE_BULK: /* stop bulk list */ ohci->hc_control &= ~OHCI_CTRL_BLE; writel (ohci->hc_control, &ohci->regs->control); break; } } frame = le16_to_cpu (ohci->hcca.frame_no) & 0x1; ed->ed_rm_list = ohci->ed_rm_list[frame]; ohci->ed_rm_list[frame] = ed; if (!ohci->disabled) { /* enable SOF interrupt */ writel (OHCI_INTR_SF, &ohci->regs->intrstatus); writel (OHCI_INTR_SF, &ohci->regs->intrenable); }}/*-------------------------------------------------------------------------* * TD handling functions *-------------------------------------------------------------------------*//* prepare a TD */static void td_fill (unsigned int info, void * data, int len, urb_t * urb, int index){ volatile td_t * td, * td_pt; urb_priv_t * urb_priv = urb->hcpriv; if (index >= urb_priv->length) { err("internal OHCI error: TD index > length"); return; } td_pt = urb_priv->td [index]; /* fill the old dummy TD */ td = urb_priv->td [index] = (td_t *) bus_to_virt (le32_to_cpup (&urb_priv->ed->hwTailP) & 0xfffffff0); td->ed = urb_priv->ed; td->next_dl_td = NULL; td->index = index; td->urb = urb; td->hwINFO = cpu_to_le32 (info); if ((td->ed->type) == PIPE_ISOCHRONOUS) { td->hwCBP = cpu_to_le32 (((!data || !len) ? 0 : virt_to_bus (data)) & 0xFFFFF000); td->ed->last_iso = info & 0xffff; } else { td->hwCBP = cpu_to_le32 (((!data || !len) ? 0 : virt_to_bus (data))); } td->hwBE = cpu_to_le32 ((!data || !len ) ? 0 : virt_to_bus (data + len - 1)); td->hwNextTD = cpu_to_le32 (virt_to_bus (td_pt)); td->hwPSW [0] = cpu_to_le16 ((virt_to_bus (data) & 0x0FFF) | 0xE000); td_pt->hwNextTD = 0; td->ed->hwTailP = td->hwNextTD;}/*-------------------------------------------------------------------------*/ /* prepare all TDs of a transfer */static void td_submit_urb (urb_t * urb){ urb_priv_t * urb_priv = urb->hcpriv; ohci_t * ohci = (ohci_t *) urb->dev->bus->hcpriv; void * ctrl = urb->setup_packet; void * data = urb->transfer_buffer; int data_len = urb->transfer_buffer_length; 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; 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 (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 (info | (cnt? TD_T_TOGGLE:toggle), data, data_len, urb, cnt); cnt++; 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 (info, data, data_len, urb, cnt++); break; case PIPE_CONTROL: info = TD_CC | TD_DP_SETUP | TD_T_DATA0; td_fill (info, ctrl, 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; td_fill (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 (info, NULL, 0, urb, cnt++); writel (OHCI_CLF, &ohci->regs->cmdstatus); /* start Control list */ break; case PIPE_ISOCHRONOUS: for (cnt = 0; cnt < urb->number_of_packets; cnt++) { td_fill (TD_CC|TD_ISO | ((urb->start_frame + cnt) & 0xffff), (__u8 *) 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 = bus_to_virt (tdBE) - urb->transfer_buffer + 1; else urb->actual_length = bus_to_virt (tdCBP) - urb->transfer_buffer; } } }}/* handle an urb that is being unlinked */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 = (td_t *) bus_to_virt (td_list_hc); if (TD_CC_GET (le32_to_cpup (&td_list->hwINFO))) { urb_priv = (urb_priv_t *) td_list->urb->hcpriv; 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 */ 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 = bus_to_virt (le32_to_cpup (&ed->hwTailP) & 0xfffffff0); tdHeadP = bus_to_virt (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 = bus_to_virt (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)
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -