📄 usb-uhci.c
字号:
} if (mode) { qh->last_used = now; list_add_tail (&qh->horizontal, &s->free_desc); // mark for later deletion }}/*-------------------------------------------------------------------*/// unlinks an urb by dequeuing its qh, waits some frames and forgets it_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; spin_lock_irqsave (&s->urb_list_lock, flags); if (urb->status == -EINPROGRESS) { // URB probably still in work dequeue_urb (s, urb); s->unlink_urb_done=1; spin_unlock_irqrestore (&s->urb_list_lock, flags); urb->status = -ENOENT; // mark urb as killed urb_priv = urb->hcpriv; switch (usb_pipetype (urb->pipe)) { case PIPE_ISOCHRONOUS: case PIPE_INTERRUPT: uhci_clean_iso_step1(s, urb_priv); 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); spin_lock_irqsave (&s->urb_list_lock, flags); uhci_clean_transfer(s, urb, qh, 1); spin_unlock_irqrestore (&s->urb_list_lock, flags); uhci_wait_ms(1); } #ifdef DEBUG_SLAB kmem_cache_free (urb_priv_kmem, urb->hcpriv);#else kfree (urb->hcpriv);#endif if (urb->complete) { dbg("unlink_urb: calling completion"); urb->complete ((struct urb *) urb); } usb_dec_dev_use (urb->dev); return 0; } 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 pipe,now; 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 (force || ((urb_priv->started != 0xffffffff) && (urb_priv->started != now))) { async_dbg("async cleanup %p",urb); switch (usb_pipetype (urb->pipe)) { // process descriptors case PIPE_CONTROL: process_transfer (s, urb, 2); break; case PIPE_BULK: if (!s->avoid_bulk.counter) process_transfer (s, urb, 2); // don't unlink (already done) else continue; break; case PIPE_ISOCHRONOUS: process_iso (s, urb, 1); // 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 pipe = urb->pipe; // completion may destroy all... dev = urb->dev; urb_priv = urb->hcpriv; if (urb->complete) { spin_unlock(&s->urb_list_lock); 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 usb_dec_dev_use (dev);#ifdef DEBUG_SLAB kmem_cache_free (urb_priv_kmem, urb_priv);#else kfree (urb_priv);#endif switch (usb_pipetype (pipe)) { case PIPE_ISOCHRONOUS: case PIPE_INTERRUPT: uhci_clean_iso_step2(s, urb_priv); break; } list_del (&urb->urb_list); } }}/*-------------------------------------------------------------------*/_static int uhci_unlink_urb_async (uhci_t *s,urb_t *urb){ uhci_desc_t *qh; urb_priv_t *urb_priv; async_dbg("unlink_urb_async called %p",urb); if (urb->status == -EINPROGRESS) { ((urb_priv_t*)urb->hcpriv)->started = ~0; dequeue_urb (s, urb); list_add_tail (&urb->urb_list, &s->urb_unlinked); // store urb s->unlink_urb_done = 1; urb->status = -ECONNABORTED; // mark urb as "waiting to be killed" urb_priv = (urb_priv_t*)urb->hcpriv; switch (usb_pipetype (urb->pipe)) { case PIPE_ISOCHRONOUS: case PIPE_INTERRUPT: 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, 0); break; } ((urb_priv_t*)urb->hcpriv)->started = UHCI_GET_CURRENT_FRAME(s); } return -EINPROGRESS;}/*-------------------------------------------------------------------*/_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); ret = uhci_unlink_urb_async(s, 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 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, ret; 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; ret = alloc_td (&td, UHCI_PTR_DEPTH); if (ret) 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, virt_to_bus (urb->transfer_buffer)); list_add_tail (&td->desc_list, &urb_priv->desc_list); urb->status = -EINPROGRESS; 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; int pipe=urb->pipe; int maxsze = usb_maxpacket (urb->dev, pipe, usb_pipeout (pipe)); 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; } // First try to get all TDs 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) { // allows ISO striping by setting length to zero in iso_descriptor tdm[n] = 0; continue; } if(urb->iso_frame_desc[n].length > maxsze) {#ifdef ISO_SANITY_CHECK err("submit_iso: urb->iso_frame_desc[%d].length(%d)>%d",n , urb->iso_frame_desc[n].length, maxsze); tdm[n] = 0; ret=-EINVAL; goto inval;#endif } ret = alloc_td (&td, UHCI_PTR_DEPTH); inval: if (ret) { int i; // Cleanup allocated TDs for (i = 0; i < n; n++) if (tdm[i]) delete_desc(tdm[i]); kfree (tdm); goto err; } last=n; tdm[n] = td; } status = TD_CTRL_ACTIVE | TD_CTRL_IOS; //| (urb->transfer_flags&USB_DISABLE_SPD?0:TD_CTRL_SPD); 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; fill_td (td, status, destination | (((urb->iso_frame_desc[n].length - 1) & 0x7ff) << 21), virt_to_bus (urb->transfer_buffer + urb->iso_frame_desc[n].offset)); list_add_tail (&td->desc_list, &urb_priv->desc_list); if (n == last) { urb->status = -EINPROGRESS; queue_urb (s, urb); } insert_td_horizontal (s, s->iso_td[(urb->start_frame + n) & 1023], td); // store in iso-tds //uhci_show_td(td); } 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; unsigned long flags; urb_t *bulk_urb=NULL; if (!urb->dev || !urb->dev->bus)
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -