📄 usb-ohci.c
字号:
static int sohci_get_current_frame_number (struct usb_device *usb_dev) { ohci_t * ohci = usb_dev->bus->hcpriv; return le16_to_cpu (ohci->hcca.frame_no);}/*-------------------------------------------------------------------------*/struct usb_operations sohci_device_operations = { sohci_alloc_dev, sohci_free_dev, sohci_get_current_frame_number, sohci_submit_urb, sohci_unlink_urb};/*-------------------------------------------------------------------------* * ED handling functions *-------------------------------------------------------------------------*/ /* search for the right branch to insert an interrupt ed into the int tree * do some load ballancing; * returns the branch and * sets the interval to interval = 2^integer (ld (interval)) */static int ep_int_ballance (ohci_t * ohci, int interval, int load){ int i, branch = 0; /* search for the least loaded interrupt endpoint branch of all 32 branches */ for (i = 0; i < 32; i++) if (ohci->ohci_int_load [branch] > ohci->ohci_int_load [i]) branch = i; branch = branch % interval; for (i = branch; i < 32; i += interval) ohci->ohci_int_load [i] += load; return branch;}/*-------------------------------------------------------------------------*//* 2^int( ld (inter)) */static int ep_2_n_interval (int inter){ int i; for (i = 0; ((inter >> i) > 1 ) && (i < 5); i++); return 1 << i;}/*-------------------------------------------------------------------------*//* the int tree is a binary tree * in order to process it sequentially the indexes of the branches have to be mapped * the mapping reverses the bits of a word of num_bits length */ static int ep_rev (int num_bits, int word){ 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 */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 CTRL: ed->hwNextED = 0; if (ohci->ed_controltail == NULL) { writel (virt_to_bus (ed), &ohci->regs->ed_controlhead); } else { ohci->ed_controltail->hwNextED = cpu_to_le32 (virt_to_bus (ed)); } ed->ed_prev = ohci->ed_controltail; ohci->ed_controltail = edi; break; case BULK: ed->hwNextED = 0; if (ohci->ed_bulktail == NULL) { writel (virt_to_bus (ed), &ohci->regs->ed_bulkhead); } else { ohci->ed_bulktail->hwNextED = cpu_to_le32 (virt_to_bus (ed)); } ed->ed_prev = ohci->ed_bulktail; ohci->ed_bulktail = edi; break; case INT: 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) && (((ed_t *) bus_to_virt (le32_to_cpup (ed_p)))->int_interval >= interval); 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); ed->hwNextED = *ed_p; *ed_p = cpu_to_le32 (virt_to_bus (ed)); }#ifdef DEBUG ep_print_int_eds (ohci, "LINK_INT");#endif break; case ISO: ed->hwNextED = 0; ed->int_interval = 1; if (ohci->ed_isotail != NULL) { ohci->ed_isotail->hwNextED = cpu_to_le32 (virt_to_bus (ed)); 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 = &(((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); *ed_p = cpu_to_le32 (virt_to_bus (ed)); } 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 */static int ep_unlink (ohci_t * ohci, ed_t * ed) { int int_branch; int i; int inter; int interval; __u32 * ed_p; switch (ed->type) { case CTRL: if (ed->ed_prev == NULL) { 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 BULK: if (ed->ed_prev == NULL) { 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 INT: 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 ISO: 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 += inter) { inter = 1; 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; spin_lock (&usb_ed_lock); 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)) return NULL; /* pending delete request */ if (ed->state == ED_NEW) { ed->hwINFO = cpu_to_le32 (OHCI_ED_SKIP); /* skip ed */ OHCI_ALLOC (td, sizeof (*td)); /* dummy td; end of td list for ed */ if(!td) return NULL; /* out of memory */ 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 == INT && ed->state == ED_UNLINK) { ed->int_period = interval; ed->int_load = load; } spin_unlock(&usb_ed_lock); 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 of frame (SOF) 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); writel (OHCI_INTR_SF, &ohci->regs->intrstatus); writel (OHCI_INTR_SF, &ohci->regs->intrenable); /* enable sof interrupt */ frame = le16_to_cpu (ohci->hcca.frame_no) & 0x1; ed->ed_rm_list = ohci->ed_rm_list[frame]; ohci->ed_rm_list[frame] = ed; switch (ed->type) { case CTRL: /* stop CTRL list */ writel (ohci->hc_control &= ~(0x01 << 4), &ohci->regs->control); break; case BULK: /* stop BULK list */ writel (ohci->hc_control &= ~(0x01 << 5), &ohci->regs->control); break; }}/*-------------------------------------------------------------------------* * TD handling functions *-------------------------------------------------------------------------*//* prepare a TD */static void td_fill (unsigned int info, void * data, int len, urb_t * urb, int type, 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->index = index; td->urb = urb; td->hwINFO = cpu_to_le32 (info); td->type = type; if ((td->ed->type & 3) == 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; td->next_dl_td = NULL; //td_pt;}/*-------------------------------------------------------------------------*/ /* 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; urb_priv->td_cnt = 0; switch (usb_pipetype (urb->pipe)) { case PIPE_BULK: info = usb_pipeout (urb->pipe)? TD_CC | TD_DP_OUT | TD_T_TOGGLE: TD_CC | TD_DP_IN | TD_T_TOGGLE; while(data_len > 4096) { td_fill (info, data, 4096, urb, (cnt? 0: ST_ADDR) | ADD_LEN, cnt); data += 4096; data_len -= 4096; cnt++; } info = usb_pipeout (urb->pipe)? TD_CC | TD_DP_OUT | TD_T_TOGGLE: TD_CC | TD_R | TD_DP_IN | TD_T_TOGGLE; td_fill (info, data, data_len, urb, (cnt? 0: ST_ADDR) | ADD_LEN, 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 | TD_T_TOGGLE: TD_CC | TD_R | TD_DP_IN | TD_T_TOGGLE; td_fill (info, data, data_len, urb, ST_ADDR | ADD_LEN, cnt++); break; case PIPE_CONTROL: info = TD_CC | TD_DP_SETUP | TD_T_DATA0; td_fill (info, ctrl, 8, urb, ST_ADDR, 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, ADD_LEN, 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, 0, 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? 0: ST_ADDR) | ADD_LEN, cnt); } break; } if (urb_priv->length != cnt) dbg("TD LENGTH %d != CNT %d", urb_priv->length, cnt);}/*-------------------------------------------------------------------------* * Done List handling functions *-------------------------------------------------------------------------*/ /* 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) {
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -