📄 usb-uhci.c
字号:
unlink_td(s,s->td32ms,1); delete_desc(s->td32ms); } for (n = 0; n < 8; n++) { td = s->int_chain[n]; clean_td_chain (td); } if (s->iso_td) { for (n = 0; n < 1024; n++) { td = s->iso_td[n]; clean_td_chain (td); } kfree (s->iso_td); } if (s->framelist) free_page ((unsigned long) s->framelist); if (s->control_chain) { // completed init_skel? struct list_head *p; uhci_desc_t *qh, *qh1; qh = s->control_chain; while ((p = qh->horizontal.next) != &qh->horizontal) { qh1 = list_entry (p, uhci_desc_t, horizontal); delete_qh (s, qh1); } delete_qh (s, qh); } else { if (s->ls_control_chain) delete_desc (s->ls_control_chain); if (s->control_chain) delete_desc(s->control_chain); if (s->bulk_chain) delete_desc (s->bulk_chain); if (s->chain_end) delete_desc (s->chain_end); } dbg("cleanup_skel finished"); }/*-------------------------------------------------------------------*/// allocates framelist and qh-skeletons// only HW-links provide continous linking, SW-links stay in their domain (ISO/INT)_static int init_skel (uhci_t *s){ int n, ret; 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, 0 * TD_CTRL_IOC, 0, 0); // generate 1ms interrupt (enabled on demand) insert_td (s, qh, td, 0); qh->hw.qh.element &= ~UHCI_PTR_TERM; // remove TERM bit s->td1ms=td; 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]); } ret = alloc_td (&td, 0); if (ret) goto init_skel_cleanup; fill_td (td, 0 * TD_CTRL_IOC, 0, 0); // generate 32ms interrupt s->td32ms=td; insert_td_horizontal (s, s->int_chain[5], td); 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, *first_td=NULL; unsigned long destination, status; char *data; unsigned int pipe = urb->pipe; int maxsze = usb_maxpacket (urb->dev, pipe, usb_pipeout (pipe)); int info, len, last; int depth_first=USE_BULK_DEPTH_FIRST; // UHCI descriptor chasing method urb_priv_t *upriv, *bpriv=NULL; 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; upriv->prev_queued_urb=bulk_urb; } 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.head = virt_to_bus(nqh) | UHCI_PTR_QH; // element upriv->bottom_qh = bqh; } queue_dbg("uhci_submit_bulk: qh %p bqh %p nqh %p",qh, bqh, nqh); /* 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; last = (len == 0 && (usb_pipein(pipe) || pktsze < maxsze || !(urb->transfer_flags & USB_DISABLE_SPD))); if (last) td->hw.td.status |= TD_CTRL_IOC; // last one generates INT insert_td (s, qh, td, UHCI_PTR_DEPTH * depth_first); if (!first_td) first_td=td; usb_dotoggle (urb->dev, usb_pipeendpoint (pipe), usb_pipeout (pipe)); } while (!last); if (bulk_urb && bpriv) // everything went OK, link with old bulk URB bpriv->next_queued_urb=urb; list_add (&qh->desc_list, &urb_priv->desc_list); if (urb->transfer_flags & USB_QUEUE_BULK) append_qh(s, td, bqh, UHCI_PTR_DEPTH * depth_first); urb->status = -EINPROGRESS; queue_urb_unlocked (s, urb); if (urb->transfer_flags & USB_QUEUE_BULK) qh->hw.qh.element = virt_to_bus (first_td); else qh->hw.qh.element &= ~UHCI_PTR_TERM; // arm QH if (!bulk_urb) { // new bulk queue 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); }}/*-------------------------------------------------------------------*/
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -