/* * Universal Host Controller Interface driver for USB (take II). * * (c) 1999-2001 Georg Acher, acher@in.tum.de (executive slave) (base guitar) * Deti Fliegl, deti@fliegl.de (executive slave) (lead voice) * Thomas Sailer, sailer@ife.ee.ethz.ch (chief consultant) (cheer leader) * Roman Weissgaerber, weissg@vienna.at (virt root hub) (studio porter) * (c) 2000 Yggdrasil Computing, Inc. (port of new PCI interface support * from usb-ohci.c by Adam Richter, adam@yggdrasil.com). * (C) 2000 David Brownell, david-b@pacbell.net (usb-ohci.c) * * HW-initalization based on material of * * (C) Copyright 1999 Linus Torvalds * (C) Copyright 1999 Johannes Erdfelt * (C) Copyright 1999 Randy Dunlap * (C) Copyright 1999 Gregory P. Smith * * $Id: usb-uhci.c,v 1.268 2001/08/29 14:08:43 acher Exp $ */ #include #include #include #include #include #include #include #include #include #include #include #include /* for in_interrupt() */ #include #include #include #include #include #include #include /* This enables more detailed sanity checks in submit_iso */ //#define ISO_SANITY_CHECK /* This enables debug printks */ #define DEBUG /* This enables all symbols to be exported, to ease debugging oopses */ //#define DEBUG_SYMBOLS /* This enables an extra UHCI slab for memory debugging */ #define DEBUG_SLAB #define VERSTR "$Revision: 1.268 $ time " __TIME__ " " __DATE__ #include #include "usb-uhci.h" #include "usb-uhci-debug.h" /* * Version Information */ #define DRIVER_VERSION "v1.268" #define DRIVER_AUTHOR "Georg Acher, Deti Fliegl, Thomas Sailer, Roman Weissgaerber" #define DRIVER_DESC "USB Universal Host Controller Interface driver" #undef DEBUG #undef dbg #define dbg(format, arg...) do {} while (0) #define DEBUG_SYMBOLS #ifdef DEBUG_SYMBOLS #define _static #else #define _static static #endif #define queue_dbg dbg //err #define async_dbg dbg //err #ifdef DEBUG_SLAB static kmem_cache_t *urb_priv_kmem; #endif #define SLAB_FLAG (in_interrupt ()? SLAB_ATOMIC : SLAB_KERNEL) #define KMALLOC_FLAG (in_interrupt ()? GFP_ATOMIC : GFP_KERNEL) /* CONFIG_USB_UHCI_HIGH_BANDWITH turns on Full Speed Bandwidth * Reclamation: feature that puts loop on descriptor loop when * there's some transfer going on. With FSBR, USB performance * is optimal, but PCI can be slowed down up-to 5 times, slowing down * system performance (eg. framebuffer devices). */ #define CONFIG_USB_UHCI_HIGH_BANDWIDTH /* *_DEPTH_FIRST puts descriptor in depth-first mode. This has * somehow similar effect to FSBR (higher speed), but does not * slow PCI down. OTOH USB performace is slightly slower than * in FSBR case and single device could hog whole USB, starving * other devices. */ #define USE_CTRL_DEPTH_FIRST 0 // 0: Breadth first, 1: Depth first #define USE_BULK_DEPTH_FIRST 0 // 0: Breadth first, 1: Depth first /* Turning off both CONFIG_USB_UHCI_HIGH_BANDWITH and *_DEPTH_FIRST * will lead to <64KB/sec performance over USB for bulk transfers targeting * one device's endpoint. You probably do not want to do that. */ // stop bandwidth reclamation after (roughly) 50ms #define IDLE_TIMEOUT (HZ/20) // Suppress HC interrupt error messages for 5s #define ERROR_SUPPRESSION_TIME (HZ*5) _static int rh_submit_urb (urb_t *urb); _static int rh_unlink_urb (urb_t *urb); _static int delete_qh (uhci_t *s, uhci_desc_t *qh); _static int process_transfer (uhci_t *s, urb_t *urb, int mode); _static int process_interrupt (uhci_t *s, urb_t *urb); _static int process_iso (uhci_t *s, urb_t *urb, int force); // How much URBs with ->next are walked #define MAX_NEXT_COUNT 2048 static uhci_t *devs = NULL; /* used by userspace UHCI data structure dumper */ uhci_t **uhci_devices = &devs; /*-------------------------------------------------------------------*/ // Cleans up collected QHs, but not more than 100 in one go void clean_descs(uhci_t *s, int force) { struct list_head *q; uhci_desc_t *qh; int now=UHCI_GET_CURRENT_FRAME(s), n=0; q=s->free_desc.prev; while (q != &s->free_desc && (force || n<100)) { qh = list_entry (q, uhci_desc_t, horizontal); q=qh->horizontal.prev; if ((qh->last_used!=now) || force) delete_qh(s,qh); n++; } } /*-------------------------------------------------------------------*/ _static void uhci_switch_timer_int(uhci_t *s) { if (!list_empty(&s->urb_unlinked)) set_td_ioc(s->td1ms); else clr_td_ioc(s->td1ms); if (s->timeout_urbs) set_td_ioc(s->td32ms); else clr_td_ioc(s->td32ms); wmb(); } /*-------------------------------------------------------------------*/ #ifdef CONFIG_USB_UHCI_HIGH_BANDWIDTH _static void enable_desc_loop(uhci_t *s, urb_t *urb) { int flags; if (urb->transfer_flags & USB_NO_FSBR) return; spin_lock_irqsave (&s->qh_lock, flags); s->chain_end->hw.qh.head&=cpu_to_le32(~UHCI_PTR_TERM); mb(); s->loop_usage++; ((urb_priv_t*)urb->hcpriv)->use_loop=1; spin_unlock_irqrestore (&s->qh_lock, flags); } /*-------------------------------------------------------------------*/ _static void disable_desc_loop(uhci_t *s, urb_t *urb) { int flags; if (urb->transfer_flags & USB_NO_FSBR) return; spin_lock_irqsave (&s->qh_lock, flags); if (((urb_priv_t*)urb->hcpriv)->use_loop) { s->loop_usage--; if (!s->loop_usage) { s->chain_end->hw.qh.head|=cpu_to_le32(UHCI_PTR_TERM); mb(); } ((urb_priv_t*)urb->hcpriv)->use_loop=0; } spin_unlock_irqrestore (&s->qh_lock, flags); } #endif /*-------------------------------------------------------------------*/ _static void queue_urb_unlocked (uhci_t *s, urb_t *urb) { struct list_head *p=&urb->urb_list; #ifdef CONFIG_USB_UHCI_HIGH_BANDWIDTH { int type; type=usb_pipetype (urb->pipe); if ((type == PIPE_BULK) || (type == PIPE_CONTROL)) enable_desc_loop(s, urb); } #endif urb->status = -EINPROGRESS; ((urb_priv_t*)urb->hcpriv)->started=jiffies; list_add (p, &s->urb_list); if (urb->timeout) s->timeout_urbs++; uhci_switch_timer_int(s); } /*-------------------------------------------------------------------*/ _static void queue_urb (uhci_t *s, urb_t *urb) { unsigned long flags=0; spin_lock_irqsave (&s->urb_list_lock, flags); queue_urb_unlocked(s,urb); spin_unlock_irqrestore (&s->urb_list_lock, flags); } /*-------------------------------------------------------------------*/ _static void dequeue_urb (uhci_t *s, urb_t *urb) { #ifdef CONFIG_USB_UHCI_HIGH_BANDWIDTH int type; type=usb_pipetype (urb->pipe); if ((type == PIPE_BULK) || (type == PIPE_CONTROL)) disable_desc_loop(s, urb); #endif list_del (&urb->urb_list); if (urb->timeout && s->timeout_urbs) s->timeout_urbs--; } /*-------------------------------------------------------------------*/ _static int alloc_td (uhci_t *s, uhci_desc_t ** new, int flags) { dma_addr_t dma_handle; *new = pci_pool_alloc(s->desc_pool, GFP_DMA | GFP_ATOMIC, &dma_handle); if (!*new) return -ENOMEM; memset (*new, 0, sizeof (uhci_desc_t)); (*new)->dma_addr = dma_handle; set_td_link((*new), UHCI_PTR_TERM | (flags & UHCI_PTR_BITS)); // last by default (*new)->type = TD_TYPE; mb(); INIT_LIST_HEAD (&(*new)->vertical); INIT_LIST_HEAD (&(*new)->horizontal); return 0; } /*-------------------------------------------------------------------*/ // append a qh to td.link physically, the SW linkage is not affected _static void append_qh(uhci_t *s, uhci_desc_t *td, uhci_desc_t* qh, int flags) { unsigned long xxx; spin_lock_irqsave (&s->td_lock, xxx); set_td_link(td, qh->dma_addr | (flags & UHCI_PTR_DEPTH) | UHCI_PTR_QH); mb(); spin_unlock_irqrestore (&s->td_lock, xxx); } /*-------------------------------------------------------------------*/ /* insert td at last position in td-list of qh (vertical) */ _static int insert_td (uhci_t *s, uhci_desc_t *qh, uhci_desc_t* new, int flags) { uhci_desc_t *prev; unsigned long xxx; spin_lock_irqsave (&s->td_lock, xxx); list_add_tail (&new->vertical, &qh->vertical); prev = list_entry (new->vertical.prev, uhci_desc_t, vertical); if (qh == prev ) { // virgin qh without any tds set_qh_element(qh, new->dma_addr | UHCI_PTR_TERM); } else { // already tds inserted, implicitely remove TERM bit of prev set_td_link(prev, new->dma_addr | (flags & UHCI_PTR_DEPTH)); } mb(); spin_unlock_irqrestore (&s->td_lock, xxx); return 0; } /*-------------------------------------------------------------------*/ /* insert new_td after td (horizontal) */ _static int insert_td_horizontal (uhci_t *s, uhci_desc_t *td, uhci_desc_t* new) { uhci_desc_t *next; unsigned long flags; spin_lock_irqsave (&s->td_lock, flags); next = list_entry (td->horizontal.next, uhci_desc_t, horizontal); list_add (&new->horizontal, &td->horizontal); new->hw.td.link = td->hw.td.link; set_td_link(td, new->dma_addr); mb(); spin_unlock_irqrestore (&s->td_lock, flags); return 0; } /*-------------------------------------------------------------------*/ _static int unlink_td (uhci_t *s, uhci_desc_t *element, int phys_unlink) { uhci_desc_t *next, *prev; int dir = 0; unsigned long flags; spin_lock_irqsave (&s->td_lock, flags); next = list_entry (element->vertical.next, uhci_desc_t, vertical); if (next == element) { dir = 1; prev = list_entry (element->horizontal.prev, uhci_desc_t, horizontal); } else prev = list_entry (element->vertical.prev, uhci_desc_t, vertical); if (phys_unlink) { // really remove HW linking if (prev->type == TD_TYPE) prev->hw.td.link = element->hw.td.link; else prev->hw.qh.element = element->hw.td.link; } mb (); if (dir == 0) list_del (&element->vertical); else list_del (&element->horizontal); spin_unlock_irqrestore (&s->td_lock, flags); return 0; } /*-------------------------------------------------------------------*/ _static int delete_desc (uhci_t *s, uhci_desc_t *element) { pci_pool_free(s->desc_pool, element, element->dma_addr); return 0; } /*-------------------------------------------------------------------*/ // Allocates qh element _static int alloc_qh (uhci_t *s, uhci_desc_t ** new) { dma_addr_t dma_handle; *new = pci_pool_alloc(s->desc_pool, GFP_DMA | GFP_ATOMIC, &dma_handle); if (!*new) return -ENOMEM; memset (*new, 0, sizeof (uhci_desc_t)); (*new)->dma_addr = dma_handle; set_qh_head(*new, UHCI_PTR_TERM); set_qh_element(*new, UHCI_PTR_TERM); (*new)->type = QH_TYPE; mb(); INIT_LIST_HEAD (&(*new)->horizontal); INIT_LIST_HEAD (&(*new)->vertical); dbg("Allocated qh @ %p", *new); return 0; } /*-------------------------------------------------------------------*/ // inserts new qh before/after the qh at pos // flags: 0: insert before pos, 1: insert after pos (for low speed transfers) _static int insert_qh (uhci_t *s, uhci_desc_t *pos, uhci_desc_t *new, int order) { uhci_desc_t *old; unsigned long flags; spin_lock_irqsave (&s->qh_lock, flags); if (!order) { // (OLD) (POS) -> (OLD) (NEW) (POS) old = list_entry (pos->horizontal.prev, uhci_desc_t, horizontal); list_add_tail (&new->horizontal, &pos->horizontal); set_qh_head(new, MAKE_QH_ADDR (pos)) ; if (!(old->hw.qh.head & cpu_to_le32(UHCI_PTR_TERM))) set_qh_head(old, MAKE_QH_ADDR (new)) ; } else { // (POS) (OLD) -> (POS) (NEW) (OLD) old = list_entry (pos->horizontal.next, uhci_desc_t, horizontal); list_add (&new->horizontal, &pos->horizontal); set_qh_head(new, MAKE_QH_ADDR (old)); set_qh_head(pos, MAKE_QH_ADDR (new)) ; } mb (); spin_unlock_irqrestore (&s->qh_lock, flags); return 0; } /*-------------------------------------------------------------------*/ _static int unlink_qh (uhci_t *s, uhci_desc_t *element) { uhci_desc_t *prev; unsigned long flags; spin_lock_irqsave (&s->qh_lock, flags); prev = list_entry (element->horizontal.prev, uhci_desc_t, horizontal); prev->hw.qh.head = element->hw.qh.head; dbg("unlink qh %p, pqh %p, nxqh %p, to %08x", element, prev, list_entry (element->horizontal.next, uhci_desc_t, horizontal),le32_to_cpu(element->hw.qh.head) &~15); list_del(&element->horizontal); mb (); spin_unlock_irqrestore (&s->qh_lock, flags); return 0; } /*-------------------------------------------------------------------*/ _static int delete_qh (uhci_t *s, uhci_desc_t *qh) { uhci_desc_t *td; struct list_head *p; list_del (&qh->horizontal); while ((p = qh->vertical.next) != &qh->vertical) { td = list_entry (p, uhci_desc_t, vertical); dbg("unlink td @ %p",td); unlink_td (s, td, 0); // no physical unlink delete_desc (s, td); } delete_desc (s, qh); return 0; } /*-------------------------------------------------------------------*/ _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 (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 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 (urb_t *urb, urb_t *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 qh->hw.qh.element &= cpu_to_le32(~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 (s, td); } } /*-------------------------------------------------------------------*/ /* mode: CLEAN_TRANSFER_NO_DELETION: unlink but no deletion mark (step 1 of async_unlink) CLEAN_TRANSFER_REGULAR: regular (unlink/delete-mark) CLEAN_TRANSFER_DELETION_MARK: deletion mark for QH (step 2 of async_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, *prevtd; int now; urb_priv_t *priv=(urb_priv_t*)urb->hcpriv; now=UHCI_GET_CURRENT_FRAME(s); bqh=priv->bottom_qh; if (!priv->next_queued_urb) { // no more appended bulk queues queue_dbg("uhci_clean_transfer: No more bulks for urb %p, qh %p, bqh %p, nqh %p", urb, qh, bqh, priv->next_qh); if (priv->prev_queued_urb && mode != CLEAN_TRANSFER_DELETION_MARK) { // qh not top of the queue unsigned long flags; urb_priv_t* ppriv=(urb_priv_t*)priv->prev_queued_urb->hcpriv; spin_lock_irqsave (&s->qh_lock, flags); prevqh = list_entry (ppriv->desc_list.next, uhci_desc_t, desc_list); prevtd = list_entry (prevqh->vertical.prev, uhci_desc_t, vertical); set_td_link(prevtd, priv->bottom_qh->dma_addr | UHCI_PTR_QH); // skip current qh mb(); queue_dbg("uhci_clean_transfer: relink pqh %p, ptd %p",prevqh, prevtd); spin_unlock_irqrestore (&s->qh_lock, flags); ppriv->bottom_qh = priv->bottom_qh; ppriv->next_queued_urb = NULL; } else { // queue is dead, qh is top of the queue if (mode != CLEAN_TRANSFER_DELETION_MARK) unlink_qh(s, qh); // remove qh from horizontal chain if (bqh) { // remove remainings of bulk queue nqh=priv->next_qh; if (mode != CLEAN_TRANSFER_DELETION_MARK) unlink_qh(s, nqh); // remove nqh from horizontal chain if (mode != CLEAN_TRANSFER_NO_DELETION) { // add helper QHs to free desc list 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 queue_dbg("uhci_clean_transfer: urb %p, prevurb %p, nexturb %p, qh %p, bqh %p, nqh %p", urb, priv->prev_queued_urb, priv->next_queued_urb, qh, bqh, priv->next_qh); if (mode != CLEAN_TRANSFER_DELETION_MARK) { // no work for cleanup at unlink-completion urb_t *nurb; unsigned long flags; nurb = priv->next_queued_urb; spin_lock_irqsave (&s->qh_lock, flags); if (!priv->prev_queued_urb) { // top QH prevqh = list_entry (qh->horizontal.prev, uhci_desc_t, horizontal); set_qh_head(prevqh, bqh->dma_addr | UHCI_PTR_QH); list_del (&qh->horizontal); // remove this qh form horizontal chain list_add (&bqh->horizontal, &prevqh->horizontal); // insert next bqh in horizontal chain } else { // intermediate QH urb_priv_t* ppriv=(urb_priv_t*)priv->prev_queued_urb->hcpriv; urb_priv_t* npriv=(urb_priv_t*)nurb->hcpriv; uhci_desc_t * bnqh; bnqh = list_entry (npriv->desc_list.next, uhci_desc_t, desc_list); ppriv->bottom_qh = bnqh; ppriv->next_queued_urb = nurb; prevqh = list_entry (ppriv->desc_list.next, uhci_desc_t, desc_list); set_qh_head(prevqh, bqh->dma_addr | UHCI_PTR_QH); } mb(); ((urb_priv_t*)nurb->hcpriv)->prev_queued_urb=priv->prev_queued_urb; spin_unlock_irqrestore (&s->qh_lock, flags); } } if (mode != CLEAN_TRANSFER_NO_DELETION) { qh->last_used = now; list_add_tail (&qh->horizontal, &s->free_desc); // mark qh for later deletion/kfree } } /*-------------------------------------------------------------------*/ // Release bandwidth for Interrupt or Isoc. transfers _static void uhci_release_bandwidth(urb_t *urb) { if (urb->bandwidth) { switch (usb_pipetype(urb->pipe)) { case PIPE_INTERRUPT: usb_release_bandwidth (urb->dev, urb, 0); break; case PIPE_ISOCHRONOUS: usb_release_bandwidth (urb->dev, urb, 1); break; default: break; } } } _static void uhci_urb_dma_sync(uhci_t *s, urb_t *urb, urb_priv_t *urb_priv) { if (urb_priv->setup_packet_dma) pci_dma_sync_single(s->uhci_pci, urb_priv->setup_packet_dma, sizeof(devrequest), PCI_DMA_TODEVICE); if (urb_priv->transfer_buffer_dma) pci_dma_sync_single(s->uhci_pci, urb_priv->transfer_buffer_dma, urb->transfer_buffer_length, usb_pipein(urb->pipe) ? PCI_DMA_FROMDEVICE : PCI_DMA_TODEVICE); } _static void uhci_urb_dma_unmap(uhci_t *s, urb_t *urb, urb_priv_t *urb_priv) { if (urb_priv->setup_packet_dma) { pci_unmap_single(s->uhci_pci, urb_priv->setup_packet_dma, sizeof(devrequest), PCI_DMA_TODEVICE); urb_priv->setup_packet_dma = 0; } if (urb_priv->transfer_buffer_dma) { pci_unmap_single(s->uhci_pci, urb_priv->transfer_buffer_dma, urb->transfer_buffer_length, usb_pipein(urb->pipe) ? PCI_DMA_FROMDEVICE : PCI_DMA_TODEVICE); urb_priv->transfer_buffer_dma = 0; } } /*-------------------------------------------------------------------*/ /* needs urb_list_lock! mode: UNLINK_ASYNC_STORE_URB: unlink and move URB into unlinked list UNLINK_ASYNC_DONT_STORE: unlink, don't move URB into unlinked list */ _static int uhci_unlink_urb_async (uhci_t *s,urb_t *urb, int mode) { uhci_desc_t *qh; urb_priv_t *urb_priv; async_dbg("unlink_urb_async called %p",urb); if ((urb->status == -EINPROGRESS) || ((usb_pipetype (urb->pipe) == PIPE_INTERRUPT) && ((urb_priv_t*)urb->hcpriv)->flags)) { ((urb_priv_t*)urb->hcpriv)->started = ~0; // mark dequeue_urb (s, urb); if (mode==UNLINK_ASYNC_STORE_URB) list_add_tail (&urb->urb_list, &s->urb_unlinked); // store urb uhci_switch_timer_int(s); s->unlink_urb_done = 1; uhci_release_bandwidth(urb); urb->status = -ECONNABORTED; // mark urb as "waiting to be killed" urb_priv = (urb_priv_t*)urb->hcpriv; switch (usb_pipetype (urb->pipe)) { case PIPE_INTERRUPT: usb_dotoggle (urb->dev, usb_pipeendpoint (urb->pipe), usb_pipeout (urb->pipe)); case PIPE_ISOCHRONOUS: uhci_clean_iso_step1 (s, urb_priv); break; case PIPE_BULK: case PIPE_CONTROL: qh = list_entry (urb_priv->desc_list.next, uhci_desc_t, desc_list); uhci_clean_transfer (s, urb, qh, CLEAN_TRANSFER_NO_DELETION); break; } ((urb_priv_t*)urb->hcpriv)->started = UHCI_GET_CURRENT_FRAME(s); return -EINPROGRESS; // completion will follow } return 0; // URB already dead } /*-------------------------------------------------------------------*/ // kills an urb by unlinking descriptors and waiting for at least one frame _static int uhci_unlink_urb_sync (uhci_t *s, urb_t *urb) { uhci_desc_t *qh; urb_priv_t *urb_priv; unsigned long flags=0; struct usb_device *usb_dev; spin_lock_irqsave (&s->urb_list_lock, flags); if (urb->status == -EINPROGRESS) { // move descriptors out the the running chains, dequeue urb uhci_unlink_urb_async(s, urb, UNLINK_ASYNC_DONT_STORE); urb_priv = urb->hcpriv; urb->status = -ENOENT; // prevent from double deletion after unlock spin_unlock_irqrestore (&s->urb_list_lock, flags); // cleanup the rest switch (usb_pipetype (urb->pipe)) { case PIPE_ISOCHRONOUS: uhci_wait_ms(1); uhci_clean_iso_step2(s, urb_priv); break; case PIPE_BULK: case PIPE_CONTROL: qh = list_entry (urb_priv->desc_list.next, uhci_desc_t, desc_list); uhci_clean_transfer(s, urb, qh, CLEAN_TRANSFER_DELETION_MARK); uhci_wait_ms(1); } urb->status = -ENOENT; // mark urb as killed uhci_urb_dma_unmap(s, urb, urb->hcpriv); #ifdef DEBUG_SLAB kmem_cache_free (urb_priv_kmem, urb->hcpriv); #else kfree (urb->hcpriv); #endif usb_dev = urb->dev; if (urb->complete) { dbg("unlink_urb: calling completion"); urb->dev = NULL; urb->complete ((struct urb *) urb); } usb_dec_dev_use (usb_dev); } else spin_unlock_irqrestore (&s->urb_list_lock, flags); return 0; } /*-------------------------------------------------------------------*/ // async unlink_urb completion/cleanup work // has to be protected by urb_list_lock! // features: if set in transfer_flags, the resulting status of the killed // transaction is not overwritten _static void uhci_cleanup_unlink(uhci_t *s, int force) { struct list_head *q; urb_t *urb; struct usb_device *dev; int now, type; urb_priv_t *urb_priv; q=s->urb_unlinked.next; now=UHCI_GET_CURRENT_FRAME(s); while (q != &s->urb_unlinked) { urb = list_entry (q, urb_t, urb_list); urb_priv = (urb_priv_t*)urb->hcpriv; q = urb->urb_list.next; if (!urb_priv) // avoid crash when URB is corrupted break; if (force || ((urb_priv->started != ~0) && (urb_priv->started != now))) { async_dbg("async cleanup %p",urb); type=usb_pipetype (urb->pipe); switch (type) { // process descriptors case PIPE_CONTROL: process_transfer (s, urb, CLEAN_TRANSFER_DELETION_MARK); // don't unlink (already done) break; case PIPE_BULK: if (!s->avoid_bulk.counter) process_transfer (s, urb, CLEAN_TRANSFER_DELETION_MARK); // don't unlink (already done) else continue; break; case PIPE_ISOCHRONOUS: process_iso (s, urb, PROCESS_ISO_FORCE); // force, don't unlink break; case PIPE_INTERRUPT: process_interrupt (s, urb); break; } if (!(urb->transfer_flags & USB_TIMEOUT_KILLED)) urb->status = -ECONNRESET; // mark as asynchronously killed dev = urb->dev; // completion may destroy all... urb_priv = urb->hcpriv; list_del (&urb->urb_list); uhci_urb_dma_sync(s, urb, urb_priv); if (urb->complete) { spin_unlock(&s->urb_list_lock); urb->dev = NULL; urb->complete ((struct urb *) urb); spin_lock(&s->urb_list_lock); } if (!(urb->transfer_flags & USB_TIMEOUT_KILLED)) urb->status = -ENOENT; // now the urb is really dead switch (type) { case PIPE_ISOCHRONOUS: case PIPE_INTERRUPT: uhci_clean_iso_step2(s, urb_priv); break; } uhci_urb_dma_unmap(s, urb, urb_priv); usb_dec_dev_use (dev); #ifdef DEBUG_SLAB kmem_cache_free (urb_priv_kmem, urb_priv); #else kfree (urb_priv); #endif } } } /*-------------------------------------------------------------------*/ _static int uhci_unlink_urb (urb_t *urb) { uhci_t *s; unsigned long flags=0; dbg("uhci_unlink_urb called for %p",urb); if (!urb || !urb->dev) // you never know... return -EINVAL; s = (uhci_t*) urb->dev->bus->hcpriv; if (usb_pipedevice (urb->pipe) == s->rh.devnum) return rh_unlink_urb (urb); if (!urb->hcpriv) return -EINVAL; if (urb->transfer_flags & USB_ASYNC_UNLINK) { int ret; spin_lock_irqsave (&s->urb_list_lock, flags); uhci_release_bandwidth(urb); ret = uhci_unlink_urb_async(s, urb, UNLINK_ASYNC_STORE_URB); spin_unlock_irqrestore (&s->urb_list_lock, flags); return ret; } else return uhci_unlink_urb_sync(s, urb); } /*-------------------------------------------------------------------*/ // In case of ASAP iso transfer, search the URB-list for already queued URBs // for this EP and calculate the earliest start frame for the new // URB (easy seamless URB continuation!) _static int find_iso_limits (urb_t *urb, unsigned int *start, unsigned int *end) { urb_t *u, *last_urb = NULL; uhci_t *s = (uhci_t*) urb->dev->bus->hcpriv; struct list_head *p; int ret=-1; unsigned long flags; spin_lock_irqsave (&s->urb_list_lock, flags); p=s->urb_list.prev; for (; p != &s->urb_list; p = p->prev) { u = list_entry (p, urb_t, urb_list); // look for pending URBs with identical pipe handle // works only because iso doesn't toggle the data bit! if ((urb->pipe == u->pipe) && (urb->dev == u->dev) && (u->status == -EINPROGRESS)) { if (!last_urb) *start = u->start_frame; last_urb = u; } } if (last_urb) { *end = (last_urb->start_frame + last_urb->number_of_packets) & 1023; ret=0; } spin_unlock_irqrestore(&s->urb_list_lock, flags); return ret; } /*-------------------------------------------------------------------*/ // adjust start_frame according to scheduling constraints (ASAP etc) _static int iso_find_start (urb_t *urb) { uhci_t *s = (uhci_t*) urb->dev->bus->hcpriv; unsigned int now; unsigned int start_limit = 0, stop_limit = 0, queued_size; int limits; now = UHCI_GET_CURRENT_FRAME (s) & 1023; if ((unsigned) urb->number_of_packets > 900) return -EFBIG; limits = find_iso_limits (urb, &start_limit, &stop_limit); queued_size = (stop_limit - start_limit) & 1023; if (urb->transfer_flags & USB_ISO_ASAP) { // first iso if (limits) { // 10ms setup should be enough //FIXME! urb->start_frame = (now + 10) & 1023; } else { urb->start_frame = stop_limit; //seamless linkage if (((now - urb->start_frame) & 1023) <= (unsigned) urb->number_of_packets) { info("iso_find_start: gap in seamless isochronous scheduling"); dbg("iso_find_start: now %u start_frame %u number_of_packets %u pipe 0x%08x", now, urb->start_frame, urb->number_of_packets, urb->pipe); urb->start_frame = (now + 5) & 1023; // 5ms setup should be enough //FIXME! } } } else { urb->start_frame &= 1023; if (((now - urb->start_frame) & 1023) < (unsigned) urb->number_of_packets) { dbg("iso_find_start: now between start_frame and end"); return -EAGAIN; } } /* check if either start_frame or start_frame+number_of_packets-1 lies between start_limit and stop_limit */ if (limits) return 0; if (((urb->start_frame - start_limit) & 1023) < queued_size || ((urb->start_frame + urb->number_of_packets - 1 - start_limit) & 1023) < queued_size) { dbg("iso_find_start: start_frame %u number_of_packets %u start_limit %u stop_limit %u", urb->start_frame, urb->number_of_packets, start_limit, stop_limit); return -EAGAIN; } return 0; } /*-------------------------------------------------------------------*/ // submits USB interrupt (ie. polling ;-) // ASAP-flag set implicitely // if period==0, the transfer is only done once _static int uhci_submit_int_urb (urb_t *urb) { uhci_t *s = (uhci_t*) urb->dev->bus->hcpriv; urb_priv_t *urb_priv = urb->hcpriv; int nint, n; uhci_desc_t *td; int status, destination; int info; unsigned int pipe = urb->pipe; if (urb->interval < 0 || urb->interval >= 256) return -EINVAL; if (urb->interval == 0) nint = 0; else { for (nint = 0, n = 1; nint <= 8; nint++, n += n) // round interval down to 2^n { if (urb->interval < n) { urb->interval = n / 2; break; } } nint--; } dbg("Rounded interval to %i, chain %i", urb->interval, nint); urb->start_frame = UHCI_GET_CURRENT_FRAME (s) & 1023; // remember start frame, just in case... urb->number_of_packets = 1; // INT allows only one packet if (urb->transfer_buffer_length > usb_maxpacket (urb->dev, pipe, usb_pipeout (pipe))) return -EINVAL; if (alloc_td (s, &td, UHCI_PTR_DEPTH)) return -ENOMEM; status = (pipe & TD_CTRL_LS) | TD_CTRL_ACTIVE | TD_CTRL_IOC | (urb->transfer_flags & USB_DISABLE_SPD ? 0 : TD_CTRL_SPD) | (3 << 27); destination = (urb->pipe & PIPE_DEVEP_MASK) | usb_packetid (urb->pipe) | (((urb->transfer_buffer_length - 1) & 0x7ff) << 21); info = destination | (usb_gettoggle (urb->dev, usb_pipeendpoint (pipe), usb_pipeout (pipe)) << TD_TOKEN_TOGGLE); fill_td (td, status, info, urb_priv->transfer_buffer_dma); list_add_tail (&td->desc_list, &urb_priv->desc_list); queue_urb (s, urb); insert_td_horizontal (s, s->int_chain[nint], td); // store in INT-TDs usb_dotoggle (urb->dev, usb_pipeendpoint (pipe), usb_pipeout (pipe)); return 0; } /*-------------------------------------------------------------------*/ _static int uhci_submit_iso_urb (urb_t *urb) { uhci_t *s = (uhci_t*) urb->dev->bus->hcpriv; urb_priv_t *urb_priv = urb->hcpriv; #ifdef ISO_SANITY_CHECK int pipe=urb->pipe; int maxsze = usb_maxpacket (urb->dev, pipe, usb_pipeout (pipe)); #endif int n, ret, last=0; uhci_desc_t *td, **tdm; int status, destination; unsigned long flags; __save_flags(flags); __cli(); // Disable IRQs to schedule all ISO-TDs in time ret = iso_find_start (urb); // adjusts urb->start_frame for later use if (ret) goto err; tdm = (uhci_desc_t **) kmalloc (urb->number_of_packets * sizeof (uhci_desc_t*), KMALLOC_FLAG); if (!tdm) { ret = -ENOMEM; goto err; } memset(tdm, 0, urb->number_of_packets * sizeof (uhci_desc_t*)); // First try to get all TDs. Cause: Removing already inserted TDs can only be done // racefree in three steps: unlink TDs, wait one frame, delete TDs. // So, this solutions seems simpler... for (n = 0; n < urb->number_of_packets; n++) { dbg("n:%d urb->iso_frame_desc[n].length:%d", n, urb->iso_frame_desc[n].length); if (!urb->iso_frame_desc[n].length) continue; // allows ISO striping by setting length to zero in iso_descriptor #ifdef ISO_SANITY_CHECK if(urb->iso_frame_desc[n].length > maxsze) { err("submit_iso: urb->iso_frame_desc[%d].length(%d)>%d",n , urb->iso_frame_desc[n].length, maxsze); ret=-EINVAL; } else #endif if (alloc_td (s, &td, UHCI_PTR_DEPTH)) { int i; // Cleanup allocated TDs for (i = 0; i < n; n++) if (tdm[i]) delete_desc(s, tdm[i]); kfree (tdm); goto err; } last=n; tdm[n] = td; } status = TD_CTRL_ACTIVE | TD_CTRL_IOS; destination = (urb->pipe & PIPE_DEVEP_MASK) | usb_packetid (urb->pipe); // Queue all allocated TDs for (n = 0; n < urb->number_of_packets; n++) { td = tdm[n]; if (!td) continue; if (n == last) { status |= TD_CTRL_IOC; queue_urb (s, urb); } fill_td (td, status, destination | (((urb->iso_frame_desc[n].length - 1) & 0x7ff) << 21), urb_priv->transfer_buffer_dma + urb->iso_frame_desc[n].offset); list_add_tail (&td->desc_list, &urb_priv->desc_list); insert_td_horizontal (s, s->iso_td[(urb->start_frame + n) & 1023], td); // store in iso-tds } kfree (tdm); dbg("ISO-INT# %i, start %i, now %i", urb->number_of_packets, urb->start_frame, UHCI_GET_CURRENT_FRAME (s) & 1023); ret = 0; err: __restore_flags(flags); return ret; } /*-------------------------------------------------------------------*/ // returns: 0 (no transfer queued), urb* (this urb already queued) _static urb_t* search_dev_ep (uhci_t *s, urb_t *urb) { struct list_head *p; urb_t *tmp; unsigned int mask = usb_pipecontrol(urb->pipe) ? (~USB_DIR_IN) : (~0); dbg("search_dev_ep:"); p=s->urb_list.next; for (; p != &s->urb_list; p = p->next) { tmp = list_entry (p, urb_t, urb_list); dbg("urb: %p", tmp); // we can accept this urb if it is not queued at this time // or if non-iso transfer requests should be scheduled for the same device and pipe if ((!usb_pipeisoc(urb->pipe) && (tmp->dev == urb->dev) && !((tmp->pipe ^ urb->pipe) & mask)) || (urb == tmp)) { return tmp; // found another urb already queued for processing } } return 0; } /*-------------------------------------------------------------------*/ _static int uhci_submit_urb (urb_t *urb) { uhci_t *s; urb_priv_t *urb_priv; int ret = 0, type; unsigned long flags; urb_t *queued_urb=NULL; int bustime; if (!urb->dev || !urb->dev->bus) return -ENODEV; s = (uhci_t*) urb->dev->bus->hcpriv; //dbg("submit_urb: %p type %d",urb,usb_pipetype(urb->pipe)); if (!s->running) return -ENODEV; type = usb_pipetype (urb->pipe); if (usb_pipedevice (urb->pipe) == s->rh.devnum) return rh_submit_urb (urb); /* virtual root hub */ // Sanity checks if (usb_maxpacket (urb->dev, urb->pipe, usb_pipeout (urb->pipe)) <= 0) { err("uhci_submit_urb: pipesize for pipe %x is zero", urb->pipe); return -EMSGSIZE; } if (urb->transfer_buffer_length < 0 && type != PIPE_ISOCHRONOUS) { err("uhci_submit_urb: Negative transfer length for urb %p", urb); return -EINVAL; } usb_inc_dev_use (urb->dev); spin_lock_irqsave (&s->urb_list_lock, flags); queued_urb = search_dev_ep (s, urb); // returns already queued urb for that pipe if (queued_urb) { queue_dbg("found bulk urb %p\n", queued_urb); if (( type != PIPE_BULK) || ((type == PIPE_BULK) && (!(urb->transfer_flags & USB_QUEUE_BULK) || !(queued_urb->transfer_flags & USB_QUEUE_BULK)))) { spin_unlock_irqrestore (&s->urb_list_lock, flags); usb_dec_dev_use (urb->dev); err("ENXIO %08x, flags %x, urb %p, burb %p",urb->pipe,urb->transfer_flags,urb,queued_urb); return -ENXIO; // urb already queued } } #ifdef DEBUG_SLAB urb_priv = kmem_cache_alloc(urb_priv_kmem, SLAB_FLAG); #else urb_priv = kmalloc (sizeof (urb_priv_t), KMALLOC_FLAG); #endif if (!urb_priv) { usb_dec_dev_use (urb->dev); spin_unlock_irqrestore (&s->urb_list_lock, flags); return -ENOMEM; } memset(urb_priv, 0, sizeof(urb_priv_t)); urb->hcpriv = urb_priv; INIT_LIST_HEAD (&urb_priv->desc_list); dbg("submit_urb: scheduling %p", urb); if (type == PIPE_CONTROL) urb_priv->setup_packet_dma = pci_map_single(s->uhci_pci, urb->setup_packet, sizeof(devrequest), PCI_DMA_TODEVICE); if (urb->transfer_buffer_length) urb_priv->transfer_buffer_dma = pci_map_single(s->uhci_pci, urb->transfer_buffer, urb->transfer_buffer_length, usb_pipein(urb->pipe) ? PCI_DMA_FROMDEVICE : PCI_DMA_TODEVICE); if (type == PIPE_BULK) { if (queued_urb) { while (((urb_priv_t*)queued_urb->hcpriv)->next_queued_urb) // find last queued bulk queued_urb=((urb_priv_t*)queued_urb->hcpriv)->next_queued_urb; ((urb_priv_t*)queued_urb->hcpriv)->next_queued_urb=urb; } atomic_inc (&s->avoid_bulk); ret = uhci_submit_bulk_urb (urb, queued_urb); atomic_dec (&s->avoid_bulk); spin_unlock_irqrestore (&s->urb_list_lock, flags); } else { spin_unlock_irqrestore (&s->urb_list_lock, flags); switch (type) { case PIPE_ISOCHRONOUS: if (urb->bandwidth == 0) { /* not yet checked/allocated */ if (urb->number_of_packets <= 0) { ret = -EINVAL; break; } bustime = usb_check_bandwidth (urb->dev, urb); if (bustime < 0) ret = bustime; else { ret = uhci_submit_iso_urb(urb); if (ret == 0) usb_claim_bandwidth (urb->dev, urb, bustime, 1); } } else { /* bandwidth is already set */ ret = uhci_submit_iso_urb(urb); } break; case PIPE_INTERRUPT: if (urb->bandwidth == 0) { /* not yet checked/allocated */ bustime = usb_check_bandwidth (urb->dev, urb); if (bustime < 0) ret = bustime; else { ret = uhci_submit_int_urb(urb); if (ret == 0) usb_claim_bandwidth (urb->dev, urb, bustime, 0); } } else { /* bandwidth is already set */ ret = uhci_submit_int_urb(urb); } break; case PIPE_CONTROL: ret = uhci_submit_control_urb (urb); break; default: ret = -EINVAL; } } dbg("submit_urb: scheduled with ret: %d", ret); if (ret != 0) { uhci_urb_dma_unmap(s, urb, urb_priv); usb_dec_dev_use (urb->dev); #ifdef DEBUG_SLAB kmem_cache_free(urb_priv_kmem, urb_priv); #else kfree (urb_priv); #endif return ret; } return 0; } // Checks for URB timeout and removes bandwidth reclamation if URB idles too long _static void uhci_check_timeouts(uhci_t *s) { struct list_head *p,*p2; urb_t *urb; int type; p = s->urb_list.prev; while (p != &s->urb_list) { urb_priv_t *hcpriv; p2 = p; p = p->prev; urb = list_entry (p2, urb_t, urb_list); type = usb_pipetype (urb->pipe); hcpriv = (urb_priv_t*)urb->hcpriv; if ( urb->timeout && ((hcpriv->started + urb->timeout) < jiffies)) { urb->transfer_flags |= USB_TIMEOUT_KILLED | USB_ASYNC_UNLINK; async_dbg("uhci_check_timeout: timeout for %p",urb); uhci_unlink_urb_async(s, urb, UNLINK_ASYNC_STORE_URB); } #ifdef CONFIG_USB_UHCI_HIGH_BANDWIDTH else if (((type == PIPE_BULK) || (type == PIPE_CONTROL)) && (hcpriv->use_loop) && ((hcpriv->started + IDLE_TIMEOUT) < jiffies)) disable_desc_loop(s, urb); #endif } s->timeout_check=jiffies; } /*------------------------------------------------------------------- Virtual Root Hub -------------------------------------------------------------------*/ _static __u8 root_hub_dev_des[] = { 0x12, /* __u8 bLength; */ 0x01, /* __u8 bDescriptorType; Device */ 0x00, /* __u16 bcdUSB; v1.0 */ 0x01, 0x09, /* __u8 bDeviceClass; HUB_CLASSCODE */ 0x00, /* __u8 bDeviceSubClass; */ 0x00, /* __u8 bDeviceProtocol; */ 0x08, /* __u8 bMaxPacketSize0; 8 Bytes */ 0x00, /* __u16 idVendor; */ 0x00, 0x00, /* __u16 idProduct; */ 0x00, 0x00, /* __u16 bcdDevice; */ 0x00, 0x00, /* __u8 iManufacturer; */ 0x02, /* __u8 iProduct; */ 0x01, /* __u8 iSerialNumber; */ 0x01 /* __u8 bNumConfigurations; */ }; /* Configuration descriptor */ _static __u8 root_hub_config_des[] = { 0x09, /* __u8 bLength; */ 0x02, /* __u8 bDescriptorType; Configuration */ 0x19, /* __u16 wTotalLength; */ 0x00, 0x01, /* __u8 bNumInterfaces; */ 0x01, /* __u8 bConfigurationValue; */ 0x00, /* __u8 iConfiguration; */ 0x40, /* __u8 bmAttributes; Bit 7: Bus-powered, 6: Self-powered, 5 Remote-wakwup, 4..0: resvd */ 0x00, /* __u8 MaxPower; */ /* interface */ 0x09, /* __u8 if_bLength; */ 0x04, /* __u8 if_bDescriptorType; Interface */ 0x00, /* __u8 if_bInterfaceNumber; */ 0x00, /* __u8 if_bAlternateSetting; */ 0x01, /* __u8 if_bNumEndpoints; */ 0x09, /* __u8 if_bInterfaceClass; HUB_CLASSCODE */ 0x00, /* __u8 if_bInterfaceSubClass; */ 0x00, /* __u8 if_bInterfaceProtocol; */ 0x00, /* __u8 if_iInterface; */ /* endpoint */ 0x07, /* __u8 ep_bLength; */ 0x05, /* __u8 ep_bDescriptorType; Endpoint */ 0x81, /* __u8 ep_bEndpointAddress; IN Endpoint 1 */ 0x03, /* __u8 ep_bmAttributes; Interrupt */ 0x08, /* __u16 ep_wMaxPacketSize; 8 Bytes */ 0x00, 0xff /* __u8 ep_bInterval; 255 ms */ }; _static __u8 root_hub_hub_des[] = { 0x09, /* __u8 bLength; */ 0x29, /* __u8 bDescriptorType; Hub-descriptor */ 0x02, /* __u8 bNbrPorts; */ 0x00, /* __u16 wHubCharacteristics; */ 0x00, 0x01, /* __u8 bPwrOn2pwrGood; 2ms */ 0x00, /* __u8 bHubContrCurrent; 0 mA */ 0x00, /* __u8 DeviceRemovable; *** 7 Ports max *** */ 0xff /* __u8 PortPwrCtrlMask; *** 7 ports max *** */ }; /*-------------------------------------------------------------------------*/ /* prepare Interrupt pipe transaction data; HUB INTERRUPT ENDPOINT */ _static int rh_send_irq (urb_t *urb) { int len = 1; int i; uhci_t *uhci = urb->dev->bus->hcpriv; unsigned int io_addr = uhci->io_addr; __u16 data = 0; for (i = 0; i < uhci->rh.numports; i++) { data |= ((inw (io_addr + USBPORTSC1 + i * 2) & 0xa) > 0 ? (1 << (i + 1)) : 0); len = (i + 1) / 8 + 1; } *(__u16 *) urb->transfer_buffer = cpu_to_le16 (data); urb->actual_length = len; urb->status = 0; if ((data > 0) && (uhci->rh.send != 0)) { dbg("Root-Hub INT complete: port1: %x port2: %x data: %x", inw (io_addr + USBPORTSC1), inw (io_addr + USBPORTSC2), data); urb->complete (urb); } return 0; } /*-------------------------------------------------------------------------*/ /* Virtual Root Hub INTs are polled by this timer every "intervall" ms */ _static int rh_init_int_timer (urb_t *urb); _static void rh_int_timer_do (unsigned long ptr) { int len; urb_t *urb = (urb_t*) ptr; uhci_t *uhci = urb->dev->bus->hcpriv; if (uhci->rh.send) { len = rh_send_irq (urb); if (len > 0) { urb->actual_length = len; if (urb->complete) urb->complete (urb); } } rh_init_int_timer (urb); } /*-------------------------------------------------------------------------*/ /* Root Hub INTs are polled by this timer, polling interval 20ms */ _static int rh_init_int_timer (urb_t *urb) { uhci_t *uhci = urb->dev->bus->hcpriv; uhci->rh.interval = urb->interval; init_timer (&uhci->rh.rh_int_timer); uhci->rh.rh_int_timer.function = rh_int_timer_do; uhci->rh.rh_int_timer.data = (unsigned long) urb; uhci->rh.rh_int_timer.expires = jiffies + (HZ * 20) / 1000; add_timer (&uhci->rh.rh_int_timer); return 0; } /*-------------------------------------------------------------------------*/ #define OK(x) len = (x); break #define CLR_RH_PORTSTAT(x) \ status = inw(io_addr+USBPORTSC1+2*(wIndex-1)); \ status = (status & 0xfff5) & ~(x); \ outw(status, io_addr+USBPORTSC1+2*(wIndex-1)) #define SET_RH_PORTSTAT(x) \ status = inw(io_addr+USBPORTSC1+2*(wIndex-1)); \ status = (status & 0xfff5) | (x); \ outw(status, io_addr+USBPORTSC1+2*(wIndex-1)) /*-------------------------------------------------------------------------*/ /**** ** Root Hub Control Pipe *************************/ _static int rh_submit_urb (urb_t *urb) { struct usb_device *usb_dev = urb->dev; uhci_t *uhci = usb_dev->bus->hcpriv; unsigned int pipe = urb->pipe; devrequest *cmd = (devrequest *) urb->setup_packet; void *data = urb->transfer_buffer; int leni = urb->transfer_buffer_length; int len = 0; int status = 0; int stat = 0; int i; unsigned int io_addr = uhci->io_addr; __u16 cstatus; __u16 bmRType_bReq; __u16 wValue; __u16 wIndex; __u16 wLength; if (usb_pipetype (pipe) == PIPE_INTERRUPT) { dbg("Root-Hub submit IRQ: every %d ms", urb->interval); uhci->rh.urb = urb; uhci->rh.send = 1; uhci->rh.interval = urb->interval; rh_init_int_timer (urb); return 0; } bmRType_bReq = cmd->requesttype | cmd->request << 8; wValue = le16_to_cpu (cmd->value); wIndex = le16_to_cpu (cmd->index); wLength = le16_to_cpu (cmd->length); for (i = 0; i < 8; i++) uhci->rh.c_p_r[i] = 0; dbg("Root-Hub: adr: %2x cmd(%1x): %04x %04x %04x %04x", uhci->rh.devnum, 8, bmRType_bReq, wValue, wIndex, wLength); switch (bmRType_bReq) { /* Request Destination: without flags: Device, RH_INTERFACE: interface, RH_ENDPOINT: endpoint, RH_CLASS means HUB here, RH_OTHER | RH_CLASS almost ever means HUB_PORT here */ case RH_GET_STATUS: *(__u16 *) data = cpu_to_le16 (1); OK (2); case RH_GET_STATUS | RH_INTERFACE: *(__u16 *) data = cpu_to_le16 (0); OK (2); case RH_GET_STATUS | RH_ENDPOINT: *(__u16 *) data = cpu_to_le16 (0); OK (2); case RH_GET_STATUS | RH_CLASS: *(__u32 *) data = cpu_to_le32 (0); OK (4); /* hub power ** */ case RH_GET_STATUS | RH_OTHER | RH_CLASS: status = inw (io_addr + USBPORTSC1 + 2 * (wIndex - 1)); cstatus = ((status & USBPORTSC_CSC) >> (1 - 0)) | ((status & USBPORTSC_PEC) >> (3 - 1)) | (uhci->rh.c_p_r[wIndex - 1] << (0 + 4)); status = (status & USBPORTSC_CCS) | ((status & USBPORTSC_PE) >> (2 - 1)) | ((status & USBPORTSC_SUSP) >> (12 - 2)) | ((status & USBPORTSC_PR) >> (9 - 4)) | (1 << 8) | /* power on ** */ ((status & USBPORTSC_LSDA) << (-8 + 9)); *(__u16 *) data = cpu_to_le16 (status); *(__u16 *) (data + 2) = cpu_to_le16 (cstatus); OK (4); case RH_CLEAR_FEATURE | RH_ENDPOINT: switch (wValue) { case (RH_ENDPOINT_STALL): OK (0); } break; case RH_CLEAR_FEATURE | RH_CLASS: switch (wValue) { case (RH_C_HUB_OVER_CURRENT): OK (0); /* hub power over current ** */ } break; case RH_CLEAR_FEATURE | RH_OTHER | RH_CLASS: switch (wValue) { case (RH_PORT_ENABLE): CLR_RH_PORTSTAT (USBPORTSC_PE); OK (0); case (RH_PORT_SUSPEND): CLR_RH_PORTSTAT (USBPORTSC_SUSP); OK (0); case (RH_PORT_POWER): OK (0); /* port power ** */ case (RH_C_PORT_CONNECTION): SET_RH_PORTSTAT (USBPORTSC_CSC); OK (0); case (RH_C_PORT_ENABLE): SET_RH_PORTSTAT (USBPORTSC_PEC); OK (0); case (RH_C_PORT_SUSPEND): /*** WR_RH_PORTSTAT(RH_PS_PSSC); */ OK (0); case (RH_C_PORT_OVER_CURRENT): OK (0); /* port power over current ** */ case (RH_C_PORT_RESET): uhci->rh.c_p_r[wIndex - 1] = 0; OK (0); } break; case RH_SET_FEATURE | RH_OTHER | RH_CLASS: switch (wValue) { case (RH_PORT_SUSPEND): SET_RH_PORTSTAT (USBPORTSC_SUSP); OK (0); case (RH_PORT_RESET): SET_RH_PORTSTAT (USBPORTSC_PR); uhci_wait_ms (10); uhci->rh.c_p_r[wIndex - 1] = 1; CLR_RH_PORTSTAT (USBPORTSC_PR); udelay (10); SET_RH_PORTSTAT (USBPORTSC_PE); uhci_wait_ms (10); SET_RH_PORTSTAT (0xa); OK (0); case (RH_PORT_POWER): OK (0); /* port power ** */ case (RH_PORT_ENABLE): SET_RH_PORTSTAT (USBPORTSC_PE); OK (0); } break; case RH_SET_ADDRESS: uhci->rh.devnum = wValue; OK (0); case RH_GET_DESCRIPTOR: switch ((wValue & 0xff00) >> 8) { case (0x01): /* device descriptor */ len = min_t(unsigned int, leni, min_t(unsigned int, sizeof (root_hub_dev_des), wLength)); memcpy (data, root_hub_dev_des, len); OK (len); case (0x02): /* configuration descriptor */ len = min_t(unsigned int, leni, min_t(unsigned int, sizeof (root_hub_config_des), wLength)); memcpy (data, root_hub_config_des, len); OK (len); case (0x03): /* string descriptors */ len = usb_root_hub_string (wValue & 0xff, uhci->io_addr, "UHCI", data, wLength); if (len > 0) { OK(min_t(int, leni, len)); } else stat = -EPIPE; } break; case RH_GET_DESCRIPTOR | RH_CLASS: root_hub_hub_des[2] = uhci->rh.numports; len = min_t(unsigned int, leni, min_t(unsigned int, sizeof (root_hub_hub_des), wLength)); memcpy (data, root_hub_hub_des, len); OK (len); case RH_GET_CONFIGURATION: *(__u8 *) data = 0x01; OK (1); case RH_SET_CONFIGURATION: OK (0); default: stat = -EPIPE; } dbg("Root-Hub stat port1: %x port2: %x", inw (io_addr + USBPORTSC1), inw (io_addr + USBPORTSC2)); urb->actual_length = len; urb->status = stat; urb->dev=NULL; if (urb->complete) urb->complete (urb); return 0; } /*-------------------------------------------------------------------------*/ _static int rh_unlink_urb (urb_t *urb) { uhci_t *uhci = urb->dev->bus->hcpriv; if (uhci->rh.urb==urb) { dbg("Root-Hub unlink IRQ"); uhci->rh.send = 0; del_timer (&uhci->rh.rh_int_timer); } return 0; } /*-------------------------------------------------------------------*/ /* * Map status to standard result codes * * is (td->status & 0xFE0000) [a.k.a. uhci_status_bits(td->status) * is True for output TDs and False for input TDs. */ _static int uhci_map_status (int status, int dir_out) { if (!status) return 0; if (status & TD_CTRL_BITSTUFF) /* Bitstuff error */ return -EPROTO; if (status & TD_CTRL_CRCTIMEO) { /* CRC/Timeout */ if (dir_out) return -ETIMEDOUT; else return -EILSEQ; } if (status & TD_CTRL_NAK) /* NAK */ return -ETIMEDOUT; if (status & TD_CTRL_BABBLE) /* Babble */ return -EOVERFLOW; if (status & TD_CTRL_DBUFERR) /* Buffer error */ return -ENOSR; if (status & TD_CTRL_STALLED) /* Stalled */ return -EPIPE; if (status & TD_CTRL_ACTIVE) /* Active */ return 0; return -EPROTO; } /* * Only the USB core should call uhci_alloc_dev and uhci_free_dev */ _static int uhci_alloc_dev (struct usb_device *usb_dev) { return 0; } _static void uhci_unlink_urbs(uhci_t *s, struct usb_device *usb_dev, int remove_all) { unsigned long flags; struct list_head *p; struct list_head *p2; urb_t *urb; spin_lock_irqsave (&s->urb_list_lock, flags); p = s->urb_list.prev; while (p != &s->urb_list) { p2 = p; p = p->prev ; urb = list_entry (p2, urb_t, urb_list); dbg("urb: %p, dev %p, %p", urb, usb_dev,urb->dev); //urb->transfer_flags |=USB_ASYNC_UNLINK; if (remove_all || (usb_dev == urb->dev)) { spin_unlock_irqrestore (&s->urb_list_lock, flags); warn("forced removing of queued URB %p due to disconnect",urb); uhci_unlink_urb(urb); urb->dev = NULL; // avoid further processing of this URB spin_lock_irqsave (&s->urb_list_lock, flags); p = s->urb_list.prev; } } spin_unlock_irqrestore (&s->urb_list_lock, flags); } _static int uhci_free_dev (struct usb_device *usb_dev) { uhci_t *s; if(!usb_dev || !usb_dev->bus || !usb_dev->bus->hcpriv) return -EINVAL; s=(uhci_t*) usb_dev->bus->hcpriv; uhci_unlink_urbs(s, usb_dev, 0); return 0; } /* * uhci_get_current_frame_number() * * returns the current frame number for a USB bus/controller. */ _static int uhci_get_current_frame_number (struct usb_device *usb_dev) { return UHCI_GET_CURRENT_FRAME ((uhci_t*) usb_dev->bus->hcpriv); } struct usb_operations uhci_device_operations = { uhci_alloc_dev, uhci_free_dev, uhci_get_current_frame_number, uhci_submit_urb, uhci_unlink_urb }; _static void correct_data_toggles(urb_t *urb) { usb_settoggle (urb->dev, usb_pipeendpoint (urb->pipe), usb_pipeout (urb->pipe), !usb_gettoggle (urb->dev, usb_pipeendpoint (urb->pipe), usb_pipeout (urb->pipe))); while(urb) { urb_priv_t *priv=urb->hcpriv; uhci_desc_t *qh = list_entry (priv->desc_list.next, uhci_desc_t, desc_list); struct list_head *p = qh->vertical.next; uhci_desc_t *td; dbg("URB to correct %p\n", urb); for (; p != &qh->vertical; p = p->next) { td = list_entry (p, uhci_desc_t, vertical); td->hw.td.info^=cpu_to_le32(1<next_queued_urb; } } /* * For IN-control transfers, process_transfer gets a bit more complicated, * since there are devices that return less data (eg. strings) than they * have announced. This leads to a queue abort due to the short packet, * the status stage is not executed. If this happens, the status stage * is manually re-executed. * mode: PROCESS_TRANSFER_REGULAR: regular (unlink QH) * PROCESS_TRANSFER_DONT_UNLINK: QHs already unlinked (for async unlink_urb) */ _static int process_transfer (uhci_t *s, urb_t *urb, int mode) { int ret = 0; urb_priv_t *urb_priv = urb->hcpriv; struct list_head *qhl = urb_priv->desc_list.next; uhci_desc_t *qh = list_entry (qhl, uhci_desc_t, desc_list); struct list_head *p = qh->vertical.next; uhci_desc_t *desc= list_entry (urb_priv->desc_list.prev, uhci_desc_t, desc_list); uhci_desc_t *last_desc = list_entry (desc->vertical.prev, uhci_desc_t, vertical); int data_toggle = usb_gettoggle (urb->dev, usb_pipeendpoint (urb->pipe), usb_pipeout (urb->pipe)); // save initial data_toggle int maxlength; // extracted and remapped info from TD int actual_length; int status = 0; //dbg("process_transfer: urb %p, urb_priv %p, qh %p last_desc %p\n",urb,urb_priv, qh, last_desc); /* if the status phase has been retriggered and the queue is empty or the last status-TD is inactive, the retriggered status stage is completed */ if (urb_priv->flags && ((qh->hw.qh.element == cpu_to_le32(UHCI_PTR_TERM)) || !is_td_active(desc))) goto transfer_finished; urb->actual_length=0; for (; p != &qh->vertical; p = p->next) { desc = list_entry (p, uhci_desc_t, vertical); if (is_td_active(desc)) { // do not process active TDs if (mode == CLEAN_TRANSFER_DELETION_MARK) // if called from async_unlink uhci_clean_transfer(s, urb, qh, CLEAN_TRANSFER_DELETION_MARK); return ret; } actual_length = uhci_actual_length(le32_to_cpu(desc->hw.td.status)); // extract transfer parameters from TD maxlength = (((le32_to_cpu(desc->hw.td.info) >> 21) & 0x7ff) + 1) & 0x7ff; status = uhci_map_status (uhci_status_bits (le32_to_cpu(desc->hw.td.status)), usb_pipeout (urb->pipe)); if (status == -EPIPE) { // see if EP is stalled // set up stalled condition usb_endpoint_halt (urb->dev, usb_pipeendpoint (urb->pipe), usb_pipeout (urb->pipe)); } if (status && (status != -EPIPE)) { // if any error occurred stop processing of further TDs // only set ret if status returned an error is_error: ret = status; urb->error_count++; break; } else if ((le32_to_cpu(desc->hw.td.info) & 0xff) != USB_PID_SETUP) urb->actual_length += actual_length; // got less data than requested if ( (actual_length < maxlength)) { if (urb->transfer_flags & USB_DISABLE_SPD) { status = -EREMOTEIO; // treat as real error dbg("process_transfer: SPD!!"); break; // exit after this TD because SP was detected } // short read during control-IN: re-start status stage if ((usb_pipetype (urb->pipe) == PIPE_CONTROL)) { if (uhci_packetid(le32_to_cpu(last_desc->hw.td.info)) == USB_PID_OUT) { set_qh_element(qh, last_desc->dma_addr); // re-trigger status stage dbg("short packet during control transfer, retrigger status stage @ %p",last_desc); urb_priv->flags = 1; // mark as short control packet return 0; } } // all other cases: short read is OK data_toggle = uhci_toggle (le32_to_cpu(desc->hw.td.info)); break; } else if (status) goto is_error; data_toggle = uhci_toggle (le32_to_cpu(desc->hw.td.info)); queue_dbg("process_transfer: len:%d status:%x mapped:%x toggle:%d", actual_length, le32_to_cpu(desc->hw.td.status),status, data_toggle); } if (usb_pipetype (urb->pipe) == PIPE_BULK ) { /* toggle correction for short bulk transfers (nonqueued/queued) */ urb_priv_t *priv=(urb_priv_t*)urb->hcpriv; urb_t *next_queued_urb=priv->next_queued_urb; if (next_queued_urb) { urb_priv_t *next_priv=(urb_priv_t*)next_queued_urb->hcpriv; uhci_desc_t *qh = list_entry (next_priv->desc_list.next, uhci_desc_t, desc_list); uhci_desc_t *first_td=list_entry (qh->vertical.next, uhci_desc_t, vertical); if (data_toggle == uhci_toggle (le32_to_cpu(first_td->hw.td.info))) { err("process_transfer: fixed toggle"); correct_data_toggles(next_queued_urb); } } else usb_settoggle (urb->dev, usb_pipeendpoint (urb->pipe), usb_pipeout (urb->pipe), !data_toggle); } transfer_finished: uhci_clean_transfer(s, urb, qh, mode); urb->status = status; #ifdef CONFIG_USB_UHCI_HIGH_BANDWIDTH disable_desc_loop(s,urb); #endif queue_dbg("process_transfer: (end) urb %p, wanted len %d, len %d status %x err %d", urb,urb->transfer_buffer_length,urb->actual_length, urb->status, urb->error_count); return ret; } _static int process_interrupt (uhci_t *s, urb_t *urb) { int i, ret = -EINPROGRESS; urb_priv_t *urb_priv = urb->hcpriv; struct list_head *p = urb_priv->desc_list.next; uhci_desc_t *desc = list_entry (urb_priv->desc_list.prev, uhci_desc_t, desc_list); int actual_length; int status = 0; //dbg("urb contains interrupt request"); for (i = 0; p != &urb_priv->desc_list; p = p->next, i++) // Maybe we allow more than one TD later ;-) { desc = list_entry (p, uhci_desc_t, desc_list); if (is_td_active(desc)) { // do not process active TDs //dbg("TD ACT Status @%p %08x",desc,le32_to_cpu(desc->hw.td.status)); break; } if (!desc->hw.td.status & cpu_to_le32(TD_CTRL_IOC)) { // do not process one-shot TDs, no recycling break; } // extract transfer parameters from TD actual_length = uhci_actual_length(le32_to_cpu(desc->hw.td.status)); status = uhci_map_status (uhci_status_bits (le32_to_cpu(desc->hw.td.status)), usb_pipeout (urb->pipe)); // see if EP is stalled if (status == -EPIPE) { // set up stalled condition usb_endpoint_halt (urb->dev, usb_pipeendpoint (urb->pipe), usb_pipeout (urb->pipe)); } // if any error occurred: ignore this td, and continue if (status != 0) { //uhci_show_td (desc); urb->error_count++; goto recycle; } else urb->actual_length = actual_length; recycle: uhci_urb_dma_sync(s, urb, urb->hcpriv); if (urb->complete) { //dbg("process_interrupt: calling completion, status %i",status); urb->status = status; ((urb_priv_t*)urb->hcpriv)->flags=1; // if unlink_urb is called during completion spin_unlock(&s->urb_list_lock); urb->complete ((struct urb *) urb); spin_lock(&s->urb_list_lock); ((urb_priv_t*)urb->hcpriv)->flags=0; } if ((urb->status != -ECONNABORTED) && (urb->status != ECONNRESET) && (urb->status != -ENOENT)) { urb->status = -EINPROGRESS; // Recycle INT-TD if interval!=0, else mark TD as one-shot if (urb->interval) { desc->hw.td.info &= cpu_to_le32(~(1 << TD_TOKEN_TOGGLE)); if (status==0) { ((urb_priv_t*)urb->hcpriv)->started=jiffies; desc->hw.td.info |= cpu_to_le32((usb_gettoggle (urb->dev, usb_pipeendpoint (urb->pipe), usb_pipeout (urb->pipe)) << TD_TOKEN_TOGGLE)); usb_dotoggle (urb->dev, usb_pipeendpoint (urb->pipe), usb_pipeout (urb->pipe)); } else { desc->hw.td.info |= cpu_to_le32((!usb_gettoggle (urb->dev, usb_pipeendpoint (urb->pipe), usb_pipeout (urb->pipe)) << TD_TOKEN_TOGGLE)); } desc->hw.td.status= cpu_to_le32((urb->pipe & TD_CTRL_LS) | TD_CTRL_ACTIVE | TD_CTRL_IOC | (urb->transfer_flags & USB_DISABLE_SPD ? 0 : TD_CTRL_SPD) | (3 << 27)); mb(); } else { uhci_unlink_urb_async(s, urb, UNLINK_ASYNC_STORE_URB); clr_td_ioc(desc); // inactivate TD } } } return ret; } // mode: PROCESS_ISO_REGULAR: processing only for done TDs, unlink TDs // mode: PROCESS_ISO_FORCE: force processing, don't unlink TDs (already unlinked) _static int process_iso (uhci_t *s, urb_t *urb, int mode) { int i; int ret = 0; urb_priv_t *urb_priv = urb->hcpriv; struct list_head *p = urb_priv->desc_list.next, *p_tmp; uhci_desc_t *desc = list_entry (urb_priv->desc_list.prev, uhci_desc_t, desc_list); dbg("urb contains iso request"); if (is_td_active(desc) && mode==PROCESS_ISO_REGULAR) return -EXDEV; // last TD not finished urb->error_count = 0; urb->actual_length = 0; urb->status = 0; dbg("process iso urb %p, %li, %i, %i, %i %08x",urb,jiffies,UHCI_GET_CURRENT_FRAME(s), urb->number_of_packets,mode,le32_to_cpu(desc->hw.td.status)); for (i = 0; p != &urb_priv->desc_list; i++) { desc = list_entry (p, uhci_desc_t, desc_list); //uhci_show_td(desc); if (is_td_active(desc)) { // means we have completed the last TD, but not the TDs before desc->hw.td.status &= cpu_to_le32(~TD_CTRL_ACTIVE); dbg("TD still active (%x)- grrr. paranoia!", le32_to_cpu(desc->hw.td.status)); ret = -EXDEV; urb->iso_frame_desc[i].status = ret; unlink_td (s, desc, 1); // FIXME: immediate deletion may be dangerous goto err; } if (mode == PROCESS_ISO_REGULAR) unlink_td (s, desc, 1); if (urb->number_of_packets <= i) { dbg("urb->number_of_packets (%d)<=(%d)", urb->number_of_packets, i); ret = -EINVAL; goto err; } urb->iso_frame_desc[i].actual_length = uhci_actual_length(le32_to_cpu(desc->hw.td.status)); urb->iso_frame_desc[i].status = uhci_map_status (uhci_status_bits (le32_to_cpu(desc->hw.td.status)), usb_pipeout (urb->pipe)); urb->actual_length += urb->iso_frame_desc[i].actual_length; err: if (urb->iso_frame_desc[i].status != 0) { urb->error_count++; urb->status = urb->iso_frame_desc[i].status; } dbg("process_iso: %i: len:%d %08x status:%x", i, urb->iso_frame_desc[i].actual_length, le32_to_cpu(desc->hw.td.status),urb->iso_frame_desc[i].status); p_tmp = p; p = p->next; list_del (p_tmp); delete_desc (s, desc); } dbg("process_iso: exit %i (%d), actual_len %i", i, ret,urb->actual_length); return ret; } _static int process_urb (uhci_t *s, struct list_head *p) { int ret = 0; urb_t *urb; urb=list_entry (p, urb_t, urb_list); //dbg("process_urb: found queued urb: %p", urb); switch (usb_pipetype (urb->pipe)) { case PIPE_CONTROL: ret = process_transfer (s, urb, CLEAN_TRANSFER_REGULAR); break; case PIPE_BULK: if (!s->avoid_bulk.counter) ret = process_transfer (s, urb, CLEAN_TRANSFER_REGULAR); else return 0; break; case PIPE_ISOCHRONOUS: ret = process_iso (s, urb, PROCESS_ISO_REGULAR); break; case PIPE_INTERRUPT: ret = process_interrupt (s, urb); break; } if (urb->status != -EINPROGRESS) { urb_priv_t *urb_priv; struct usb_device *usb_dev; usb_dev=urb->dev; /* Release bandwidth for Interrupt or Iso transfers */ if (urb->bandwidth) { if (usb_pipetype(urb->pipe)==PIPE_ISOCHRONOUS) usb_release_bandwidth (urb->dev, urb, 1); else if (usb_pipetype(urb->pipe)==PIPE_INTERRUPT && urb->interval) usb_release_bandwidth (urb->dev, urb, 0); } dbg("dequeued urb: %p", urb); dequeue_urb (s, urb); urb_priv = urb->hcpriv; uhci_urb_dma_unmap(s, urb, urb_priv); #ifdef DEBUG_SLAB kmem_cache_free(urb_priv_kmem, urb_priv); #else kfree (urb_priv); #endif if ((usb_pipetype (urb->pipe) != PIPE_INTERRUPT)) { // process_interrupt does completion on its own urb_t *next_urb = urb->next; int is_ring = 0; int contains_killed = 0; int loop_count=0; if (next_urb) { // Find out if the URBs are linked to a ring while (next_urb != NULL && next_urb != urb && loop_count < MAX_NEXT_COUNT) { if (next_urb->status == -ENOENT) {// killed URBs break ring structure & resubmission contains_killed = 1; break; } next_urb = next_urb->next; loop_count++; } if (loop_count == MAX_NEXT_COUNT) err("process_urb: Too much linked URBs in ring detection!"); if (next_urb == urb) is_ring=1; } // Submit idle/non-killed URBs linked with urb->next // Stop before the current URB next_urb = urb->next; if (next_urb && !contains_killed) { int ret_submit; next_urb = urb->next; loop_count=0; while (next_urb != NULL && next_urb != urb && loop_count < MAX_NEXT_COUNT) { if (next_urb->status != -EINPROGRESS) { if (next_urb->status == -ENOENT) break; spin_unlock(&s->urb_list_lock); ret_submit=uhci_submit_urb(next_urb); spin_lock(&s->urb_list_lock); if (ret_submit) break; } loop_count++; next_urb = next_urb->next; } if (loop_count == MAX_NEXT_COUNT) err("process_urb: Too much linked URBs in resubmission!"); } // Completion if (urb->complete) { int was_unlinked = (urb->status == -ENOENT); urb->dev = NULL; spin_unlock(&s->urb_list_lock); urb->complete ((struct urb *) urb); // Re-submit the URB if ring-linked if (is_ring && !was_unlinked && !contains_killed) { urb->dev=usb_dev; uhci_submit_urb (urb); } spin_lock(&s->urb_list_lock); } usb_dec_dev_use (usb_dev); } } return ret; } _static void uhci_interrupt (int irq, void *__uhci, struct pt_regs *regs) { uhci_t *s = __uhci; unsigned int io_addr = s->io_addr; unsigned short status; struct list_head *p, *p2; int restarts, work_done; /* * Read the interrupt status, and write it back to clear the * interrupt cause */ status = inw (io_addr + USBSTS); if (!status) /* shared interrupt, not mine */ return; dbg("interrupt"); if (status != 1) { // Avoid too much error messages at a time if ((jiffies - s->last_error_time > ERROR_SUPPRESSION_TIME)) { warn("interrupt, status %x, frame# %i", status, UHCI_GET_CURRENT_FRAME(s)); s->last_error_time = jiffies; } // remove host controller halted state if ((status&0x20) && (s->running)) { err("Host controller halted, trying to restart."); outw (USBCMD_RS | inw(io_addr + USBCMD), io_addr + USBCMD); } //uhci_show_status (s); } /* * traverse the list in *reverse* direction, because new entries * may be added at the end. * also, because process_urb may unlink the current urb, * we need to advance the list before * New: check for max. workload and restart count */ spin_lock (&s->urb_list_lock); restarts=0; work_done=0; restart: s->unlink_urb_done=0; p = s->urb_list.prev; while (p != &s->urb_list && (work_done < 1024)) { p2 = p; p = p->prev; process_urb (s, p2); work_done++; if (s->unlink_urb_done) { s->unlink_urb_done=0; restarts++; if (restarts<16) // avoid endless restarts goto restart; else break; } } if ((jiffies - s->timeout_check) > (HZ/30)) uhci_check_timeouts(s); clean_descs(s, CLEAN_NOT_FORCED); uhci_cleanup_unlink(s, CLEAN_NOT_FORCED); uhci_switch_timer_int(s); spin_unlock (&s->urb_list_lock); outw (status, io_addr + USBSTS); //dbg("uhci_interrupt: done"); } _static void reset_hc (uhci_t *s) { unsigned int io_addr = s->io_addr; s->apm_state = 0; /* Global reset for 50ms */ outw (USBCMD_GRESET, io_addr + USBCMD); uhci_wait_ms (50); outw (0, io_addr + USBCMD); uhci_wait_ms (10); } _static void start_hc (uhci_t *s) { unsigned int io_addr = s->io_addr; int timeout = 1000; /* * Reset the HC - this will force us to get a * new notification of any already connected * ports due to the virtual disconnect that it * implies. */ outw (USBCMD_HCRESET, io_addr + USBCMD); while (inw (io_addr + USBCMD) & USBCMD_HCRESET) { if (!--timeout) { err("USBCMD_HCRESET timed out!"); break; } } /* Turn on all interrupts */ outw (USBINTR_TIMEOUT | USBINTR_RESUME | USBINTR_IOC | USBINTR_SP, io_addr + USBINTR); /* Start at frame 0 */ outw (0, io_addr + USBFRNUM); outl (s->framelist_dma, io_addr + USBFLBASEADD); /* Run and mark it configured with a 64-byte max packet */ outw (USBCMD_RS | USBCMD_CF | USBCMD_MAXP, io_addr + USBCMD); s->apm_state = 1; s->running = 1; } _static void __devexit uhci_pci_remove (struct pci_dev *dev) { uhci_t *s = pci_get_drvdata(dev); struct usb_device *root_hub = s->bus->root_hub; s->running = 0; // Don't allow submit_urb if (root_hub) usb_disconnect (&root_hub); reset_hc (s); wait_ms (1); uhci_unlink_urbs (s, 0, CLEAN_FORCED); // Forced unlink of remaining URBs uhci_cleanup_unlink (s, CLEAN_FORCED); // force cleanup of async killed URBs usb_deregister_bus (s->bus); release_region (s->io_addr, s->io_size); free_irq (s->irq, s); usb_free_bus (s->bus); cleanup_skel (s); kfree (s); } _static int __init uhci_start_usb (uhci_t *s) { /* start it up */ /* connect the virtual root hub */ struct usb_device *usb_dev; usb_dev = usb_alloc_dev (NULL, s->bus); if (!usb_dev) return -1; s->bus->root_hub = usb_dev; usb_connect (usb_dev); if (usb_new_device (usb_dev) != 0) { usb_free_dev (usb_dev); return -1; } return 0; } #ifdef CONFIG_PM _static int uhci_pci_suspend (struct pci_dev *dev, u32 state) { reset_hc((uhci_t *) pci_get_drvdata(dev)); return 0; } _static int uhci_pci_resume (struct pci_dev *dev) { start_hc((uhci_t *) pci_get_drvdata(dev)); return 0; } #endif _static int __devinit alloc_uhci (struct pci_dev *dev, int irq, unsigned int io_addr, unsigned int io_size) { uhci_t *s; struct usb_bus *bus; char buf[8], *bufp = buf; #ifndef __sparc__ sprintf(buf, "%d", irq); #else bufp = __irq_itoa(irq); #endif printk(KERN_INFO __FILE__ ": USB UHCI at I/O 0x%x, IRQ %s\n", io_addr, bufp); s = kmalloc (sizeof (uhci_t), GFP_KERNEL); if (!s) return -1; memset (s, 0, sizeof (uhci_t)); INIT_LIST_HEAD (&s->free_desc); INIT_LIST_HEAD (&s->urb_list); INIT_LIST_HEAD (&s->urb_unlinked); spin_lock_init (&s->urb_list_lock); spin_lock_init (&s->qh_lock); spin_lock_init (&s->td_lock); atomic_set(&s->avoid_bulk, 0); s->irq = -1; s->io_addr = io_addr; s->io_size = io_size; s->uhci_pci=dev; bus = usb_alloc_bus (&uhci_device_operations); if (!bus) { kfree (s); return -1; } s->bus = bus; bus->hcpriv = s; /* UHCI specs says devices must have 2 ports, but goes on to say */ /* they may have more but give no way to determine how many they */ /* have, so default to 2 */ /* According to the UHCI spec, Bit 7 is always set to 1. So we try */ /* to use this to our advantage */ for (s->maxports = 0; s->maxports < (io_size - 0x10) / 2; s->maxports++) { unsigned int portstatus; portstatus = inw (io_addr + 0x10 + (s->maxports * 2)); dbg("port %i, adr %x status %x", s->maxports, io_addr + 0x10 + (s->maxports * 2), portstatus); if (!(portstatus & 0x0080)) break; } warn("Detected %d ports", s->maxports); /* This is experimental so anything less than 2 or greater than 8 is */ /* something weird and we'll ignore it */ if (s->maxports < 2 || s->maxports > 8) { dbg("Port count misdetected, forcing to 2 ports"); s->maxports = 2; } s->rh.numports = s->maxports; s->loop_usage=0; if (init_skel (s)) { usb_free_bus (bus); kfree(s); return -1; } request_region (s->io_addr, io_size, MODNAME); reset_hc (s); usb_register_bus (s->bus); start_hc (s); if (request_irq (irq, uhci_interrupt, SA_SHIRQ, MODNAME, s)) { err("request_irq %d failed!",irq); usb_free_bus (bus); reset_hc (s); release_region (s->io_addr, s->io_size); cleanup_skel(s); kfree(s); return -1; } /* Enable PIRQ */ pci_write_config_word (dev, USBLEGSUP, USBLEGSUP_DEFAULT); s->irq = irq; if(uhci_start_usb (s) < 0) { uhci_pci_remove(dev); return -1; } //chain new uhci device into global list pci_set_drvdata(dev, s); devs=s; return 0; } _static int __devinit uhci_pci_probe (struct pci_dev *dev, const struct pci_device_id *id) { int i; if (pci_enable_device(dev) < 0) return -ENODEV; if (!dev->irq) { err("found UHCI device with no IRQ assigned. check BIOS settings!"); return -ENODEV; } pci_set_master(dev); /* Search for the IO base address.. */ for (i = 0; i < 6; i++) { unsigned int io_addr = pci_resource_start(dev, i); unsigned int io_size = pci_resource_len(dev, i); if (!(pci_resource_flags(dev,i) & IORESOURCE_IO)) continue; /* Is it already in use? */ if (check_region (io_addr, io_size)) break; /* disable legacy emulation */ pci_write_config_word (dev, USBLEGSUP, 0); return alloc_uhci(dev, dev->irq, io_addr, io_size); } return -ENODEV; } /*-------------------------------------------------------------------------*/ static const struct pci_device_id __devinitdata uhci_pci_ids [] = { { /* handle any USB UHCI controller */ class: ((PCI_CLASS_SERIAL_USB << 8) | 0x00), class_mask: ~0, /* no matter who makes it */ vendor: PCI_ANY_ID, device: PCI_ANY_ID, subvendor: PCI_ANY_ID, subdevice: PCI_ANY_ID, }, { /* end: all zeroes */ } }; MODULE_DEVICE_TABLE (pci, uhci_pci_ids); static struct pci_driver uhci_pci_driver = { name: "usb-uhci", id_table: &uhci_pci_ids [0], probe: uhci_pci_probe, remove: __devexit_p(uhci_pci_remove), #ifdef CONFIG_PM suspend: uhci_pci_suspend, resume: uhci_pci_resume, #endif /* PM */ }; /*-------------------------------------------------------------------------*/ static int __init uhci_hcd_init (void) { int retval; #ifdef DEBUG_SLAB urb_priv_kmem = kmem_cache_create("urb_priv", sizeof(urb_priv_t), 0, SLAB_HWCACHE_ALIGN, NULL, NULL); if(!urb_priv_kmem) { err("kmem_cache_create for urb_priv_t failed (out of memory)"); return -ENOMEM; } #endif info(VERSTR); #ifdef CONFIG_USB_UHCI_HIGH_BANDWIDTH info("High bandwidth mode enabled"); #endif retval = pci_module_init (&uhci_pci_driver); #ifdef DEBUG_SLAB if (retval < 0 ) { if (kmem_cache_destroy(urb_priv_kmem)) err("urb_priv_kmem remained"); } #endif info(DRIVER_VERSION ":" DRIVER_DESC); return retval; } static void __exit uhci_hcd_cleanup (void) { pci_unregister_driver (&uhci_pci_driver); #ifdef DEBUG_SLAB if(kmem_cache_destroy(urb_priv_kmem)) err("urb_priv_kmem remained"); #endif } module_init (uhci_hcd_init); module_exit (uhci_hcd_cleanup); MODULE_AUTHOR( DRIVER_AUTHOR ); MODULE_DESCRIPTION( DRIVER_DESC ); MODULE_LICENSE("GPL");