📄 usb-uhci.c
字号:
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 (struct urb *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 (struct urb *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 struct urb* search_dev_ep (uhci_t *s, struct urb *urb){ struct list_head *p; struct urb *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, struct urb, 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 (struct urb *urb){ uhci_t *s; urb_priv_t *urb_priv; int ret = 0, type; unsigned long flags; struct urb *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(struct usb_ctrlrequest), 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; struct urb *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, struct urb, urb_list); type = usb_pipetype (urb->pipe); hcpriv = (urb_priv_t*)urb->hcpriv; if ( urb->timeout && time_after(jiffies, hcpriv->started + urb->timeout)) { 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) && time_after(jiffies, hcpriv->started + IDLE_TIMEOUT)) 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[] =
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -