📄 usb-uhci.c
字号:
{ 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 len = 1; int i; uhci_t *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 = 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); urb->complete (urb); } return 0;}/*-------------------------------------------------------------------------*//* Virtual Root Hub INTs are polled by this timer every "intervall" ms */_static int rh_init_int_timer (struct urb *urb);_static void rh_int_timer_do (unsigned long ptr){ int len; struct urb *urb = (struct urb*) ptr; uhci_t *uhci = urb->dev->bus->hcpriv; if (uhci->rh.send) { len = rh_send_irq (urb); if (len > 0) { urb->actual_length = len; if (urb->complete) urb->complete (urb); } } rh_init_int_timer (urb);}/*-------------------------------------------------------------------------*//* Root Hub INTs are polled by this timer, polling interval 20ms */_static int rh_init_int_timer (struct urb *urb){ uhci_t *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 * 20) / 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 usb_device *usb_dev = urb->dev; uhci_t *uhci = usb_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) { dbg("Root-Hub submit IRQ: every %d ms", urb->interval); uhci->rh.urb = urb; uhci->rh.send = 1; uhci->rh.interval = urb->interval; rh_init_int_timer (urb); return 0; } 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; dbg("Root-Hub: adr: %2x cmd(%1x): %04x %04x %04x %04x", uhci->rh.devnum, 8, bmRType_bReq, wValue, wIndex, wLength); 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); uhci_wait_ms (10); uhci->rh.c_p_r[wIndex - 1] = 1; CLR_RH_PORTSTAT (USBPORTSC_PR); udelay (10); SET_RH_PORTSTAT (USBPORTSC_PE); uhci_wait_ms (10); SET_RH_PORTSTAT (0xa); OK (0); case (RH_PORT_POWER): OK (0); /* port power ** */ case (RH_PORT_ENABLE): SET_RH_PORTSTAT (USBPORTSC_PE); OK (0); } break; case RH_SET_ADDRESS: uhci->rh.devnum = wValue; OK (0); case RH_GET_DESCRIPTOR: switch ((wValue & 0xff00) >> 8) { case (0x01): /* device descriptor */ len = min_t(unsigned int, leni, min_t(unsigned int, sizeof (root_hub_dev_des), wLength)); memcpy (data, root_hub_dev_des, len); OK (len); case (0x02): /* configuration descriptor */ len = min_t(unsigned int, leni, min_t(unsigned int, sizeof (root_hub_config_des), wLength)); memcpy (data, root_hub_config_des, len); OK (len); case (0x03): /* string descriptors */ len = usb_root_hub_string (wValue & 0xff, uhci->io_addr, "UHCI", data, wLength); if (len > 0) { OK(min_t(int, leni, len)); } else stat = -EPIPE; } break; case RH_GET_DESCRIPTOR | RH_CLASS: root_hub_hub_des[2] = uhci->rh.numports; len = min_t(unsigned int, leni, min_t(unsigned int, sizeof (root_hub_hub_des), wLength)); memcpy (data, root_hub_hub_des, len); OK (len); case RH_GET_CONFIGURATION: *(__u8 *) data = 0x01; OK (1); case RH_SET_CONFIGURATION: OK (0); default: stat = -EPIPE; } dbg("Root-Hub stat port1: %x port2: %x", inw (io_addr + USBPORTSC1), inw (io_addr + USBPORTSC2)); urb->actual_length = len; urb->status = stat; urb->dev=NULL; if (urb->complete) urb->complete (urb); return 0;}/*-------------------------------------------------------------------------*/_static int rh_unlink_urb (struct urb *urb){ uhci_t *uhci = urb->dev->bus->hcpriv; if (uhci->rh.urb==urb) { dbg("Root-Hub unlink IRQ"); uhci->rh.send = 0; del_timer (&uhci->rh.rh_int_timer); } return 0;}/*-------------------------------------------------------------------*//* * Map status to standard result codes * * <status> is (td->status & 0xFE0000) [a.k.a. uhci_status_bits(td->status) * <dir_out> is True for output TDs and False for input TDs. */_static int uhci_map_status (int status, int dir_out){ if (!status) return 0; if (status & TD_CTRL_BITSTUFF) /* Bitstuff error */ return -EPROTO; if (status & TD_CTRL_CRCTIMEO) { /* CRC/Timeout */ if (dir_out) return -ETIMEDOUT; else return -EILSEQ; } if (status & TD_CTRL_NAK) /* NAK */ return -ETIMEDOUT; if (status & TD_CTRL_BABBLE) /* Babble */ return -EOVERFLOW; if (status & TD_CTRL_DBUFERR) /* Buffer error */ return -ENOSR; if (status & TD_CTRL_STALLED) /* Stalled */ return -EPIPE; if (status & TD_CTRL_ACTIVE) /* Active */ return 0; return -EPROTO;}/* * Only the USB core should call uhci_alloc_dev and uhci_free_dev */_static int uhci_alloc_dev (struct usb_device *usb_dev){ return 0;}_static void uhci_unlink_urbs(uhci_t *s, struct usb_device *usb_dev, int remove_all){ unsigned long flags; struct list_head *p; struct list_head *p2; struct urb *urb; spin_lock_irqsave (&s->urb_list_lock, flags); p = s->urb_list.prev; while (p != &s->urb_list) { p2 = p; p = p->prev ; urb = list_entry (p2, struct urb, urb_list); dbg("urb: %p, dev %p, %p", urb, usb_dev,urb->dev); //urb->transfer_flags |=USB_ASYNC_UNLINK; if (remove_all || (usb_dev == urb->dev)) { spin_unlock_irqrestore (&s->urb_list_lock, flags); warn("forced removing of queued URB %p due to disconnect",urb); uhci_unlink_urb(urb); urb->dev = NULL; // avoid further processing of this URB spin_lock_irqsave (&s->urb_list_lock, flags); p = s->urb_list.prev; } } spin_unlock_irqrestore (&s->urb_list_lock, flags);}_static int uhci_free_dev (struct usb_device *usb_dev){ uhci_t *s; if(!usb_dev || !usb_dev->bus || !usb_dev->bus->hcpriv) return -EINVAL; s=(uhci_t*) usb_dev->bus->hcpriv; uhci_unlink_urbs(s, usb_dev, 0); 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 *usb_dev){ return UHCI_GET_CURRENT_FRAME ((uhci_t*) usb_dev->bus->hcpriv);}struct usb_operations uhci_device_operations ={ uhci_alloc_dev, uhci_free_dev, uhci_get_current_frame_number, uhci_submit_urb, uhci_unlink_urb};_static void correct_data_toggles(struct urb *urb){ usb_settoggle (urb->dev, usb_pipeendpoint (urb->pipe), usb_pipeout (urb->pipe), !usb_gettoggle (urb->dev, usb_pipeendpoint (urb->pipe), usb_pipeout (urb->pipe))); while(urb) { urb_priv_t *priv=urb->hcpriv; uhci_desc_t *qh = list_entry (priv->desc_list.next, uhci_desc_t, desc_list); struct list_head *p = qh->vertical.next; uhci_desc_t *td; dbg("URB to correct %p\n", urb); for (; p != &qh->vertical; p = p->next) { td = list_entry (p, uhci_desc_t, vertical); td->hw.td.info^=cpu_to_le32(1<<TD_TOKEN_TOGGLE); } urb=priv->next_queued_urb; }}/* * For IN-control transfers, process_transfer gets a bit more complicated, * since there are devices that return less data (eg. strings) than they * have announced. This leads to a queue abort due to the short packet, * the status stage is not executed. If this happens, the status stage * is manually re-executed. * mode: PROCESS_TRANSFER_REGULAR: regular (unlink QH) * PROCESS_TRANSFER_DONT_UNLINK: QHs already unlinked (for async unlink_urb) */_static int process_transfer (uhci_t *s, struct urb *urb, int mode){ int ret = 0; urb_priv_t *urb_priv = urb->hcpriv; struct list_head *qhl = urb_priv->desc_list.next; uhci_desc_t *qh = list_entry (qhl, uhci_desc_t, desc_list); struct list_head *p = qh->vertical.next; uhci_desc_t *desc= list_entry (urb_priv->desc_list.prev, uhci_desc_t, desc_list); uhci_desc_t *last_desc = list_entry (desc->vertical.prev, uhci_desc_t, vertical); int data_toggle = usb_gettoggle (urb->dev, usb_pipeendpoint (urb->pipe), usb_pipeout (urb->pipe)); // save initial data_toggle int maxlength; // extracted and remapped info from TD int actual_length; int status = 0; //dbg("process_transfer: urb %p, urb_priv %p, qh %p last_desc %p\n",urb,urb_priv, qh, last_desc); /* if the status phase has been retriggered and the queue is empty or the last status-TD is inactive, the retriggered status stage is completed */ if (urb_priv->flags && ((qh->hw.qh.element == cpu_to_le32(UHCI_PTR_TERM)) || !is_td_
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -