📄 usb-ohci.c
字号:
: PCI_DMA_FROMDEVICE);
*/
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);
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 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;
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) */
Ohci_WriteReg (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);
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);
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;
* returns the branch and
* sets the interval to interval = 2^integer (ld (interval)) */
static int ep_int_ballance (ohci_t * ohci, int interval, int load)
{
int i, branch = 0;
/* search for the least loaded interrupt endpoint branch of all 32 branches */
for (i = 0; i < 32; i++)
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -