📄 uhci.c
字号:
tmp = head->next; while (tmp != head) { u = list_entry(tmp, struct urb, urb_list); tmp = tmp->next; if (u->dev == urb->dev && u->pipe == urb->pipe) goto found; } u = NULL;found: nested_unlock(&uhci->urblist_lock, flags); return u;}static int uhci_submit_urb(struct urb *urb){ int ret = -EINVAL; struct uhci *uhci; unsigned long flags; struct urb *u; int bustime; if (!urb) return -EINVAL; if (!urb->dev || !urb->dev->bus || !urb->dev->bus->hcpriv) return -ENODEV; uhci = (struct uhci *)urb->dev->bus->hcpriv; /* Short circuit the virtual root hub */ if (usb_pipedevice(urb->pipe) == uhci->rh.devnum) return rh_submit_urb(urb); u = uhci_find_urb_ep(uhci, urb); if (u && !(urb->transfer_flags & USB_QUEUE_BULK)) return -ENXIO; usb_inc_dev_use(urb->dev); spin_lock_irqsave(&urb->lock, flags); if (!uhci_alloc_urb_priv(urb)) { spin_unlock_irqrestore(&urb->lock, flags); usb_dec_dev_use(urb->dev); return -ENOMEM; } switch (usb_pipetype(urb->pipe)) { case PIPE_CONTROL: ret = uhci_submit_control(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_interrupt(urb); if (ret == -EINPROGRESS) usb_claim_bandwidth(urb->dev, urb, bustime, 0); } } else /* bandwidth is already set */ ret = uhci_submit_interrupt(urb); break; case PIPE_BULK: ret = uhci_submit_bulk(urb, u); break; 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; break; } ret = uhci_submit_isochronous(urb); if (ret == -EINPROGRESS) usb_claim_bandwidth(urb->dev, urb, bustime, 1); } else /* bandwidth is already set */ ret = uhci_submit_isochronous(urb); break; } urb->status = ret; spin_unlock_irqrestore(&urb->lock, flags); if (ret == -EINPROGRESS) ret = 0; else { uhci_unlink_generic(urb); usb_dec_dev_use(urb->dev); } return ret;}/* * Return the result of a transfer * * Must be called with urblist_lock acquired */static void uhci_transfer_result(struct urb *urb){ struct usb_device *dev = urb->dev; struct urb *turb; int proceed = 0, is_ring = 0; int ret = -EINVAL; unsigned long flags; spin_lock_irqsave(&urb->lock, flags); 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; } urb->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(urb); break; case PIPE_INTERRUPT: /* Interrupts are an exception */ if (urb->interval) { urb->complete(urb); uhci_reset_interrupt(urb); return; } /* Release bandwidth for Interrupt or Isoc. transfers */ /* Spinlock needed ? */ if (urb->bandwidth) usb_release_bandwidth(urb->dev, urb, 0); uhci_unlink_generic(urb); break; } if (urb->next) { turb = urb->next; do { if (turb->status != -EINPROGRESS) { proceed = 1; break; } turb = turb->next; } while (turb && turb != urb && turb != urb->next); if (turb == urb || turb == urb->next) is_ring = 1; } if (urb->complete && !proceed) { urb->complete(urb); if (!proceed && is_ring) uhci_submit_urb(urb); } if (proceed && urb->next) { turb = urb->next; do { if (turb->status != -EINPROGRESS && uhci_submit_urb(turb) != 0) turb = turb->next; } while (turb && turb != urb->next); if (urb->complete) urb->complete(urb); } /* We decrement the usage count after we're done with everything */ usb_dec_dev_use(dev);}static int uhci_unlink_generic(struct urb *urb){ struct urb_priv *urbp = urb->hcpriv; struct uhci *uhci = (struct uhci *)urb->dev->bus->hcpriv; if (!urbp) return -EINVAL; uhci_dec_fsbr(uhci, urb); /* Safe since it checks */ uhci_remove_urb_list(uhci, urb); if (urbp->qh) /* The interrupt loop will reclaim the QH's */ uhci_remove_qh(uhci, urbp->qh); if (!list_empty(&urbp->urb_queue_list)) uhci_delete_queued_urb(uhci, urb); uhci_destroy_urb_priv(urb); urb->dev = NULL; return 0;}static int uhci_unlink_urb(struct urb *urb){ struct uhci *uhci; int ret = 0; unsigned long flags; if (!urb) return -EINVAL; if (!urb->dev || !urb->dev->bus) return -ENODEV; uhci = (struct uhci *)urb->dev->bus->hcpriv; /* Short circuit the virtual root hub */ if (usb_pipedevice(urb->pipe) == uhci->rh.devnum) return rh_unlink_urb(urb); /* 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) { uhci_unlink_generic(urb); if (urb->transfer_flags & USB_ASYNC_UNLINK) { urb->status = -ECONNABORTED; spin_lock_irqsave(&uhci->urb_remove_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_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); if (urb->complete) urb->complete(urb); } } return ret;}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->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) { td->status |= TD_CTRL_IOC; 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; 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 = USB_ST_NOERROR; 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 USB_ST_NOERROR;}/*-------------------------------------------------------------------------*//* 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 *tmp, *head = &uhci->urb_list; struct urb_priv *urbp; int len; unsigned long flags; if (uhci->rh.send) { len = rh_send_irq(urb); if (len > 0) { urb->actual_length = len; if (urb->complete) urb->complete(urb); } } nested_lock(&uhci->urblist_lock, flags); tmp = head->next; while (tmp != head) { struct urb *u = list_entry(tmp, urb_t, urb_list); tmp = tmp->next; urbp = (struct urb_priv *)u->hcpriv; if (urbp) { /* Check if the FSBR timed out */ if (urbp->fsbr && time_after_eq(jiffies, urbp->inserttime + IDLE_TIMEOUT)) uhci_fsbr_timeout(uhci, u); /* Check if the URB timed out */ if (u->timeout && time_after_eq(jiffies, u->timeout)) { u->transfer_flags |= USB_ASYNC_UNLINK | USB_TIMEOUT_KILLED; uhci_unlink_urb(u); } } } nested_unlock(&uhci->urblist_lock, flags); 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 = USB_ST_NOERROR; 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 USB_ST_NOERROR; } 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); */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -