📄 uhci.c
字号:
if (urb->status != -EINPROGRESS) { info("uhci_transfer_result: called for URB %p not in flight?", urb); spin_unlock_irqrestore(&urb->lock, flags); return; } 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; spin_unlock_irqrestore(&urb->lock, flags); if (ret == -EINPROGRESS) return; 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) { uhci_add_complete(urb); return; /* <-- note return */ } /* 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); } list_del_init(&urb->urb_list); uhci_add_complete(urb);}static void uhci_unlink_generic(struct uhci *uhci, struct urb *urb){ struct list_head *head, *tmp; struct urb_priv *urbp = urb->hcpriv; /* We can get called when urbp allocation fails, so check */ if (!urbp) return; uhci_dec_fsbr(uhci, urb); /* Safe since it checks */ head = &urbp->td_list; tmp = head->next; while (tmp != head) { struct uhci_td *td = list_entry(tmp, struct uhci_td, list); tmp = tmp->next; /* Control and Isochronous ignore the toggle, so this */ /* is safe for all types */ 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); } } uhci_delete_queued_urb(uhci, urb); /* The interrupt loop will reclaim the QH's */ uhci_remove_qh(uhci, urb);}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; /* 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) return 0; spin_lock_irqsave(&uhci->urb_list_lock, flags); list_del_init(&urb->urb_list); spin_unlock_irqrestore(&uhci->urb_list_lock, flags); uhci_unlink_generic(uhci, urb); /* Short circuit the virtual root hub */ if (urb->dev == uhci->rh.dev) { rh_unlink_urb(urb); uhci_call_completion(urb); } else { if (urb->transfer_flags & USB_ASYNC_UNLINK) { /* urb_list is available now since we called */ /* uhci_unlink_generic already */ urbp->status = urb->status = -ECONNABORTED; spin_lock_irqsave(&uhci->urb_remove_list_lock, flags); /* Check to see if the remove list is empty */ if (list_empty(&uhci->urb_remove_list)) uhci_set_next_interrupt(uhci); list_add(&urb->urb_list, &uhci->urb_remove_list); spin_unlock_irqrestore(&uhci->urb_remove_list_lock, flags); } else { urb->status = -ENOENT; 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; uhci_dec_fsbr(uhci, urb); /* There is a race with updating IOC in here, but it's not worth */ /* trying to fix since this is merely an optimization. The only */ /* time we'd lose is if the status of the packet got updated */ /* and we'd be turning on FSBR next frame anyway, so it's a wash */ urbp->fsbr_timeout = 1; 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) { set_bit(TD_CTRL_IOC_BIT, &td->status); break; } } 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){ int i, len = 1; struct uhci *uhci = (struct uhci *)urb->dev->bus->hcpriv; unsigned int io_addr = uhci->io_addr; __u16 data = 0; struct urb_priv *urbp = (struct urb_priv *)urb->hcpriv; 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; 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 *urbp = (struct urb_priv *)u->hcpriv; tmp = tmp->next; /* Check if the FSBR timed out */ if (urbp->fsbr && !urbp->fsbr_timeout && time_after_eq(jiffies, urbp->fsbrtime + IDLE_TIMEOUT)) uhci_fsbr_timeout(uhci, u); /* Check if the URB timed out */ if (u->timeout && time_after_eq(jiffies, urbp->inserttime + u->timeout)) { list_del(&u->urb_list); list_add_tail(&u->urb_list, &list); } } 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); } /* 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; 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) { uhci->rh.urb = urb; uhci->rh.send = 1; uhci->rh.interval = urb->interval; rh_init_int_timer(urb); return -EINPROGRESS; } 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; 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); wait_ms(50); /* USB v1.1 7.1.7.3 */ uhci->rh.c_p_r[wIndex - 1] = 1; CLR_RH_PORTSTAT(USBPORTSC_PR); udelay(10); SET_RH_PORTSTAT(USBPORTSC_PE); wait_ms(10); SET_RH_PORTSTAT(0xa); OK(0); case RH_PORT_POWER: OK(0); /* port power ** */ case RH_PORT_ENABLE:
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -