📄 usb-ohci.c
字号:
(temp & RH_PS_PPS) ? " PPS" : "", (temp & RH_PS_PRS) ? " PRS" : "", (temp & RH_PS_POCI) ? " POCI" : "", (temp & RH_PS_PSS) ? " PSS" : "", (temp & RH_PS_PES) ? " PES" : "", (temp & RH_PS_CCS) ? " CCS" : "" ); }}static void ohci_dump (ohci_t *controller, int verbose){ dbg ("OHCI controller usb-%s state", controller->ohci_dev->slot_name); // dumps some of the state we know about ohci_dump_status (controller); if (verbose) ep_print_int_eds (controller, "hcca"); dbg ("hcca frame #%04x", controller->hcca->frame_no); ohci_dump_roothub (controller, 1);}#endif/*-------------------------------------------------------------------------* * Interface functions (URB) *-------------------------------------------------------------------------*//* return a request to the completion handler */ static int sohci_return_urb (struct ohci *hc, struct urb * urb){ urb_priv_t * urb_priv = urb->hcpriv; struct urb * urbt; int i; if (!urb_priv) return -1; /* urb already unlinked */ /* just to be sure */ if (!urb->complete) { urb_rm_priv_locked (urb); ohci_complete_add(hc, urb); /* Just usb_dec_dev_use */ return -1; } #ifdef DEBUG urb_print (urb, "RET", usb_pipeout (urb->pipe));#endif switch (usb_pipetype (urb->pipe)) { case PIPE_INTERRUPT: pci_unmap_single (hc->ohci_dev, urb_priv->td [0]->data_dma, urb->transfer_buffer_length, usb_pipeout (urb->pipe) ? PCI_DMA_TODEVICE : PCI_DMA_FROMDEVICE); if (urb->interval) { urb->complete (urb); /* implicitly requeued */ urb->actual_length = 0; urb->status = -EINPROGRESS; td_submit_urb (urb); } else { urb_rm_priv_locked (urb); ohci_complete_add(hc, urb); } break; case PIPE_ISOCHRONOUS: for (urbt = urb->next; urbt && (urbt != urb); urbt = urbt->next); if (urbt) { /* send the reply and requeue URB */ pci_unmap_single (hc->ohci_dev, urb_priv->td [0]->data_dma, urb->transfer_buffer_length, usb_pipeout (urb->pipe) ? PCI_DMA_TODEVICE : PCI_DMA_FROMDEVICE); urb->complete (urb); urb->actual_length = 0; urb->status = USB_ST_URB_PENDING; urb->start_frame = urb_priv->ed->last_iso + 1; if (urb_priv->state != URB_DEL) { for (i = 0; i < urb->number_of_packets; i++) { urb->iso_frame_desc[i].actual_length = 0; urb->iso_frame_desc[i].status = -EXDEV; } td_submit_urb (urb); } } else { /* unlink URB, call complete */ urb_rm_priv_locked (urb); ohci_complete_add(hc, urb); } break; case PIPE_BULK: case PIPE_CONTROL: /* unlink URB, call complete */ urb_rm_priv_locked (urb); ohci_complete_add(hc, urb); break; } return 0;}/*-------------------------------------------------------------------------*//* get a transfer request */ static int sohci_submit_urb (struct urb * urb){ ohci_t * ohci; ed_t * ed; urb_priv_t * urb_priv; unsigned int pipe = urb->pipe; int maxps = usb_maxpacket (urb->dev, pipe, usb_pipeout (pipe)); int i, size = 0; unsigned long flags; int bustime = 0; int mem_flags = GFP_ATOMIC; if (!urb->dev || !urb->dev->bus) return -ENODEV; if (urb->hcpriv) /* urb already in use */ return -EINVAL;// if(usb_endpoint_halted (urb->dev, usb_pipeendpoint (pipe), usb_pipeout (pipe))) // return -EPIPE; usb_inc_dev_use (urb->dev); ohci = (ohci_t *) urb->dev->bus->hcpriv; #ifdef DEBUG urb_print (urb, "SUB", usb_pipein (pipe));#endif /* handle a request to the virtual root hub */ if (usb_pipedevice (pipe) == ohci->rh.devnum) return rh_submit_urb (urb); spin_lock_irqsave(&ohci->ohci_lock, flags); /* when controller's hung, permit only roothub cleanup attempts * such as powering down ports */ if (ohci->disabled) { spin_unlock_irqrestore(&ohci->ohci_lock, flags); usb_dec_dev_use (urb->dev); return -ESHUTDOWN; } /* every endpoint has a ed, locate and fill it */ if (!(ed = ep_add_ed (urb->dev, pipe, urb->interval, 1, mem_flags))) { spin_unlock_irqrestore(&ohci->ohci_lock, flags); usb_dec_dev_use (urb->dev); return -ENOMEM; } /* for the private part of the URB we need the number of TDs (size) */ switch (usb_pipetype (pipe)) { case PIPE_BULK: /* one TD for every 4096 Byte */ size = (urb->transfer_buffer_length - 1) / 4096 + 1; /* If the transfer size is multiple of the pipe mtu, * we may need an extra TD to create a empty frame * Jean II */ if ((urb->transfer_flags & USB_ZERO_PACKET) && usb_pipeout (pipe) && (urb->transfer_buffer_length != 0) && ((urb->transfer_buffer_length % maxps) == 0)) size++; break; case PIPE_ISOCHRONOUS: /* number of packets from URB */ size = urb->number_of_packets; if (size <= 0) { spin_unlock_irqrestore(&ohci->ohci_lock, flags); usb_dec_dev_use (urb->dev); return -EINVAL; } for (i = 0; i < urb->number_of_packets; i++) { urb->iso_frame_desc[i].actual_length = 0; urb->iso_frame_desc[i].status = -EXDEV; } break; case PIPE_CONTROL: /* 1 TD for setup, 1 for ACK and 1 for every 4096 B */ size = (urb->transfer_buffer_length == 0)? 2: (urb->transfer_buffer_length - 1) / 4096 + 3; break; case PIPE_INTERRUPT: /* one TD */ size = 1; break; } /* allocate the private part of the URB */ urb_priv = kmalloc (sizeof (urb_priv_t) + size * sizeof (td_t *), GFP_ATOMIC); if (!urb_priv) { spin_unlock_irqrestore(&ohci->ohci_lock, flags); usb_dec_dev_use (urb->dev); return -ENOMEM; } memset (urb_priv, 0, sizeof (urb_priv_t) + size * sizeof (td_t *)); init_waitqueue_head (&urb_priv->wait); /* fill the private part of the URB */ urb_priv->length = size; urb_priv->ed = ed; /* allocate the TDs (updating hash chains) */ for (i = 0; i < size; i++) { urb_priv->td[i] = td_alloc (ohci, SLAB_ATOMIC); if (!urb_priv->td[i]) { urb_priv->length = i; urb_free_priv (ohci, urb_priv); spin_unlock_irqrestore(&ohci->ohci_lock, flags); usb_dec_dev_use (urb->dev); return -ENOMEM; } } if (ed->state == ED_NEW || (ed->state & ED_DEL)) { urb_free_priv (ohci, urb_priv); spin_unlock_irqrestore(&ohci->ohci_lock, flags); usb_dec_dev_use (urb->dev); return -EINVAL; } /* allocate and claim bandwidth if needed; ISO * needs start frame index if it was't provided. */ switch (usb_pipetype (pipe)) { case PIPE_ISOCHRONOUS: if (urb->transfer_flags & USB_ISO_ASAP) { urb->start_frame = ((ed->state == ED_OPER) ? (ed->last_iso + 1) : (le16_to_cpu (ohci->hcca->frame_no) + 10)) & 0xffff; } /* FALLTHROUGH */ case PIPE_INTERRUPT: if (urb->bandwidth == 0) { bustime = usb_check_bandwidth (urb->dev, urb); } if (bustime < 0) { urb_free_priv (ohci, urb_priv); spin_unlock_irqrestore(&ohci->ohci_lock, flags); usb_dec_dev_use (urb->dev); return bustime; } usb_claim_bandwidth (urb->dev, urb, bustime, usb_pipeisoc (urb->pipe));#ifdef DO_TIMEOUTS urb->timeout = 0;#endif } urb->actual_length = 0; urb->hcpriv = urb_priv; urb->status = USB_ST_URB_PENDING; /* link the ed into a chain if is not already */ if (ed->state != ED_OPER) ep_link (ohci, ed); /* fill the TDs and link it to the ed */ td_submit_urb (urb);#ifdef DO_TIMEOUTS /* maybe add to ordered list of timeouts */ if (urb->timeout) { struct list_head *entry; urb->timeout += jiffies; list_for_each (entry, &ohci->timeout_list) { struct urb *next_urb; next_urb = list_entry (entry, struct urb, urb_list); if (time_after_eq (urb->timeout, next_urb->timeout)) break; } list_add (&urb->urb_list, entry); /* drive timeouts by SF (messy, but works) */ writel (OHCI_INTR_SF, &ohci->regs->intrenable); (void)readl (&ohci->regs->intrdisable); /* PCI posting flush */ }#endif spin_unlock_irqrestore(&ohci->ohci_lock, flags); return 0; }/*-------------------------------------------------------------------------*//* deactivate all TDs and remove the private part of the URB *//* interrupt callers must use async unlink mode */static int sohci_unlink_urb (struct urb * urb){ unsigned long flags; ohci_t * ohci; if (!urb) /* just to be sure */ return -EINVAL; if (!urb->dev || !urb->dev->bus) return -ENODEV; ohci = (ohci_t *) urb->dev->bus->hcpriv; #ifdef DEBUG urb_print (urb, "UNLINK", 1);#endif /* handle a request to the virtual root hub */ if (usb_pipedevice (urb->pipe) == ohci->rh.devnum) return rh_unlink_urb (urb); spin_lock_irqsave(&ohci->ohci_lock, flags); if (urb->hcpriv && (urb->status == USB_ST_URB_PENDING)) { if (!ohci->disabled) { urb_priv_t * urb_priv; /* interrupt code may not sleep; it must use * async status return to unlink pending urbs. */ if (!(urb->transfer_flags & USB_ASYNC_UNLINK) && in_interrupt ()) { spin_unlock_irqrestore(&ohci->ohci_lock, flags); err ("bug in call from %p; use async!", __builtin_return_address(0)); return -EWOULDBLOCK; } /* flag the urb and its TDs for deletion in some * upcoming SF interrupt delete list processing */ urb_priv = urb->hcpriv; if (!urb_priv || (urb_priv->state == URB_DEL)) { spin_unlock_irqrestore(&ohci->ohci_lock, flags); return 0; } urb_priv->state = URB_DEL; ep_rm_ed (urb->dev, urb_priv->ed); urb_priv->ed->state |= ED_URB_DEL; if (!(urb->transfer_flags & USB_ASYNC_UNLINK)) { DECLARE_WAITQUEUE (wait, current); int timeout = OHCI_UNLINK_TIMEOUT; add_wait_queue(&urb_priv->wait, &wait); spin_unlock_irqrestore(&ohci->ohci_lock, flags); /* wait until all TDs are deleted */ set_current_state(TASK_UNINTERRUPTIBLE); while (timeout && (urb->status == USB_ST_URB_PENDING)) { timeout = schedule_timeout (timeout); set_current_state(TASK_UNINTERRUPTIBLE); } set_current_state(TASK_RUNNING); /* * A waitqueue head is self-locked, but we try * to interlock with the dl_del_urb() which may * be doing wake_up() right now, least * urb->complete poisons over the urb->wait. */ spin_lock_irqsave(&ohci->ohci_lock, flags); remove_wait_queue(&urb_priv->wait, &wait); spin_unlock_irqrestore(&ohci->ohci_lock, flags); if (urb->status == USB_ST_URB_PENDING) { err ("unlink URB timeout"); return -ETIMEDOUT; } usb_dec_dev_use (urb->dev); urb->dev = NULL; if (urb->complete) urb->complete (urb); } else { /* usb_dec_dev_use done in dl_del_list() */ urb->status = -EINPROGRESS; spin_unlock_irqrestore(&ohci->ohci_lock, flags); return -EINPROGRESS; } } else { urb_rm_priv_locked (urb); spin_unlock_irqrestore(&ohci->ohci_lock, flags); usb_dec_dev_use (urb->dev); urb->dev = NULL; if (urb->transfer_flags & USB_ASYNC_UNLINK) { urb->status = -ECONNRESET; if (urb->complete) urb->complete (urb); } else urb->status = -ENOENT; } } else { spin_unlock_irqrestore(&ohci->ohci_lock, flags); } return 0;}/*-------------------------------------------------------------------------*//* allocate private data space for a usb device */static int sohci_alloc_dev (struct usb_device *usb_dev){ struct ohci_device * dev; dev = dev_alloc ((struct ohci *) usb_dev->bus->hcpriv, ALLOC_FLAGS); if (!dev) return -ENOMEM; usb_dev->hcpriv = dev; return 0;}/*-------------------------------------------------------------------------*//* may be called from interrupt context *//* frees private data space of usb device */ static int sohci_free_dev (struct usb_device * usb_dev){ unsigned long flags; int i, cnt = 0; ed_t * ed; struct ohci_device * dev = usb_to_ohci (usb_dev); ohci_t * ohci = usb_dev->bus->hcpriv; if (!dev) return 0; if (usb_dev->devnum >= 0) { /* driver disconnects should have unlinked all urbs * (freeing all the TDs, unlinking EDs) but we need * to defend against bugs that prevent that. */ spin_lock_irqsave(&ohci->ohci_lock, flags); for(i = 0; i < NUM_EDS; i++) { ed = &(dev->ed[i]); if (ed->state != ED_NEW) { if (ed->state == ED_OPER) { /* driver on that interface didn't unlink an urb */ dbg ("driver usb-%s dev %d ed 0x%x unfreed URB", ohci->ohci_dev->slot_name, usb_dev->devnum, i); ep_unlink (ohci, ed); } ep_rm_ed (usb_dev, ed); ed->state = ED_DEL; cnt++; } } spin_unlock_irqrestore(&ohci->ohci_lock, flags); /* if the controller is running, tds for those unlinked * urbs get freed by dl_del_list at the next SF interrupt */ if (cnt > 0) { if (ohci->disabled) { /* FIXME: Something like this should kick in, * though it's currently an exotic case ... * the controller won't ever be touching
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -