📄 usb-ohci.c
字号:
urb->transfer_buffer_length, usb_pipeout (urb->pipe) ? PCI_DMA_TODEVICE : PCI_DMA_FROMDEVICE);#if 1 urb->complete (urb); /* implicitly requeued */ urb->actual_length = 0; urb->status = USB_ST_URB_PENDING; if (urb_priv->state != URB_DEL) td_submit_urb (urb);#else if (urb->interval) { urb->complete (urb); /* implicitly requeued */ urb->actual_length = 0; urb->status = USB_ST_URB_PENDING; if (urb_priv->state != URB_DEL) td_submit_urb (urb); } else { urb_rm_priv(urb); urb->complete (urb); }#endif 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); spin_lock_irqsave (&usb_ed_lock, flags); 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); } spin_unlock_irqrestore (&usb_ed_lock, flags); } else { /* unlink URB, call complete */ urb_rm_priv (urb); urb->complete (urb); } break; case PIPE_BULK: case PIPE_CONTROL: /* unlink URB, call complete */ urb_rm_priv (urb); urb->complete (urb); break; } return 0;}/*-------------------------------------------------------------------------*//* get a transfer request */ static int sohci_submit_urb (urb_t * 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 = ALLOC_FLAGS; 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); } /* when controller's hung, permit only roothub cleanup attempts * such as powering down ports */ if (ohci->disabled) { 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))) { 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) { 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 *), in_interrupt() ? GFP_ATOMIC : GFP_KERNEL); if (!urb_priv) { usb_dec_dev_use (urb->dev); return -ENOMEM; } memset (urb_priv, 0, sizeof (urb_priv_t) + size * sizeof (td_t *)); /* fill the private part of the URB */ urb_priv->length = size; urb_priv->ed = ed; /* allocate the TDs (updating hash chains) */ spin_lock_irqsave (&usb_ed_lock, flags); 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 (&usb_ed_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 (&usb_ed_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 (&usb_ed_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; // FIXME: usb-uhci uses relative timeouts (like this), // while uhci uses absolute ones (probably better). // Pick one solution and change the affected drivers. 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); }#endif spin_unlock_irqrestore (&usb_ed_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 (urb_t * 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); 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 ()) { 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 */ spin_lock_irqsave (&usb_ed_lock, flags); urb_priv = urb->hcpriv; if (!urb_priv || (urb_priv->state == URB_DEL)) { spin_unlock_irqrestore (&usb_ed_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_WAIT_QUEUE_HEAD (unlink_wakeup); DECLARE_WAITQUEUE (wait, current); int timeout = OHCI_UNLINK_TIMEOUT; add_wait_queue (&unlink_wakeup, &wait); urb_priv->wait = &unlink_wakeup; spin_unlock_irqrestore (&usb_ed_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_RUNNING); remove_wait_queue (&unlink_wakeup, &wait); if (urb->status == USB_ST_URB_PENDING) { err ("unlink URB timeout"); return -ETIMEDOUT; } } else { /* usb_dec_dev_use done in dl_del_list() */ urb->status = -EINPROGRESS; spin_unlock_irqrestore (&usb_ed_lock, flags); } } else { urb_rm_priv (urb); if (urb->transfer_flags & USB_ASYNC_UNLINK) { urb->status = -ECONNRESET; if (urb->complete) urb->complete (urb); } else urb->status = -ENOENT; } } 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 (&usb_ed_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->slot_name, usb_dev->devnum, i); ep_unlink (ohci, ed); } ep_rm_ed (usb_dev, ed); ed->state = ED_DEL; cnt++; } } spin_unlock_irqrestore (&usb_ed_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 * these lists again!! dl_del_list (ohci, le16_to_cpu (ohci->hcca->frame_no) & 1); */ warn ("TD leak, %d", cnt); } else if (!in_interrupt ()) { DECLARE_WAIT_QUEUE_HEAD (freedev_wakeup); DECLARE_WAITQUEUE (wait, current); int timeout = OHCI_UNLINK_TIMEOUT; /* SF interrupt handler calls dl_del_list */ add_wait_queue (&freedev_wakeup, &wait); dev->wait = &freedev_wakeup; set_current_state(TASK_UNINTERRUPTIBLE); while (timeout && dev->ed_cnt) timeout = schedule_timeout (timeout); set_current_state(TASK_RUNNING); remove_wait_queue (&freedev_wakeup, &wait); if (dev->ed_cnt) { err ("free device %d timeout", usb_dev->devnum); return -ETIMEDOUT; } } else { /* likely some interface's driver has a refcount bug */ err ("bus %s devnum %d deletion in interrupt", ohci->slot_name, usb_dev->devnum); BUG (); } } } /* free device, and associated EDs */ dev_free (ohci, dev); return 0;}/*-------------------------------------------------------------------------*//* tell us the current USB frame number */static int sohci_get_current_frame_number (struct usb_device *usb_dev) { ohci_t * ohci = usb_dev->bus->hcpriv; return le16_to_cpu (ohci->hcca->frame_no);}/*-------------------------------------------------------------------------*/struct usb_operations sohci_device_operations = { sohci_alloc_dev, sohci_free_dev, sohci_get_current_frame_number, sohci_submit_urb, sohci_unlink_urb};/*-------------------------------------------------------------------------* * ED handling functions *-------------------------------------------------------------------------*/ /* search for the right branch to insert an interrupt ed into the int tree * do some load ballancing;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -