📄 uhci.c
字号:
return ret;}/* * Return the result of a transfer * * MUST be called with urb_list_lock acquired */static void uhci_transfer_result(struct uhci *uhci, struct urb *urb){ int ret = -EINVAL; unsigned long flags; struct urb_priv *urbp; /* The root hub is special */ if (urb->dev == uhci->rh.dev) return; spin_lock_irqsave(&urb->lock, flags); urbp = (struct urb_priv *)urb->hcpriv; if (urb->status != -EINPROGRESS) { info("uhci_transfer_result: called for URB %p not in flight?", urb); goto out; } switch (usb_pipetype(urb->pipe)) { case PIPE_CONTROL: ret = uhci_result_control(urb); break; case PIPE_INTERRUPT: ret = uhci_result_interrupt(urb); break; case PIPE_BULK: ret = uhci_result_bulk(urb); break; case PIPE_ISOCHRONOUS: ret = uhci_result_isochronous(urb); break; } urbp->status = ret; if (ret == -EINPROGRESS) goto out; switch (usb_pipetype(urb->pipe)) { case PIPE_CONTROL: case PIPE_BULK: case PIPE_ISOCHRONOUS: /* Release bandwidth for Interrupt or Isoc. transfers */ /* Spinlock needed ? */ if (urb->bandwidth) usb_release_bandwidth(urb->dev, urb, 1); uhci_unlink_generic(uhci, urb); break; case PIPE_INTERRUPT: /* Interrupts are an exception */ if (urb->interval) goto out_complete; /* Release bandwidth for Interrupt or Isoc. transfers */ /* Spinlock needed ? */ if (urb->bandwidth) usb_release_bandwidth(urb->dev, urb, 0); uhci_unlink_generic(uhci, urb); break; default: info("uhci_transfer_result: unknown pipe type %d for urb %p\n", usb_pipetype(urb->pipe), urb); } /* Remove it from uhci->urb_list */ list_del_init(&urb->urb_list);out_complete: uhci_add_complete(urb);out: spin_unlock_irqrestore(&urb->lock, flags);}/* * MUST be called with urb->lock acquired */static void uhci_unlink_generic(struct uhci *uhci, struct urb *urb){ struct list_head *head, *tmp; struct urb_priv *urbp = urb->hcpriv; int prevactive = 1; /* We can get called when urbp allocation fails, so check */ if (!urbp) return; uhci_dec_fsbr(uhci, urb); /* Safe since it checks */ /* * Now we need to find out what the last successful toggle was * so we can update the local data toggle for the next transfer * * There's 3 way's the last successful completed TD is found: * * 1) The TD is NOT active and the actual length < expected length * 2) The TD is NOT active and it's the last TD in the chain * 3) The TD is active and the previous TD is NOT active * * Control and Isochronous ignore the toggle, so this is safe * for all types */ head = &urbp->td_list; tmp = head->next; while (tmp != head) { struct uhci_td *td = list_entry(tmp, struct uhci_td, list); tmp = tmp->next; if (!(td->status & TD_CTRL_ACTIVE) && (uhci_actual_length(td->status) < uhci_expected_length(td->info) || tmp == head)) usb_settoggle(urb->dev, uhci_endpoint(td->info), uhci_packetout(td->info), uhci_toggle(td->info) ^ 1); else if ((td->status & TD_CTRL_ACTIVE) && !prevactive) usb_settoggle(urb->dev, uhci_endpoint(td->info), uhci_packetout(td->info), uhci_toggle(td->info)); prevactive = td->status & TD_CTRL_ACTIVE; } uhci_delete_queued_urb(uhci, urb); /* The interrupt loop will reclaim the QH's */ uhci_remove_qh(uhci, urbp->qh); urbp->qh = NULL;}static int uhci_unlink_urb(struct urb *urb){ struct uhci *uhci; unsigned long flags; struct urb_priv *urbp = urb->hcpriv; if (!urb) return -EINVAL; if (!urb->dev || !urb->dev->bus || !urb->dev->bus->hcpriv) return -ENODEV; uhci = (struct uhci *)urb->dev->bus->hcpriv; spin_lock_irqsave(&uhci->urb_list_lock, flags); spin_lock(&urb->lock); /* Release bandwidth for Interrupt or Isoc. transfers */ /* Spinlock needed ? */ 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; } } if (urb->status != -EINPROGRESS) { spin_unlock(&urb->lock); spin_unlock_irqrestore(&uhci->urb_list_lock, flags); return 0; } list_del_init(&urb->urb_list); uhci_unlink_generic(uhci, urb); /* Short circuit the virtual root hub */ if (urb->dev == uhci->rh.dev) { rh_unlink_urb(urb); spin_unlock(&urb->lock); spin_unlock_irqrestore(&uhci->urb_list_lock, flags); uhci_call_completion(urb); } else { if (urb->transfer_flags & USB_ASYNC_UNLINK) { urbp->status = urb->status = -ECONNABORTED; spin_lock(&uhci->urb_remove_list_lock); /* If we're the first, set the next interrupt bit */ if (list_empty(&uhci->urb_remove_list)) uhci_set_next_interrupt(uhci); list_add(&urb->urb_list, &uhci->urb_remove_list); spin_unlock(&uhci->urb_remove_list_lock); spin_unlock(&urb->lock); spin_unlock_irqrestore(&uhci->urb_list_lock, flags); } else { urb->status = -ENOENT; spin_unlock(&urb->lock); spin_unlock_irqrestore(&uhci->urb_list_lock, flags); if (in_interrupt()) { /* wait at least 1 frame */ static int errorcount = 10; if (errorcount--) dbg("uhci_unlink_urb called from interrupt for urb %p", urb); udelay(1000); } else schedule_timeout(1+1*HZ/1000); uhci_call_completion(urb); } } return 0;}static int uhci_fsbr_timeout(struct uhci *uhci, struct urb *urb){ struct urb_priv *urbp = (struct urb_priv *)urb->hcpriv; struct list_head *head, *tmp; int count = 0; uhci_dec_fsbr(uhci, urb); urbp->fsbr_timeout = 1; /* * Ideally we would want to fix qh->element as well, but it's * read/write by the HC, so that can introduce a race. It's not * really worth the hassle */ head = &urbp->td_list; tmp = head->next; while (tmp != head) { struct uhci_td *td = list_entry(tmp, struct uhci_td, list); tmp = tmp->next; /* * Make sure we don't do the last one (since it'll have the * TERM bit set) as well as we skip every so many TD's to * make sure it doesn't hog the bandwidth */ if (tmp != head && (count % DEPTH_INTERVAL) == (DEPTH_INTERVAL - 1)) td->link |= UHCI_PTR_DEPTH; count++; } 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 *dev){ struct uhci *uhci = (struct uhci *)dev->bus->hcpriv; return inw(uhci->io_addr + USBFRNUM);}struct usb_operations uhci_device_operations = { uhci_alloc_dev, uhci_free_dev, uhci_get_current_frame_number, uhci_submit_urb, uhci_unlink_urb};/* 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, Bit 5 Remote-wakeup, 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(struct urb *urb){ struct uhci *uhci = (struct uhci *)urb->dev->bus->hcpriv; struct urb_priv *urbp = (struct urb_priv *)urb->hcpriv; unsigned int io_addr = uhci->io_addr; unsigned long flags; int i, len = 1; __u16 data = 0; spin_lock_irqsave(&urb->lock, flags); 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; urbp->status = 0; spin_unlock_irqrestore(&urb->lock, flags); 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); uhci_call_completion(urb); } return 0;}/* Virtual Root Hub INTs are polled by this timer every "interval" ms */static int rh_init_int_timer(struct urb *urb);static void rh_int_timer_do(unsigned long ptr){ struct urb *urb = (struct urb *)ptr; struct uhci *uhci = (struct uhci *)urb->dev->bus->hcpriv; struct list_head list, *tmp, *head; unsigned long flags; if (uhci->rh.send) rh_send_irq(urb); INIT_LIST_HEAD(&list); spin_lock_irqsave(&uhci->urb_list_lock, flags); head = &uhci->urb_list; tmp = head->next; while (tmp != head) { struct urb *u = list_entry(tmp, struct urb, urb_list); struct urb_priv *up = (struct urb_priv *)u->hcpriv; tmp = tmp->next; spin_lock(&u->lock); /* Check if the FSBR timed out */ if (up->fsbr && !up->fsbr_timeout && time_after_eq(jiffies, up->fsbrtime + IDLE_TIMEOUT)) uhci_fsbr_timeout(uhci, u); /* Check if the URB timed out */ if (u->timeout && time_after_eq(jiffies, up->inserttime + u->timeout)) { list_del(&u->urb_list); list_add_tail(&u->urb_list, &list); } spin_unlock(&u->lock); } spin_unlock_irqrestore(&uhci->urb_list_lock, flags); head = &list; tmp = head->next; while (tmp != head) { struct urb *u = list_entry(tmp, struct urb, urb_list); tmp = tmp->next; u->transfer_flags |= USB_ASYNC_UNLINK | USB_TIMEOUT_KILLED; uhci_unlink_urb(u); } /* Really disable FSBR */ if (!uhci->fsbr && uhci->fsbrtimeout && time_after_eq(jiffies, uhci->fsbrtimeout)) { uhci->fsbrtimeout = 0; uhci->skel_term_qh->link = UHCI_PTR_TERM; } /* enter global suspend if nothing connected */ if (!uhci->is_suspended && !ports_active(uhci)) suspend_hc(uhci); rh_init_int_timer(urb);}/* Root Hub INTs are polled by this timer */static int rh_init_int_timer(struct urb *urb){ struct uhci *uhci = (struct 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 * (urb->interval < 30 ? 30 : urb->interval)) / 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(struct urb *urb){ struct uhci *uhci = (struct uhci *)urb->dev->bus->hcpriv; unsigned int pipe = urb->pipe; struct usb_ctrlrequest *cmd = (struct usb_ctrlrequest *)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) { uhci->rh.urb = urb; uhci->rh.send = 1; uhci->rh.interval = urb->interval; rh_init_int_timer(urb); return -EINPROGRESS; } bmRType_bReq = cmd->bRequestType | cmd->bRequest << 8; wValue = le16_to_cpu(cmd->wValue); wIndex = le16_to_cpu(cmd->wIndex); wLength = le16_to_cpu(cmd->wLength); for (i = 0; i < 8; i++) uhci->rh.c_p_r[i] = 0; 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);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -