📄 usb-ohci.c
字号:
{ int i, wout = 0; for (i = 0; i < num_bits; i++) wout |= (((word >> i) & 1) << (num_bits - i - 1)); return wout;}/*-------------------------------------------------------------------------*//* link an ed into one of the HC chains *//*********************************************************************************************************** Function name: ** Descriptions: ** Input:** Output :** Created by:** Created Date: **-------------------------------------------------------------------------------------------------------** Modified by:** Modified Date: **------------------------------------------------------------------------------------------------------********************************************************************************************************/static int ep_link (ohci_t * ohci, ed_t * edi){ int int_branch; int i; int inter; int interval; int load; __u32 * ed_p; volatile ed_t * ed = edi; ed->state = ED_OPER; switch (ed->type) { case PIPE_CONTROL: ed->hwNextED = 0; if (ohci->ed_controltail == NULL) { writel (ed->dma, &ohci->regs->ed_controlhead); } else { ohci->ed_controltail->hwNextED = cpu_to_le32 (ed->dma); } ed->ed_prev = ohci->ed_controltail; if (!ohci->ed_controltail && !ohci->ed_rm_list[0] && !ohci->ed_rm_list[1] && !ohci->sleeping) { ohci->hc_control |= OHCI_CTRL_CLE; writel (ohci->hc_control, &ohci->regs->control); } ohci->ed_controltail = edi; break; case PIPE_BULK: ed->hwNextED = 0; if (ohci->ed_bulktail == NULL) { writel (ed->dma, &ohci->regs->ed_bulkhead); } else { ohci->ed_bulktail->hwNextED = cpu_to_le32 (ed->dma); } ed->ed_prev = ohci->ed_bulktail; if (!ohci->ed_bulktail && !ohci->ed_rm_list[0] && !ohci->ed_rm_list[1] && !ohci->sleeping) { ohci->hc_control |= OHCI_CTRL_BLE; writel (ohci->hc_control, &ohci->regs->control); } ohci->ed_bulktail = edi; break; case PIPE_INTERRUPT: load = ed->int_load; interval = ep_2_n_interval (ed->int_period); ed->int_interval = interval; int_branch = ep_int_ballance (ohci, interval, load); ed->int_branch = int_branch; for (i = 0; i < ep_rev (6, interval); i += inter) { inter = 1; for (ed_p = &(ohci->hcca->int_table[ep_rev (5, i) + int_branch]); (*ed_p != 0) && ((dma_to_ed (ohci, le32_to_cpup (ed_p)))->int_interval >= interval); ed_p = &((dma_to_ed (ohci, le32_to_cpup (ed_p)))->hwNextED)) inter = ep_rev (6, (dma_to_ed (ohci, le32_to_cpup (ed_p)))->int_interval); ed->hwNextED = *ed_p; *ed_p = cpu_to_le32 (ed->dma); }#ifdef DEBUG ep_print_int_eds (ohci, "LINK_INT");#endif break; case PIPE_ISOCHRONOUS: ed->hwNextED = 0; ed->int_interval = 1; if (ohci->ed_isotail != NULL) { ohci->ed_isotail->hwNextED = cpu_to_le32 (ed->dma); ed->ed_prev = ohci->ed_isotail; } else { for ( i = 0; i < 32; i += inter) { inter = 1; for (ed_p = &(ohci->hcca->int_table[ep_rev (5, i)]); *ed_p != 0; ed_p = &((dma_to_ed (ohci, le32_to_cpup (ed_p)))->hwNextED)) inter = ep_rev (6, (dma_to_ed (ohci, le32_to_cpup (ed_p)))->int_interval); *ed_p = cpu_to_le32 (ed->dma); } ed->ed_prev = NULL; } ohci->ed_isotail = edi; #ifdef DEBUG ep_print_int_eds (ohci, "LINK_ISO");#endif break; } 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 *//*********************************************************************************************************** Function name: ** Descriptions: ** Input:** Output :** Created by:** Created Date: **-------------------------------------------------------------------------------------------------------** Modified by:** Modified Date: **------------------------------------------------------------------------------------------------------********************************************************************************************************/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 { (dma_to_ed (ohci, 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 { (dma_to_ed (ohci, 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 = &((dma_to_ed (ohci, le32_to_cpup (ed_p)))->hwNextED), inter = ep_rev (6, (dma_to_ed (ohci, le32_to_cpup (ed_p)))->int_interval)) { if((dma_to_ed (ohci, 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) (dma_to_ed (ohci, 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 = &((dma_to_ed (ohci, le32_to_cpup (ed_p)))->hwNextED)) { // inter = ep_rev (6, (dma_to_ed (ohci, le32_to_cpup (ed_p)))->int_interval); if((dma_to_ed (ohci, 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, int mem_flags){ 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, SLAB_ATOMIC); /* hash the ed for later reverse mapping */ if (!td || !hash_add_ed (ohci, (ed_t *)ed)) { /* out of memory */ if (td) td_free(ohci, td); spin_unlock_irqrestore (&usb_ed_lock, flags); return NULL; } ed->hwTailP = cpu_to_le32 (td->td_dma); 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 *//*********************************************************************************************************** Function name: ** Descriptions: ** Input:** Output :** Created by:** Created Date: **-------------------------------------------------------------------------------------------------------** Modified by:** Modified Date: **------------------------------------------------------------------------------------------------------********************************************************************************************************/ 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 && !ohci->sleeping) { /* enable SOF interrupt */ writel (OHCI_INTR_SF, &ohci->regs->intrstatus); writel (OHCI_INTR_SF, &ohci->regs->intrenable); }}/*-------------------------------------------------------------------------* * TD handling functions *-------------------------------------------------------------------------*//* enqueue next TD for this URB (OHCI spec 5.2.8.2) */static voidtd_fill (ohci_t * ohci, unsigned int info, dma_addr_t 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; }// BUSHI//if (data & (1 << 20)) panic("td_fill: A20 = 1: %08x\n", data); /* use this td as the next dummy */ td_pt = urb_priv->td [index]; td_pt->hwNextTD = 0; /* fill the old dummy TD */ td = urb_priv->td [index] = dma_to_td (ohci, le32_to_cpup (&urb_priv->ed->hwTailP) & ~0xf); td->ed = urb_priv->ed; td->next_dl_td = NULL; td->index = index; td->urb = urb; td->data_dma = data; if (!len) data = 0; td->hwINFO = cpu_to_le32 (info); if ((td->ed->type) == PIPE_ISOCHRONOUS) { td->hwCBP = cpu_to_le32 (data & 0xFFFFF000); td->ed->last_iso = info & 0xffff; } else { td->hwCBP = cpu_to_le32 (data); } if (data) td->hwBE = cpu_to_le32 (data + len - 1); else td->hwBE = 0; td->hwNextTD = cpu_to_le32 (td_pt->td_dma); td->hwPSW [0] = cpu_to_le16 ((data & 0x0FFF) | 0xE000); /* append to queue */// BUSHI wmb(); td->ed->hwTailP = td->hwNextTD;}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -