📄 uhci.c
字号:
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;}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 urb_priv *)urb->hcpriv; 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; killed = (urb->status == -ENOENT || urb->status == -ECONNABORTED || urb->status == -ECONNRESET); resubmit_interrupt = (usb_pipetype(urb->pipe) == PIPE_INTERRUPT && urb->interval && !killed); 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); } status = urbp->status; if (!resubmit_interrupt) /* We don't need urb_priv anymore */ uhci_destroy_urb_priv(urb); if (!killed) urb->status = status; 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(devrequest), PCI_DMA_TODEVICE); urb->dev = NULL; if (urb->complete) urb->complete(urb); if (resubmit_interrupt) { 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; tmp = tmp->next; list_del_init(&urbp->complete_list); uhci_call_completion(urb); } 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_call_completion(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(struct pci_dev *dev, unsigned int io_addr, unsigned int io_size){ struct uhci *uhci; int retval; char buf[8], *bufp = buf; int i, port; struct usb_bus *bus; dma_addr_t dma_handle;#ifdef CONFIG_PROC_FS struct proc_dir_entry *ent;#endif retval = -ENODEV; if (pci_enable_device(dev) < 0) { err("couldn't enable PCI device"); goto err_enable_device; } if (!dev->irq) { err("found UHCI device with no IRQ assigned. check BIOS settings!"); goto err_invalid_irq; } if (!pci_dma_supported(dev, 0xFFFFFFFF)) { err("PCI subsystem doesn't support 32 bit addressing?"); goto err_pci_dma_supported; } retval = -EBUSY; if (!request_region(io_addr, io_size, "usb-uhci")) { err("couldn't allocate I/O range %x - %x", io_addr, io_addr + io_size - 1); goto err_request_region; } pci_set_master(dev);#ifndef __sparc__ sprintf(buf, "%d", dev->irq);#else bufp = __irq_itoa(dev->irq);#endif printk(KERN_INFO __FILE__ ": USB UHCI at I/O 0x%x, IRQ %s\n", io_addr, bufp); if (pci_set_dma_mask(dev, 0xFFFFFFFF)) { err("couldn't set PCI dma mask"); retval = -ENODEV; goto err_pci_set_dma_mask; } uhci = kmalloc(sizeof(*uhci), GFP_KERNEL); if (!uhci) { err("couldn't allocate uhci structure"); retval = -ENOMEM; goto err_alloc_uhci; } uhci->dev = dev; uhci->io_addr = io_addr; uhci->io_size = io_size; pci_set_drvdata(dev, uhci);#ifdef CONFIG_PROC_FS uhci->num = uhci_num++; sprintf(buf, "hc%d", uhci->num); ent = create_proc_entry(buf, S_IFREG|S_IRUGO|S_IWUSR, uhci_proc_root); if (!ent) { err("couldn't create uhci proc entry"); retval = -ENOMEM; goto err_create_proc_entry; } ent->data = uhci; ent->proc_fops = &uhci_proc_operations; ent->size = 0; uhci->proc_entry = ent;#endif /* Reset here so we don't get any interrupts from an old setup */ /* or broken setup */ reset_hc(uhci); spin_lock_init(&uhci->qh_remove_list_lock); INIT_LIST_HEAD(&uhci->qh_remove_list); spin_lock_init(&uhci->urb_remove_list_lock); INIT_LIST_HEAD(&uhci->urb_remove_list); spin_lock_init(&uhci->urb_list_lock); INIT_LIST_HEAD(&uhci->urb_list); spin_lock_init(&uhci->complete_list_lock); INIT_LIST_HEAD(&uhci->complete_list); spin_lock_init(&uhci->frame_list_lock); /* We need exactly one page (per UHCI specs), how convenient */ /* We assume that one page is atleast 4k (1024 frames * 4 bytes) */#if PAGE_SIZE < (4 * 1024)#error PAGE_SIZE is not atleast 4k#endif uhci->fl = pci_alloc_consistent(uhci->dev, sizeof(*uhci->fl), &dma_handle); if (!uhci->fl) { err("unable to allocate consistent
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -