📄 usb-uhci.c
字号:
}/*-------------------------------------------------------------------*/_static void clean_td_chain (uhci_t *s, uhci_desc_t *td){ struct list_head *p; uhci_desc_t *td1; if (!td) return; while ((p = td->horizontal.next) != &td->horizontal) { td1 = list_entry (p, uhci_desc_t, horizontal); delete_desc (s, td1); } delete_desc (s, td);}/*-------------------------------------------------------------------*/_static void fill_td (uhci_desc_t *td, int status, int info, __u32 buffer){ td->hw.td.status = cpu_to_le32(status); td->hw.td.info = cpu_to_le32(info); td->hw.td.buffer = cpu_to_le32(buffer);}/*-------------------------------------------------------------------*/// Removes ALL qhs in chain (paranoia!)_static void cleanup_skel (uhci_t *s){ unsigned int n; uhci_desc_t *td; dbg("cleanup_skel"); clean_descs(s,1); if (s->td32ms) { unlink_td(s,s->td32ms,1); delete_desc(s, s->td32ms); } for (n = 0; n < 8; n++) { td = s->int_chain[n]; clean_td_chain (s, td); } if (s->iso_td) { for (n = 0; n < 1024; n++) { td = s->iso_td[n]; clean_td_chain (s, td); } kfree (s->iso_td); } if (s->framelist) pci_free_consistent(s->uhci_pci, PAGE_SIZE, s->framelist, s->framelist_dma); 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, s->ls_control_chain); if (s->control_chain) delete_desc (s, s->control_chain); if (s->bulk_chain) delete_desc (s, s->bulk_chain); if (s->chain_end) delete_desc (s, s->chain_end); } if (s->desc_pool) { pci_pool_destroy(s->desc_pool); s->desc_pool = NULL; } 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 = pci_alloc_consistent(s->uhci_pci, PAGE_SIZE, &s->framelist_dma); if (!s->framelist) return -ENOMEM; memset (s->framelist, 0, 4096); dbg("creating descriptor pci_pool"); s->desc_pool = pci_pool_create("uhci_desc", s->uhci_pci, sizeof(uhci_desc_t), 16, 0, GFP_DMA | GFP_ATOMIC); if (!s->desc_pool) goto init_skel_cleanup; 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 if (alloc_td (s, &td, 0)) goto init_skel_cleanup; s->iso_td[n] = td; s->framelist[n] = cpu_to_le32((__u32) td->dma_addr); } dbg("allocating qh: chain_end"); if (alloc_qh (s, &qh)) goto init_skel_cleanup; s->chain_end = qh; if (alloc_td (s, &td, 0)) 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 &= cpu_to_le32(~UHCI_PTR_TERM); // remove TERM bit s->td1ms=td; dbg("allocating qh: bulk_chain"); if (alloc_qh (s, &qh)) goto init_skel_cleanup; insert_qh (s, s->chain_end, qh, 0); s->bulk_chain = qh; dbg("allocating qh: control_chain"); ret = alloc_qh (s, &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 set_qh_head(s->chain_end, s->control_chain->dma_addr | UHCI_PTR_QH | UHCI_PTR_TERM);#endif dbg("allocating qh: ls_control_chain"); if (alloc_qh (s, &qh)) 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; if (alloc_td (s, &td, 0)) goto init_skel_cleanup; s->int_chain[n] = td; if (n == 0) { set_td_link(s->int_chain[0], s->ls_control_chain->dma_addr | UHCI_PTR_QH); } else { set_td_link(s->int_chain[n], s->int_chain[0]->dma_addr); } } 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,le32_to_cpu(s->framelist[n])); if ((n&127)==127) ((uhci_desc_t*) s->iso_td[n])->hw.td.link = cpu_to_le32(s->int_chain[0]->dma_addr); else for (o = 1, m = 2; m <= 128; o++, m += m) if ((n & (m - 1)) == ((m - 1) / 2)) set_td_link(((uhci_desc_t*) s->iso_td[n]), s->int_chain[o]->dma_addr); } if (alloc_td (s, &td, 0)) goto init_skel_cleanup; fill_td (td, 0 * TD_CTRL_IOC, 0, 0); // generate 32ms interrupt (activated later) 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 (struct urb *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 dbg("uhci_submit_control start"); if (alloc_qh (s, &qh)) // alloc qh for this request return -ENOMEM; if (alloc_td (s, &td, UHCI_PTR_DEPTH * depth_first)) // get td for setup stage { 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), urb_priv->setup_packet_dma); 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; if (alloc_td (s, &td, UHCI_PTR_DEPTH * depth_first)) goto fail_unmap_enomem; if (pktsze > maxsze) pktsze = maxsze; destination ^= 1 << TD_TOKEN_TOGGLE; // toggle DATA0/1 // Status, pktsze bytes of data fill_td (td, status, destination | ((pktsze - 1) << 21), urb_priv->transfer_buffer_dma + (data - (char *)urb->transfer_buffer)); 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 */ if (alloc_td (s, &td, UHCI_PTR_DEPTH)) goto fail_unmap_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); queue_urb (s, urb); // queue before inserting in desc chain qh->hw.qh.element &= cpu_to_le32(~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;fail_unmap_enomem: delete_qh(s, qh); return -ENOMEM;}/*-------------------------------------------------------------------*/// 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 (struct urb *urb, struct urb *bulk_urb){ uhci_t *s = (uhci_t*) urb->dev->bus->hcpriv; urb_priv_t *urb_priv = urb->hcpriv, *upriv, *bpriv=NULL; uhci_desc_t *qh, *td, *nqh=NULL, *bqh=NULL, *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 if (usb_endpoint_halted (urb->dev, usb_pipeendpoint (pipe), usb_pipeout (pipe))) return -EPIPE; 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) { if (alloc_qh (s, &qh)) // get qh for this request return -ENOMEM; if (urb->transfer_flags & USB_QUEUE_BULK) { if (alloc_qh(s, &nqh)) // placeholder for clean unlink { delete_desc (s, 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) { if (alloc_qh (s, &bqh)) // "bottom" QH { if (!bulk_urb) { delete_desc(s, qh); delete_desc(s, nqh); } return -ENOMEM; } set_qh_element(bqh, UHCI_PTR_TERM); set_qh_head(bqh, nqh->dma_addr | 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; if (alloc_td (s, &td, UHCI_PTR_DEPTH * depth_first)) { 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, urb_priv->transfer_buffer_dma + (data - (char *)urb->transfer_buffer)); data += pktsze; len -= pktsze; // Use USB_ZERO_PACKET to finish bulk OUTs always with a zero length packet last = (len == 0 && (usb_pipein(pipe) || pktsze < maxsze || !(urb->transfer_flags & USB_ZERO_PACKET))); if (last) set_td_ioc(td); // 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); queue_urb_unlocked (s, urb); if (urb->transfer_flags & USB_QUEUE_BULK) set_qh_element(qh, first_td->dma_addr); else
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -