📄 usb-uhci.c
字号:
uhci_desc_t *qh, *td; dbg("init_skel"); s->framelist = (__u32 *) get_free_page (GFP_KERNEL); if (!s->framelist) return -ENOMEM; memset (s->framelist, 0, 4096); dbg("allocating iso desc pointer list"); s->iso_td = (uhci_desc_t **) kmalloc (1024 * sizeof (uhci_desc_t*), GFP_KERNEL); if (!s->iso_td) goto init_skel_cleanup; s->ls_control_chain = NULL; s->control_chain = NULL; s->bulk_chain = NULL; s->chain_end = NULL; dbg("allocating iso descs"); for (n = 0; n < 1024; n++) { // allocate skeleton iso/irq-tds ret = alloc_td (&td, 0); if (ret) goto init_skel_cleanup; s->iso_td[n] = td; s->framelist[n] = ((__u32) virt_to_bus (td)); } dbg("allocating qh: chain_end"); ret = alloc_qh (&qh); if (ret) goto init_skel_cleanup; s->chain_end = qh; ret = alloc_td (&td, 0); if (ret) goto init_skel_cleanup; fill_td (td, TD_CTRL_IOC, 0, 0); // generate 1ms interrupt insert_td (s, qh, td, 0); dbg("allocating qh: bulk_chain"); ret = alloc_qh (&qh); if (ret) goto init_skel_cleanup; insert_qh (s, s->chain_end, qh, 0); s->bulk_chain = qh; dbg("allocating qh: control_chain"); ret = alloc_qh (&qh); if (ret) goto init_skel_cleanup; insert_qh (s, s->bulk_chain, qh, 0); s->control_chain = qh;#ifdef CONFIG_USB_UHCI_HIGH_BANDWIDTH // disabled reclamation loop s->chain_end->hw.qh.head=virt_to_bus(s->control_chain) | UHCI_PTR_QH | UHCI_PTR_TERM;#endif dbg("allocating qh: ls_control_chain"); ret = alloc_qh (&qh); if (ret) goto init_skel_cleanup; insert_qh (s, s->control_chain, qh, 0); s->ls_control_chain = qh; for (n = 0; n < 8; n++) s->int_chain[n] = 0; dbg("allocating skeleton INT-TDs"); for (n = 0; n < 8; n++) { uhci_desc_t *td; alloc_td (&td, 0); if (!td) goto init_skel_cleanup; s->int_chain[n] = td; if (n == 0) { s->int_chain[0]->hw.td.link = virt_to_bus (s->ls_control_chain) | UHCI_PTR_QH; } else { s->int_chain[n]->hw.td.link = virt_to_bus (s->int_chain[0]); } } dbg("Linking skeleton INT-TDs"); for (n = 0; n < 1024; n++) { // link all iso-tds to the interrupt chains int m, o; dbg("framelist[%i]=%x",n,s->framelist[n]); if ((n&127)==127) ((uhci_desc_t*) s->iso_td[n])->hw.td.link = virt_to_bus(s->int_chain[0]); else for (o = 1, m = 2; m <= 128; o++, m += m) if ((n & (m - 1)) == ((m - 1) / 2)) ((uhci_desc_t*) s->iso_td[n])->hw.td.link = virt_to_bus (s->int_chain[o]); } mb(); //uhci_show_queue(s->control_chain); dbg("init_skel exit"); return 0; init_skel_cleanup: cleanup_skel (s); return -ENOMEM;}/*-------------------------------------------------------------------*/// LOW LEVEL STUFF// assembles QHs und TDs for control, bulk and iso/*-------------------------------------------------------------------*/_static int uhci_submit_control_urb (urb_t *urb){ uhci_desc_t *qh, *td; uhci_t *s = (uhci_t*) urb->dev->bus->hcpriv; urb_priv_t *urb_priv = urb->hcpriv; unsigned long destination, status; int maxsze = usb_maxpacket (urb->dev, urb->pipe, usb_pipeout (urb->pipe)); unsigned long len; char *data; int depth_first=USE_CTRL_DEPTH_FIRST; // UHCI descriptor chasing method if (!maxsze) { err("uhci_submit_control_urb: pipesize for pipe %x is zero", urb->pipe); return -EINVAL; } dbg("uhci_submit_control start"); alloc_qh (&qh); // alloc qh for this request if (!qh) return -ENOMEM; alloc_td (&td, UHCI_PTR_DEPTH * depth_first); // get td for setup stage if (!td) { delete_qh (s, qh); return -ENOMEM; } /* The "pipe" thing contains the destination in bits 8--18 */ destination = (urb->pipe & PIPE_DEVEP_MASK) | USB_PID_SETUP; /* 3 errors */ status = (urb->pipe & TD_CTRL_LS) | TD_CTRL_ACTIVE | (urb->transfer_flags & USB_DISABLE_SPD ? 0 : TD_CTRL_SPD) | (3 << 27); /* Build the TD for the control request, try forever, 8 bytes of data */ fill_td (td, status, destination | (7 << 21), virt_to_bus (urb->setup_packet)); insert_td (s, qh, td, 0); // queue 'setup stage'-td in qh#if 0 { char *sp=urb->setup_packet; dbg("SETUP to pipe %x: %x %x %x %x %x %x %x %x", urb->pipe, sp[0],sp[1],sp[2],sp[3],sp[4],sp[5],sp[6],sp[7]); } //uhci_show_td(td);#endif len = urb->transfer_buffer_length; data = urb->transfer_buffer; /* If direction is "send", change the frame from SETUP (0x2D) to OUT (0xE1). Else change it from SETUP to IN (0x69). */ destination = (urb->pipe & PIPE_DEVEP_MASK) | (usb_pipeout (urb->pipe)?USB_PID_OUT:USB_PID_IN); while (len > 0) { int pktsze = len; alloc_td (&td, UHCI_PTR_DEPTH * depth_first); if (!td) { delete_qh (s, qh); return -ENOMEM; } if (pktsze > maxsze) pktsze = maxsze; destination ^= 1 << TD_TOKEN_TOGGLE; // toggle DATA0/1 fill_td (td, status, destination | ((pktsze - 1) << 21), virt_to_bus (data)); // Status, pktsze bytes of data insert_td (s, qh, td, UHCI_PTR_DEPTH * depth_first); // queue 'data stage'-td in qh data += pktsze; len -= pktsze; } /* Build the final TD for control status */ /* It's only IN if the pipe is out AND we aren't expecting data */ destination &= ~UHCI_PID; if (usb_pipeout (urb->pipe) || (urb->transfer_buffer_length == 0)) destination |= USB_PID_IN; else destination |= USB_PID_OUT; destination |= 1 << TD_TOKEN_TOGGLE; /* End in Data1 */ alloc_td (&td, UHCI_PTR_DEPTH); if (!td) { delete_qh (s, qh); return -ENOMEM; } status &=~TD_CTRL_SPD; /* no limit on errors on final packet , 0 bytes of data */ fill_td (td, status | TD_CTRL_IOC, destination | (UHCI_NULL_DATA_SIZE << 21), 0); insert_td (s, qh, td, UHCI_PTR_DEPTH * depth_first); // queue status td list_add (&qh->desc_list, &urb_priv->desc_list); urb->status = -EINPROGRESS; queue_urb (s, urb); // queue before inserting in desc chain qh->hw.qh.element &= ~UHCI_PTR_TERM; //uhci_show_queue(qh); /* Start it up... put low speed first */ if (urb->pipe & TD_CTRL_LS) insert_qh (s, s->control_chain, qh, 0); else insert_qh (s, s->bulk_chain, qh, 0); dbg("uhci_submit_control end"); return 0;}/*-------------------------------------------------------------------*/// For queued bulk transfers, two additional QH helpers are allocated (nqh, bqh)// Due to the linking with other bulk urbs, it has to be locked with urb_list_lock!_static int uhci_submit_bulk_urb (urb_t *urb, urb_t *bulk_urb){ uhci_t *s = (uhci_t*) urb->dev->bus->hcpriv; urb_priv_t *urb_priv = urb->hcpriv; uhci_desc_t *qh, *td, *nqh, *bqh; unsigned long destination, status; char *data; unsigned int pipe = urb->pipe; int maxsze = usb_maxpacket (urb->dev, pipe, usb_pipeout (pipe)); int info, len; int depth_first=USE_BULK_DEPTH_FIRST; // UHCI descriptor chasing method urb_priv_t *upriv, *bpriv; if (usb_endpoint_halted (urb->dev, usb_pipeendpoint (pipe), usb_pipeout (pipe))) return -EPIPE; if (urb->transfer_buffer_length < 0) { err("Negative transfer length in submit_bulk"); return -EINVAL; } if (!maxsze) return -EMSGSIZE; queue_dbg("uhci_submit_bulk_urb: urb %p, old %p, pipe %08x, len %i", urb,bulk_urb,urb->pipe,urb->transfer_buffer_length); upriv=(urb_priv_t*)urb->hcpriv; if (!bulk_urb) { alloc_qh (&qh); // get qh for this request if (!qh) return -ENOMEM; if (urb->transfer_flags & USB_QUEUE_BULK) { alloc_qh(&nqh); // placeholder for clean unlink if (!nqh) { delete_desc (qh); return -ENOMEM; } upriv->next_qh = nqh; queue_dbg("new next qh %p",nqh); } } else { bpriv = (urb_priv_t*)bulk_urb->hcpriv; qh = bpriv->bottom_qh; // re-use bottom qh and next qh nqh = bpriv->next_qh; upriv->next_qh=nqh; bpriv->next_queued_urb=urb; upriv->prev_queued_urb=bulk_urb; } queue_dbg("uhci_submit_bulk: qh=%p, nqh=%p\n",bqh,nqh); if (urb->transfer_flags & USB_QUEUE_BULK) { alloc_qh (&bqh); // "bottom" QH, if (!bqh) { if (!bulk_urb) { delete_desc(qh); delete_desc(nqh); } return -ENOMEM; } bqh->hw.qh.element = UHCI_PTR_TERM; bqh->hw.qh.element = virt_to_bus(nqh)|UHCI_PTR_QH; upriv->bottom_qh = bqh; queue_dbg("uhci_submit_bulk: new bqh %p\n",bqh); } /* The "pipe" thing contains the destination in bits 8--18. */ destination = (pipe & PIPE_DEVEP_MASK) | usb_packetid (pipe); /* 3 errors */ status = (pipe & TD_CTRL_LS) | TD_CTRL_ACTIVE | ((urb->transfer_flags & USB_DISABLE_SPD) ? 0 : TD_CTRL_SPD) | (3 << 27); /* Build the TDs for the bulk request */ len = urb->transfer_buffer_length; data = urb->transfer_buffer; do { // TBD: Really allow zero-length packets? int pktsze = len; alloc_td (&td, UHCI_PTR_DEPTH * depth_first); if (!td) { delete_qh (s, qh); return -ENOMEM; } if (pktsze > maxsze) pktsze = maxsze; // pktsze bytes of data info = destination | (((pktsze - 1)&UHCI_NULL_DATA_SIZE) << 21) | (usb_gettoggle (urb->dev, usb_pipeendpoint (pipe), usb_pipeout (pipe)) << TD_TOKEN_TOGGLE); fill_td (td, status, info, virt_to_bus (data)); data += pktsze; len -= pktsze; if (!len) td->hw.td.status |= TD_CTRL_IOC; // last one generates INT insert_td (s, qh, td, UHCI_PTR_DEPTH * depth_first); usb_dotoggle (urb->dev, usb_pipeendpoint (pipe), usb_pipeout (pipe)); } while (len > 0); list_add (&qh->desc_list, &urb_priv->desc_list); if (urb->transfer_flags & USB_QUEUE_BULK) { qh->hw.qh.element&=~UHCI_PTR_TERM; append_qh(s, td, bqh, UHCI_PTR_DEPTH * depth_first); } urb->status = -EINPROGRESS; queue_urb_unlocked (s, urb); qh->hw.qh.element &= ~UHCI_PTR_TERM; if (!bulk_urb) { if (urb->transfer_flags & USB_QUEUE_BULK) { spin_lock (&s->td_lock); // both QHs in one go insert_qh (s, s->chain_end, qh, 0); // Main QH insert_qh (s, s->chain_end, nqh, 0); // Helper QH spin_unlock (&s->td_lock); } else insert_qh (s, s->chain_end, qh, 0); } //uhci_show_queue(s->bulk_chain); //dbg("uhci_submit_bulk_urb: exit\n"); return 0;}/*-------------------------------------------------------------------*/_static void uhci_clean_iso_step1(uhci_t *s, urb_priv_t *urb_priv){ struct list_head *p; uhci_desc_t *td; for (p = urb_priv->desc_list.next; p != &urb_priv->desc_list; p = p->next) { td = list_entry (p, uhci_desc_t, desc_list); unlink_td (s, td, 1); }}/*-------------------------------------------------------------------*/_static void uhci_clean_iso_step2(uhci_t *s, urb_priv_t *urb_priv){ struct list_head *p; uhci_desc_t *td; while ((p = urb_priv->desc_list.next) != &urb_priv->desc_list) { td = list_entry (p, uhci_desc_t, desc_list); list_del (p); delete_desc (td); }}/*-------------------------------------------------------------------*/// mode: 0: unlink + no deletion mark, 1: regular (unlink/delete-mark), 2: don't unlink// looks a bit complicated because of all the bulk queueing goodies_static void uhci_clean_transfer (uhci_t *s, urb_t *urb, uhci_desc_t *qh, int mode){ uhci_desc_t *bqh, *nqh, *prevqh; int now; urb_priv_t *priv=(urb_priv_t*)urb->hcpriv; now=UHCI_GET_CURRENT_FRAME(s); dbg("clean transfer urb %p, qh %p, mode %i",urb,qh,mode); bqh=priv->bottom_qh; if (!priv->next_queued_urb) { // no more appended bulk queues if (mode != 2) unlink_qh (s, qh); if (priv->prev_queued_urb) { urb_priv_t* ppriv=(urb_priv_t*)priv->prev_queued_urb->hcpriv; ppriv->bottom_qh = priv->bottom_qh; ppriv->next_queued_urb = NULL; } else if (bqh) { // queue dead nqh=priv->next_qh; if (mode != 2) unlink_qh(s, nqh); if (mode) { nqh->last_used = bqh->last_used = now; list_add_tail (&nqh->horizontal, &s->free_desc); list_add_tail (&bqh->horizontal, &s->free_desc); } } } else { // there are queued urbs following urb_t *nurb; unsigned long flags; nurb=priv->next_queued_urb; spin_lock_irqsave (&s->qh_lock, flags); if (!priv->prev_queued_urb) { // top if (mode !=2) { prevqh = list_entry (qh->horizontal.prev, uhci_desc_t, horizontal); prevqh->hw.qh.head = virt_to_bus(bqh) | UHCI_PTR_QH; queue_dbg ("TOP relink of %p to %p-%p",qh,prevqh,bqh); list_del (&qh->horizontal); list_add (&bqh->horizontal, &prevqh->horizontal); } } else { //intermediate urb_priv_t* ppriv=(urb_priv_t*)priv->prev_queued_urb->hcpriv; uhci_desc_t * bnqh; bnqh=list_entry (&((urb_priv_t*)(nurb->hcpriv))->desc_list.next, uhci_desc_t, desc_list); ppriv->bottom_qh=bnqh; ppriv->next_queued_urb=nurb; if (mode!=2) { prevqh = list_entry (ppriv->desc_list.next, uhci_desc_t, desc_list); prevqh->hw.qh.head = virt_to_bus(bqh) | UHCI_PTR_QH; queue_dbg ("IM relink of %p to %p-%p",qh,prevqh,bqh); } } mb(); spin_unlock_irqrestore (&s->qh_lock, flags); ((urb_priv_t*)nurb->hcpriv)->prev_queued_urb=priv->prev_queued_urb;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -