📄 usb_ohci.c
字号:
/* allocate the TDs */ /* note that td[0] was allocated in ep_add_ed */ for (i = 0; i < size; i++) { purb_priv->td[i] = td_alloc (dev); if (!purb_priv->td[i]) { purb_priv->length = i; urb_free_priv (purb_priv); err("sohci_submit_job: ENOMEM"); return -1; } } if (ed->state == ED_NEW || (ed->state & ED_DEL)) { urb_free_priv (purb_priv); err("sohci_submit_job: EINVAL"); return -1; } /* link the ed into a chain if is not already */ if (ed->state != ED_OPER) ep_link (ohci, ed); /* fill the TDs and link it to the ed */ td_submit_job(dev, pipe, buffer, transfer_len, setup, purb_priv, interval); return 0;}static inline int sohci_return_job(struct ohci *hc, urb_priv_t *urb){ struct ohci_regs *regs = hc->regs; switch (usb_pipetype (urb->pipe)) { case PIPE_INTERRUPT: /* implicitly requeued */ if (urb->dev->irq_handle && (urb->dev->irq_act_len = urb->actual_length)) { writel (OHCI_INTR_WDH, ®s->intrenable); readl (®s->intrenable); /* PCI posting flush */ urb->dev->irq_handle(urb->dev); writel (OHCI_INTR_WDH, ®s->intrdisable); readl (®s->intrdisable); /* PCI posting flush */ } urb->actual_length = 0; td_submit_job ( urb->dev, urb->pipe, urb->transfer_buffer, urb->transfer_buffer_length, NULL, urb, urb->interval); break; case PIPE_CONTROL: case PIPE_BULK: break; default: return 0; } return 1;}/*-------------------------------------------------------------------------*/#ifdef DEBUG/* tell us the current USB frame number */static int sohci_get_current_frame_number (struct usb_device *usb_dev){ ohci_t *ohci = &gohci; return m16_swap (ohci->hcca->frame_no);}#endif/*-------------------------------------------------------------------------* * 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;}/*-------------------------------------------------------------------------* * ED handling functions *-------------------------------------------------------------------------*//* link an ed into one of the HC chains */static int ep_link (ohci_t *ohci, ed_t *edi){ volatile ed_t *ed = edi; int int_branch; int i; int inter; int interval; int load; __u32 * ed_p; ed->state = ED_OPER; ed->int_interval = 0; switch (ed->type) { case PIPE_CONTROL: ed->hwNextED = 0; if (ohci->ed_controltail == NULL) { writel (ed, &ohci->regs->ed_controlhead); } else { ohci->ed_controltail->hwNextED = m32_swap ((unsigned long)ed); } 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, &ohci->regs->ed_bulkhead); } else { ohci->ed_bulktail->hwNextED = m32_swap ((unsigned long)ed); } 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) && (((ed_t *)ed_p)->int_interval >= interval); ed_p = &(((ed_t *)ed_p)->hwNextED)) inter = ep_rev (6, ((ed_t *)ed_p)->int_interval); ed->hwNextED = *ed_p; *ed_p = m32_swap((unsigned long)ed); } break; } return 0;}/*-------------------------------------------------------------------------*//* scan the periodic table to find and unlink this ED */static void periodic_unlink ( struct ohci *ohci, volatile struct ed *ed, unsigned index, unsigned period){ for (; index < NUM_INTS; index += period) { __u32 *ed_p = &ohci->hcca->int_table [index]; /* ED might have been unlinked through another path */ while (*ed_p != 0) { if (((struct ed *)m32_swap ((unsigned long)ed_p)) == ed) { *ed_p = ed->hwNextED; break; } ed_p = & (((struct ed *)m32_swap ((unsigned long)ed_p))->hwNextED); } }}/* 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 *edi){ volatile ed_t *ed = edi; int i; ed->hwINFO |= m32_swap (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 (m32_swap (*((__u32 *)&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 *)m32_swap (*((__u32 *)&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 (m32_swap (*((__u32 *)&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 *)m32_swap (*((__u32 *)&ed->hwNextED)))->ed_prev = ed->ed_prev; } break; case PIPE_INTERRUPT: periodic_unlink (ohci, ed, 0, 1); for (i = ed->int_branch; i < 32; i += ed->int_interval) ohci->ohci_int_load[i] -= ed->int_load; 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 long pipe, int interval, int load){ td_t *td; ed_t *ed_ret; volatile ed_t *ed; ed = ed_ret = &ohci_dev.ed[(usb_pipeendpoint (pipe) << 1) | (usb_pipecontrol (pipe)? 0: usb_pipeout (pipe))]; if ((ed->state & ED_DEL) || (ed->state & ED_URB_DEL)) { err("ep_add_ed: pending delete"); /* pending delete request */ return NULL; } if (ed->state == ED_NEW) { ed->hwINFO = m32_swap (OHCI_ED_SKIP); /* skip ed */ /* dummy td; end of td list for ed */ td = td_alloc (usb_dev); ed->hwTailP = m32_swap ((unsigned long)td); ed->hwHeadP = ed->hwTailP; ed->state = ED_UNLINK; ed->type = usb_pipetype (pipe); ohci_dev.ed_cnt++; } ed->hwINFO = m32_swap (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) << 16); if (ed->type == PIPE_INTERRUPT && ed->state == ED_UNLINK) { ed->int_period = interval; ed->int_load = load; } return ed_ret;}/*-------------------------------------------------------------------------* * TD handling functions *-------------------------------------------------------------------------*//* enqueue next TD for this URB (OHCI spec 5.2.8.2) */static void td_fill (ohci_t *ohci, unsigned int info, void *data, int len, struct usb_device *dev, int index, urb_priv_t *urb_priv){ volatile td_t *td, *td_pt;#ifdef OHCI_FILL_TRACE int i;#endif if (index > urb_priv->length) { err("index > length"); return; } /* 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] = (td_t *)(m32_swap (urb_priv->ed->hwTailP) & ~0xf); td->ed = urb_priv->ed; td->next_dl_td = NULL; td->index = index; td->data = (__u32)data;#ifdef OHCI_FILL_TRACE if ((usb_pipetype(urb_priv->pipe) == PIPE_BULK) && usb_pipeout(urb_priv->pipe)) { for (i = 0; i < len; i++) printf("td->data[%d] %#2x ",i, ((unsigned char *)td->data)[i]); printf("\n"); }#endif if (!len) data = 0; td->hwINFO = m32_swap (info); td->hwCBP = m32_swap ((unsigned long)data); if (data) td->hwBE = m32_swap ((unsigned long)(data + len - 1)); else td->hwBE = 0; td->hwNextTD = m32_swap ((unsigned long)td_pt); /* append to queue */ td->ed->hwTailP = td->hwNextTD;}/*-------------------------------------------------------------------------*//* prepare all TDs of a transfer */static void td_submit_job (struct usb_device *dev, unsigned long pipe, void *buffer, int transfer_len, struct devrequest *setup, urb_priv_t *urb, int interval){ ohci_t *ohci = &gohci; int data_len = transfer_len; void *data; 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(dev, usb_pipeendpoint(pipe), usb_pipeout(pipe))) { toggle = TD_T_TOGGLE; } else { toggle = TD_T_DATA0; usb_settoggle(dev, usb_pipeendpoint(pipe), usb_pipeout(pipe), 1); } urb->td_cnt = 0; if (data_len) data = buffer; else data = 0; switch (usb_pipetype (pipe)) { case PIPE_BULK: info = usb_pipeout (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, dev, cnt, urb); data += 4096; data_len -= 4096; cnt++; } info = usb_pipeout (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, dev, cnt, urb); cnt++; if (!ohci->sleeping) writel (OHCI_BLF, &ohci->regs->cmdstatus); /* start bulk list */ break; case PIPE_CONTROL: info = TD_CC | TD_DP_SETUP | TD_T_DATA0; td_fill (ohci, info, setup, 8, dev, cnt++, urb); if (data_len > 0) { info = usb_pipeout (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, dev, cnt++, urb); } info = usb_pipeout (pipe)? TD_CC | TD_DP_IN | TD_T_DATA1: TD_CC | TD_DP_OUT | TD_T_DATA1; td_fill (ohci, info, data, 0, dev, cnt++, urb); if (!ohci->sleeping) writel (OHCI_CLF, &ohci->regs->cmdstatus); /* start Control 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, dev, cnt++, urb); break; } if (urb->length != cnt) dbg("TD LENGTH %d != CNT %d", urb->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; urb_priv_t *lurb_priv = td->ed->purb; tdINFO = m32_swap (td->hwINFO); tdBE = m32_swap (td->hwBE); tdCBP = m32_swap (td->hwCBP); if (!(usb_pipetype (lurb_priv->pipe) == PIPE_CONTROL && ((td->index == 0) || (td->index == lurb_priv->length - 1)))) { if (tdBE != 0) { if (td->hwCBP == 0) lurb_priv->actual_length += tdBE - td->data + 1; else lurb_priv->actual_length += tdCBP - td->data; } }}/*-------------------------------------------------------------------------*/
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -