📄 usb-ohci.c
字号:
#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)) {
// 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;
printf("unlink urb\n");
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);
wait_ms(timeout);
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);
// printf("urb status:%X\n",urb->status);
}
} 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 = ohci_dev_alloc ((struct ohci *) usb_dev->bus->hcpriv);
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->ohci_dev->slot_name, usb_dev->devnum, i);
ep_unlink (ohci, ed);
}
printf("free device\n");
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{
// 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);
wait_ms(timeout);
if (dev->ed_cnt) {
printf ("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->ohci_dev->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++)
if (ohci->ohci_int_load [branch] > ohci->ohci_int_load [i]) branch = i;
branch = branch % interval;
for (i = branch; i < 32; i += interval) ohci->ohci_int_load [i] += load;
return branch;
}
/*-------------------------------------------------------------------------*/
/* 2^int( ld (inter)) */
static int ep_2_n_interval (int inter)
{
int i;
for (i = 0; ((inter >> i) > 1 ) && (i < 5); i++);
return 1 << i;
}
/*-------------------------------------------------------------------------*/
/* the int tree is a binary tree
* in order to process it sequentially the indexes of the branches have to be mapped
* the mapping reverses the bits of a word of num_bits length */
static int ep_rev (int num_bits, int word)
{
int i, wout = 0;
for (i = 0; i < num_bits; i++) wout |= (((word >> i) & 1) << (num_bits - i - 1));
return wout;
}
/*-------------------------------------------------------------------------*/
/* link an ed into one of the HC chains */
static int ep_link (ohci_t * ohci, ed_t * edi)
{
int int_branch;
int i;
int inter;
int interval;
int load;
unsigned long * ed_p;
volatile ed_t * ed = edi;
ed->state = ED_OPER;
switch (ed->type) {
case PIPE_CONTROL:
ed->hwNextED = 0;
if (ohci->ed_controltail == NULL) {
writel (ed->dma, &ohci->regs->ed_controlhead);
} else {
// ohci->ed_controltail->hwNextED = cpu_to_le32 (ed->dma);
ohci->ed_controltail->hwNextED = ed->dma;
}
ed->ed_prev = ohci->ed_controltail;
if (!ohci->ed_controltail && !ohci->ed_rm_list[0] &&
!ohci->ed_rm_list[1] && !ohci->sleeping) {
ohci->hc_control |= OHCI_CTRL_CLE;
writel (ohci->hc_control, &ohci->regs->control);
}
ohci->ed_controltail = edi;
break;
case PIPE_BULK:
ed->hwNextED = 0;
if (ohci->ed_bulktail == NULL) {
writel (ed->dma, &ohci->regs->ed_bulkhead);
} else {
// ohci->ed_bulktail->hwNextED = cpu_to_le32 (ed->dma);
ohci->ed_bulktail->hwNextED = ed->dma;
}
ed->ed_prev = ohci->ed_bulktail;
if (!ohci->ed_bulktail && !ohci->ed_rm_list[0] &&
!ohci->ed_rm_list[1] && !ohci->sleeping) {
ohci->hc_control |= OHCI_CTRL_BLE;
writel (ohci->hc_control, &ohci->regs->control);
}
ohci->ed_bulktail = edi;
break;
case PIPE_INTERRUPT:
load = ed->int_load;
interval = ep_2_n_interval (ed->int_period);
ed->int_interval = interval;
int_branch = ep_int_ballance (ohci, interval, load);
ed->int_branch = int_branch;
for (i = 0; i < ep_rev (6, interval); i += inter) {
inter = 1;
for (ed_p = &(ohci->hcca->int_table[ep_rev (5, i) + int_branch]);
(*ed_p != 0) && ((dma_to_ed (ohci, le32_to_cpup (ed_p)))->int_interval >= interval);
ed_p = &((dma_to_ed (ohci, le32_to_cpup (ed_p)))->hwNextED))
inter = ep_rev (6, (dma_to_ed (ohci, le32_to_cpup (ed_p)))->int_interval);
ed->hwNextED = *ed_p;
*ed_p = cpu_to_le32 (ed->dma);
}
#ifdef DEBUG
ep_print_int_eds (ohci, "LINK_INT");
#endif
break;
case PIPE_ISOCHRONOUS:
ed->hwNextED = 0;
ed->int_interval = 1;
if (ohci->ed_isotail != NULL) {
ohci->ed_isotail->hwNextED = cpu_to_le32 (ed->dma);
ed->ed_prev = ohci->ed_isotail;
} else {
for ( i = 0; i < 32; i += inter) {
inter = 1;
for (ed_p = &(ohci->hcca->int_table[ep_rev (5, i)]);
*ed_p != 0;
ed_p = &((dma_to_ed (ohci, le32_to_cpup (ed_p)))->hwNextED))
inter = ep_rev (6, (dma_to_ed (ohci, le32_to_cpup (ed_p)))->int_interval);
*ed_p = cpu_to_le32 (ed->dma);
}
ed->ed_prev = NULL;
}
ohci->ed_isotail = edi;
#ifdef DEBUG
ep_print_int_eds (ohci, "LINK_ISO");
#endif
break;
}
return 0;
}
/*-------------------------------------------------------------------------*/
/* unlink an ed from one of the HC chains.
* just the link to the ed is unlinked.
* the link from the ed still points to another operational ed or 0
* so the HC can eventually finish the processing of the unlinked ed */
static int ep_unlink (ohci_t * ohci, ed_t * ed)
{
int int_branch;
int i;
int inter;
int interval;
unsigned long * ed_p;
ed->hwINFO |= cpu_to_le32 (OHCI_ED_SKIP);
switch (ed->type) {
case PIPE_CONTROL:
if (ed->ed_prev == NULL) {
if (!ed->hwNextED) {
ohci->hc_control &= ~OHCI_CTRL_CLE;
writel (ohci->hc_control, &ohci->regs->control);
}
writel (le32_to_cpup (&ed->hwNextED), &ohci->regs->ed_controlhead);
} else {
ed->ed_prev->hwNextED = ed->hwNextED;
}
if (ohci->ed_controltail == ed) {
ohci->ed_controltail = ed->ed_prev;
} else {
(dma_to_ed (ohci, le32_to_cpup (&ed->hwNextED)))->ed_prev = ed->ed_prev;
}
break;
case PIPE_BULK:
if (ed->ed_prev == NULL) {
if (!ed->hwNextED) {
ohci->hc_control &= ~OHCI_CTRL_BLE;
writel (ohci->hc_control, &ohci->regs->control);
}
writel (le32_to_cpup (&ed->hwNextED), &ohci->regs->ed_bulkhead);
} else {
ed->ed_prev->hwNextED = ed->hwNextED;
}
if (ohci->ed_bulktail == ed) {
ohci->ed_bulktail = ed->ed_prev;
} else {
(dma_to_ed (ohci, le32_to_cpup (&ed->hwNextED)))->ed_prev = ed->ed_prev;
}
break;
case PIPE_INTERRUPT:
int_branch = ed->int_branch;
interval = ed->int_interval;
for (i = 0; i < ep_rev (6, interval); i += inter) {
for (ed_p = &(ohci->hcca->int_table[ep_rev (5, i) + int_branch]), inter = 1;
(*ed_p != 0) && (*ed_p != ed->hwNextED);
ed_p = &((dma_to_ed (ohci, le32_to_cpup (ed_p)))->hwNextED),
inter = ep_rev (6, (dma_to_ed (ohci, le32_to_cpup (ed_p)))->int_interval)) {
if((dma_to_ed (ohci, le32_to_cpup (ed_p))) == ed) {
*ed_p = ed->hwNextED;
break;
}
}
}
for (i = int_branch; i < 32; i += interval)
ohci->ohci_int_load[i] -= ed->int_load;
#ifdef DEBUG
ep_print_int_eds (ohci, "UNLINK_INT");
#endif
break;
case PIPE_ISOCHRONOUS:
if (ohci->ed_isotail == ed)
ohci->ed_isotail = ed->ed_prev;
if (ed->hwNextED != 0)
(dma_to_ed (ohci, le32_to_cpup (&ed->hwNextED)))->ed_prev = ed->ed_prev;
if (ed->ed_prev != NULL) {
ed->ed_prev->hwNextED = ed->hwNextED;
} else {
for (i = 0; i < 32; i++) {
for (ed_p = &(ohci->hcca->int_table[ep_rev (5, i)]);
*ed_p != 0;
ed_p = &((dma_to_ed (ohci, le32_to_cpup (ed_p)))->hwNextED)) {
// inter = ep_rev (6, (dma_to_ed (ohci, le32_to_cpup (ed_p)))->int_interval);
if((dma_to_ed (ohci, le32_to_cpup (ed_p))) == ed) {
*ed_p = ed->hwNextED;
break;
}
}
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -