📄 uhci.c
字号:
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); mdelay(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); mdelay(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-alt", 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); case RH_GET_INTERFACE | RH_INTERFACE: *(__u8 *)data = 0x00; OK(1); case RH_SET_INTERFACE | RH_INTERFACE: OK(0); default: stat = -EPIPE; } urb->actual_length = len; return stat;}/* * MUST be called with urb->lock acquired */static int rh_unlink_urb(struct urb *urb){ struct uhci *uhci = (struct uhci *)urb->dev->bus->hcpriv; if (uhci->rh.urb == urb) { urb->status = -ENOENT; uhci->rh.send = 0; uhci->rh.urb = NULL; del_timer(&uhci->rh.rh_int_timer); } return 0;}static void uhci_free_pending_qhs(struct uhci *uhci){ struct list_head *tmp, *head; unsigned long flags; spin_lock_irqsave(&uhci->qh_remove_list_lock, flags); head = &uhci->qh_remove_list; tmp = head->next; while (tmp != head) { struct uhci_qh *qh = list_entry(tmp, struct uhci_qh, remove_list); tmp = tmp->next; list_del_init(&qh->remove_list); uhci_free_qh(uhci, qh); } spin_unlock_irqrestore(&uhci->qh_remove_list_lock, flags);}static void uhci_call_completion(struct urb *urb){ struct urb_priv *urbp; struct usb_device *dev = urb->dev; struct uhci *uhci = (struct uhci *)dev->bus->hcpriv; int is_ring = 0, killed, resubmit_interrupt, status; struct urb *nurb; unsigned long flags; spin_lock_irqsave(&urb->lock, flags); urbp = (struct urb_priv *)urb->hcpriv; if (!urbp || !urb->dev) { spin_unlock_irqrestore(&urb->lock, flags); return; } killed = (urb->status == -ENOENT || urb->status == -ECONNABORTED || urb->status == -ECONNRESET); resubmit_interrupt = (usb_pipetype(urb->pipe) == PIPE_INTERRUPT && urb->interval); nurb = urb->next; if (nurb && !killed) { int count = 0; while (nurb && nurb != urb && count < MAX_URB_LOOP) { if (nurb->status == -ENOENT || nurb->status == -ECONNABORTED || nurb->status == -ECONNRESET) { killed = 1; break; } nurb = nurb->next; count++; } if (count == MAX_URB_LOOP) err("uhci_call_completion: too many linked URB's, loop? (first loop)"); /* Check to see if chain is a ring */ is_ring = (nurb == urb); } if (urbp->transfer_buffer_dma_handle) pci_dma_sync_single(uhci->dev, urbp->transfer_buffer_dma_handle, urb->transfer_buffer_length, usb_pipein(urb->pipe) ? PCI_DMA_FROMDEVICE : PCI_DMA_TODEVICE); if (urbp->setup_packet_dma_handle) pci_dma_sync_single(uhci->dev, urbp->setup_packet_dma_handle, sizeof(struct usb_ctrlrequest), PCI_DMA_TODEVICE); status = urbp->status; if (!resubmit_interrupt || killed) /* We don't need urb_priv anymore */ uhci_destroy_urb_priv(urb); if (!killed) urb->status = status; urb->dev = NULL; spin_unlock_irqrestore(&urb->lock, flags); if (urb->complete) urb->complete(urb); if (resubmit_interrupt) /* Recheck the status. The completion handler may have */ /* unlinked the resubmitting interrupt URB */ killed = (urb->status == -ENOENT || urb->status == -ECONNABORTED || urb->status == -ECONNRESET); if (resubmit_interrupt && !killed) { urb->dev = dev; uhci_reset_interrupt(urb); } else { if (is_ring && !killed) { urb->dev = dev; uhci_submit_urb(urb); } else { /* We decrement the usage count after we're done */ /* with everything */ usb_dec_dev_use(dev); } }}static void uhci_finish_completion(struct uhci *uhci){ struct list_head *tmp, *head; unsigned long flags; spin_lock_irqsave(&uhci->complete_list_lock, flags); head = &uhci->complete_list; tmp = head->next; while (tmp != head) { struct urb_priv *urbp = list_entry(tmp, struct urb_priv, complete_list); struct urb *urb = urbp->urb; list_del_init(&urbp->complete_list); spin_unlock_irqrestore(&uhci->complete_list_lock, flags); uhci_call_completion(urb); spin_lock_irqsave(&uhci->complete_list_lock, flags); head = &uhci->complete_list; tmp = head->next; } spin_unlock_irqrestore(&uhci->complete_list_lock, flags);}static void uhci_remove_pending_qhs(struct uhci *uhci){ struct list_head *tmp, *head; unsigned long flags; spin_lock_irqsave(&uhci->urb_remove_list_lock, flags); head = &uhci->urb_remove_list; tmp = head->next; while (tmp != head) { struct urb *urb = list_entry(tmp, struct urb, urb_list); struct urb_priv *urbp = (struct urb_priv *)urb->hcpriv; tmp = tmp->next; list_del_init(&urb->urb_list); urbp->status = urb->status = -ECONNRESET; uhci_add_complete(urb); } spin_unlock_irqrestore(&uhci->urb_remove_list_lock, flags);}static void uhci_interrupt(int irq, void *__uhci, struct pt_regs *regs){ struct uhci *uhci = __uhci; unsigned int io_addr = uhci->io_addr; unsigned short status; struct list_head *tmp, *head; /* * Read the interrupt status, and write it back to clear the * interrupt cause */ status = inw(io_addr + USBSTS); if (!status) /* shared interrupt, not mine */ return; outw(status, io_addr + USBSTS); /* Clear it */ if (status & ~(USBSTS_USBINT | USBSTS_ERROR | USBSTS_RD)) { if (status & USBSTS_HSE) err("%x: host system error, PCI problems?", io_addr); if (status & USBSTS_HCPE) err("%x: host controller process error. something bad happened", io_addr); if ((status & USBSTS_HCH) && !uhci->is_suspended) { err("%x: host controller halted. very bad", io_addr); /* FIXME: Reset the controller, fix the offending TD */ } } if (status & USBSTS_RD) wakeup_hc(uhci); uhci_free_pending_qhs(uhci); uhci_remove_pending_qhs(uhci); uhci_clear_next_interrupt(uhci); /* Walk the list of pending URB's to see which ones completed */ spin_lock(&uhci->urb_list_lock); head = &uhci->urb_list; tmp = head->next; while (tmp != head) { struct urb *urb = list_entry(tmp, struct urb, urb_list); tmp = tmp->next; /* Checks the status and does all of the magic necessary */ uhci_transfer_result(uhci, urb); } spin_unlock(&uhci->urb_list_lock); uhci_finish_completion(uhci);}static void reset_hc(struct uhci *uhci){ unsigned int io_addr = uhci->io_addr; /* Global reset for 50ms */ outw(USBCMD_GRESET, io_addr + USBCMD); wait_ms(50); outw(0, io_addr + USBCMD); wait_ms(10);}static void suspend_hc(struct uhci *uhci){ unsigned int io_addr = uhci->io_addr; dbg("%x: suspend_hc", io_addr); outw(USBCMD_EGSM, io_addr + USBCMD); uhci->is_suspended = 1;}static void wakeup_hc(struct uhci *uhci){ unsigned int io_addr = uhci->io_addr; unsigned int status; dbg("%x: wakeup_hc", io_addr); outw(0, io_addr + USBCMD); /* wait for EOP to be sent */ status = inw(io_addr + USBCMD); while (status & USBCMD_FGR) status = inw(io_addr + USBCMD); uhci->is_suspended = 0; /* Run and mark it configured with a 64-byte max packet */ outw(USBCMD_RS | USBCMD_CF | USBCMD_MAXP, io_addr + USBCMD);}static int ports_active(struct uhci *uhci){ unsigned int io_addr = uhci->io_addr; int connection = 0; int i; for (i = 0; i < uhci->rh.numports; i++) connection |= (inw(io_addr + USBPORTSC1 + i * 2) & 0x1); return connection;}static void start_hc(struct uhci *uhci){ unsigned int io_addr = uhci->io_addr; int timeout = 1000; /* * Reset the HC - this will force us to get a * new notification of any already connected * ports due to the virtual disconnect that it * implies. */ outw(USBCMD_HCRESET, io_addr + USBCMD); while (inw(io_addr + USBCMD) & USBCMD_HCRESET) { if (!--timeout) { printk(KERN_ERR "uhci: USBCMD_HCRESET timed out!\n"); break; } } /* Turn on all interrupts */ outw(USBINTR_TIMEOUT | USBINTR_RESUME | USBINTR_IOC | USBINTR_SP, io_addr + USBINTR); /* Start at frame 0 */ outw(0, io_addr + USBFRNUM); outl(uhci->fl->dma_handle, io_addr + USBFLBASEADD); /* Run and mark it configured with a 64-byte max packet */ outw(USBCMD_RS | USBCMD_CF | USBCMD_MAXP, io_addr + USBCMD);}#ifdef CONFIG_PROC_FSstatic int uhci_num = 0;#endifstatic void free_uhci(struct uhci *uhci){ kfree(uhci);}/* * De-allocate all resources.. */static void release_uhci(struct uhci *uhci){ int i;#ifdef CONFIG_PROC_FS char buf[8];#endif if (uhci->irq >= 0) { free_irq(uhci->irq, uhci); uhci->irq = -1; } for (i = 0; i < UHCI_NUM_SKELQH; i++) if (uhci->skelqh[i]) { uhci_free_qh(uhci, uhci->skelqh[i]); uhci->skelqh[i] = NULL; } for (i = 0; i < UHCI_NUM_SKELTD; i++) if (uhci->skeltd[i]) { uhci_free_td(uhci, uhci->skeltd[i]); uhci->skeltd[i] = NULL; } if (uhci->qh_pool) { pci_pool_destroy(uhci->qh_pool); uhci->qh_pool = NULL; } if (uhci->td_pool) { pci_pool_destroy(uhci->td_pool); uhci->td_pool = NULL; } if (uhci->fl) { pci_free_consistent(uhci->dev, sizeof(*uhci->fl), uhci->fl, uhci->fl->dma_handle); uhci->fl = NULL; } if (uhci->bus) { usb_free_bus(uhci->bus); uhci->bus = NULL; }#ifdef CONFIG_PROC_FS if (uhci->proc_entry) { sprintf(buf, "hc%d", uhci->num); remove_proc_entry(buf, uhci_proc_root); uhci->proc_entry = NULL; }#endif free_uhci(uhci);}/* * Allocate a frame list, and then setup the skeleton * * The hardware doesn't really know any difference * in the queues, but the order does matter for the * protocols higher up. The order is: * * - any isochronous events handled before any * of the queues. We don't do that here, because * we'll create the actual TD entries on demand. * - The first queue is the interrupt queue. * - The second queue is the control queue, split into low and high speed * - The third queue is bulk queue. * - The fourth queue is the bandwidth reclamation queue, which loops back * to the high speed control queue. */static int alloc_uhci(str
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -