patch-2.4.0-test9 linux/drivers/usb/usb-ohci.c
Next file: linux/drivers/usb/usb-ohci.h
Previous file: linux/drivers/usb/usb-core.c
Back to the patch index
Back to the overall index
- Lines: 891
- Date:
Tue Oct 3 09:24:40 2000
- Orig file:
v2.4.0-test8/linux/drivers/usb/usb-ohci.c
- Orig date:
Thu Sep 7 08:39:00 2000
diff -u --recursive --new-file v2.4.0-test8/linux/drivers/usb/usb-ohci.c linux/drivers/usb/usb-ohci.c
@@ -2,6 +2,7 @@
* URB OHCI HCD (Host Controller Driver) for USB.
*
* (C) Copyright 1999 Roman Weissgaerber <weissg@vienna.at>
+ * (C) Copyright 2000 David Brownell <david-b@pacbell.net>
*
* [ Initialisation is based on Linus' ]
* [ uhci code and gregs ohci fragments ]
@@ -11,6 +12,14 @@
*
* History:
*
+ * 2000/09/26 fixed races in removing the private portion of the urb
+ * 2000/09/07 disable bulk and control lists when unlinking the last
+ * endpoint descriptor in order to avoid unrecoverable errors on
+ * the Lucent chips.
+ * 2000/08/29 use bandwidth claiming hooks (thanks Randy!), fix some
+ * urb unlink probs, indentation fixes
+ * 2000/08/11 various oops fixes mostly affecting iso and cleanup from
+ * device unplugs.
* 2000/06/28 use PCI hotplug framework, for better power management
* and for Cardbus support (David Brownell)
* 2000/earlier: fixes for NEC/Lucent chips; suspend/resume handling
@@ -73,8 +82,9 @@
/* For initializing controller (mask in an HCFS mode too) */
#define OHCI_CONTROL_INIT \
- (OHCI_CTRL_CBSR & 0x3) \
- | OHCI_CTRL_BLE | OHCI_CTRL_CLE | OHCI_CTRL_IE | OHCI_CTRL_PLE
+ (OHCI_CTRL_CBSR & 0x3) | OHCI_CTRL_IE | OHCI_CTRL_PLE
+
+#define OHCI_UNLINK_TIMEOUT (HZ / 10)
static LIST_HEAD (ohci_hcd_list);
static spinlock_t usb_ed_lock = SPIN_LOCK_UNLOCKED;
@@ -83,23 +93,55 @@
* URB support functions
*-------------------------------------------------------------------------*/
-/* free the private part of an URB */
-
-static void urb_rm_priv (urb_t * urb)
+/* free HCD-private data associated with this URB */
+
+static void urb_free_priv (urb_priv_t * urb_priv)
{
- urb_priv_t * urb_priv = urb->hcpriv;
int i;
-
- if (!urb_priv) return;
-
+
for (i = 0; i < urb_priv->length; i++) {
if (urb_priv->td [i]) {
OHCI_FREE (urb_priv->td [i]);
}
}
- kfree (urb->hcpriv);
- urb->hcpriv = NULL;
+ kfree (urb_priv);
+}
+
+static void urb_rm_priv_locked (urb_t * urb)
+{
+ urb_priv_t * urb_priv = urb->hcpriv;
+
+ if (urb_priv) {
+ urb->hcpriv = NULL;
+
+ /* Release int/iso bandwidth */
+ if (urb->bandwidth) {
+ switch (usb_pipetype(urb->pipe)) {
+ case PIPE_INTERRUPT:
+ usb_release_bandwidth (urb->dev, urb, 0);
+ break;
+ case PIPE_ISOCHRONOUS:
+ usb_release_bandwidth (urb->dev, urb, 1);
+ break;
+ default:
+ break;
+ }
+ }
+
+ urb_free_priv (urb_priv);
+ usb_dec_dev_use (urb->dev);
+ urb->dev = NULL;
+ }
+}
+
+static void urb_rm_priv (urb_t * urb)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave (&usb_ed_lock, flags);
+ urb_rm_priv_locked (urb);
+ spin_unlock_irqrestore (&usb_ed_lock, flags);
}
/*-------------------------------------------------------------------------*/
@@ -347,15 +389,15 @@
unsigned long flags;
int i;
+ if (!urb_priv)
+ return -1; /* urb already unlinked */
+
/* just to be sure */
if (!urb->complete) {
urb_rm_priv (urb);
- usb_dec_dev_use (urb->dev);
return -1;
}
- if (!urb_priv) return -1; /* urb already unlinked */
-
#ifdef DEBUG
urb_print (urb, "RET", usb_pipeout (urb->pipe));
#endif
@@ -389,7 +431,6 @@
} else { /* unlink URB, call complete */
urb_rm_priv (urb);
- usb_dec_dev_use (urb->dev);
urb->complete (urb);
}
break;
@@ -397,7 +438,6 @@
case PIPE_BULK:
case PIPE_CONTROL: /* unlink URB, call complete */
urb_rm_priv (urb);
- usb_dec_dev_use (urb->dev);
urb->complete (urb);
break;
}
@@ -416,9 +456,10 @@
unsigned int pipe = urb->pipe;
int i, size = 0;
unsigned long flags;
+ int bustime = 0;
if (!urb->dev || !urb->dev->bus)
- return -EINVAL;
+ return -ENODEV;
if (urb->hcpriv) /* urb already in use */
return -EINVAL;
@@ -433,7 +474,7 @@
urb_print (urb, "SUB", usb_pipein (pipe));
#endif
- /* a request to the virtual root hub */
+ /* handle a request to the virtual root hub */
if (usb_pipedevice (pipe) == ohci->rh.devnum)
return rh_submit_urb (urb);
@@ -457,6 +498,10 @@
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;
@@ -468,11 +513,10 @@
break;
case PIPE_INTERRUPT: /* one TD */
size = 1;
-
break;
}
- /* allocate the private part or the URB */
+ /* 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) {
@@ -482,43 +526,59 @@
memset (urb_priv, 0, sizeof (urb_priv_t) + size * sizeof (td_t *));
/* fill the private part of the URB */
- urb->hcpriv = urb_priv;
urb_priv->length = size;
- urb_priv->td_cnt = 0;
- urb_priv->state = 0;
urb_priv->ed = ed;
-
+
/* allocate the TDs */
for (i = 0; i < size; i++) {
OHCI_ALLOC (urb_priv->td[i], sizeof (td_t));
if (!urb_priv->td[i]) {
+ urb_free_priv (urb_priv);
usb_dec_dev_use (urb->dev);
- urb_rm_priv (urb);
return -ENOMEM;
}
}
- spin_lock_irqsave (&usb_ed_lock, flags);
+
if (ed->state == ED_NEW || (ed->state & ED_DEL)) {
- urb_rm_priv(urb);
+ urb_free_priv (urb_priv);
usb_dec_dev_use (urb->dev);
- spin_unlock_irqrestore(&usb_ed_lock, flags);
return -EINVAL;
}
- /* for ISOC transfers calculate start frame index */
- 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;
- }
- urb->status = USB_ST_URB_PENDING;
+ /* 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 (urb_priv);
+ usb_dec_dev_use (urb->dev);
+ return bustime;
+ }
+ usb_claim_bandwidth (urb->dev, urb, bustime, usb_pipeisoc (urb->pipe));
+ }
+
+ spin_lock_irqsave (&usb_ed_lock, flags);
urb->actual_length = 0;
-
- if (ed->state != ED_OPER) /* link the ed into a chain if is not already */
+ 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);
-
- urb->status = USB_ST_URB_PENDING;
- td_submit_urb (urb); /* fill the TDs and link it to the ed */
+ /* fill the TDs and link it to the ed */
+ td_submit_urb (urb);
spin_unlock_irqrestore (&usb_ed_lock, flags);
return 0;
@@ -528,7 +588,7 @@
/* 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;
@@ -545,16 +605,14 @@
#ifdef DEBUG
urb_print (urb, "UNLINK", 1);
#endif
-
- if (usb_pipedevice (urb->pipe) == ohci->rh.devnum) {
- usb_dec_dev_use(urb->dev);
- return rh_unlink_urb (urb); /* a request to the virtual root hub */
- }
- if (urb->hcpriv) {
- /* URB active? */
- if (urb->status == USB_ST_URB_PENDING && !ohci->disabled) {
- urb_priv_t * urb_priv = urb->hcpriv;
+ /* 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.
@@ -566,40 +624,50 @@
return -EWOULDBLOCK;
}
-
/* flag the urb and its TDs for deletion in some
* upcoming SF interrupt delete list processing
*/
- urb_priv->state = URB_DEL;
-
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;
- spin_unlock_irqrestore (&usb_ed_lock, flags);
if (!(urb->transfer_flags & USB_ASYNC_UNLINK)) {
DECLARE_WAIT_QUEUE_HEAD (unlink_wakeup);
DECLARE_WAITQUEUE (wait, current);
+ int timeout = OHCI_UNLINK_TIMEOUT;
- usb_dec_dev_use (urb->dev);
- /* wait until all TDs are deleted */
add_wait_queue (&unlink_wakeup, &wait);
urb_priv->wait = &unlink_wakeup;
- current->state = TASK_UNINTERRUPTIBLE;
- schedule ();
+ 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);
remove_wait_queue (&unlink_wakeup, &wait);
- urb->status = -ENOENT;
- urb_priv->wait = 0;
+ 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 {
- usb_dec_dev_use (urb->dev);
urb_rm_priv (urb);
- if (urb->complete && (urb->transfer_flags & USB_ASYNC_UNLINK)) {
- urb->complete (urb);
- urb->status = 0;
+ if (urb->transfer_flags & USB_ASYNC_UNLINK) {
+ urb->status = -ECONNRESET;
+ if (urb->complete)
+ urb->complete (urb);
} else
urb->status = -ENOENT;
}
@@ -683,14 +751,19 @@
} 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;
- current->state = TASK_UNINTERRUPTIBLE;
- schedule ();
+ set_current_state(TASK_UNINTERRUPTIBLE);
+ while (timeout && dev->ed_cnt)
+ timeout = schedule_timeout (timeout);
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",
@@ -790,7 +863,7 @@
ed->state = ED_OPER;
switch (ed->type) {
- case CTRL:
+ case PIPE_CONTROL:
ed->hwNextED = 0;
if (ohci->ed_controltail == NULL) {
writel (virt_to_bus (ed), &ohci->regs->ed_controlhead);
@@ -798,10 +871,15 @@
ohci->ed_controltail->hwNextED = cpu_to_le32 (virt_to_bus (ed));
}
ed->ed_prev = ohci->ed_controltail;
+ if (!ohci->ed_controltail && !ohci->ed_rm_list[0] &&
+ !ohci->ed_rm_list[1]) {
+ ohci->hc_control |= OHCI_CTRL_CLE;
+ writel (ohci->hc_control, &ohci->regs->control);
+ }
ohci->ed_controltail = edi;
break;
- case BULK:
+ case PIPE_BULK:
ed->hwNextED = 0;
if (ohci->ed_bulktail == NULL) {
writel (virt_to_bus (ed), &ohci->regs->ed_bulkhead);
@@ -809,10 +887,15 @@
ohci->ed_bulktail->hwNextED = cpu_to_le32 (virt_to_bus (ed));
}
ed->ed_prev = ohci->ed_bulktail;
+ if (!ohci->ed_bulktail && !ohci->ed_rm_list[0] &&
+ !ohci->ed_rm_list[1]) {
+ ohci->hc_control |= OHCI_CTRL_BLE;
+ writel (ohci->hc_control, &ohci->regs->control);
+ }
ohci->ed_bulktail = edi;
break;
- case INT:
+ case PIPE_INTERRUPT:
load = ed->int_load;
interval = ep_2_n_interval (ed->int_period);
ed->int_interval = interval;
@@ -833,7 +916,7 @@
#endif
break;
- case ISO:
+ case PIPE_ISOCHRONOUS:
ed->hwNextED = 0;
ed->int_interval = 1;
if (ohci->ed_isotail != NULL) {
@@ -873,24 +956,33 @@
int inter;
int interval;
__u32 * ed_p;
-
-
+
+ ed->hwINFO |= cpu_to_le32 (OHCI_ED_SKIP);
+
switch (ed->type) {
- case CTRL:
+ 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) {
+ if (ohci->ed_controltail == ed) {
ohci->ed_controltail = ed->ed_prev;
} else {
((ed_t *) bus_to_virt (le32_to_cpup (&ed->hwNextED)))->ed_prev = ed->ed_prev;
}
break;
- case BULK:
+ 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;
@@ -902,7 +994,7 @@
}
break;
- case INT:
+ case PIPE_INTERRUPT:
int_branch = ed->int_branch;
interval = ed->int_interval;
@@ -924,7 +1016,7 @@
#endif
break;
- case ISO:
+ case PIPE_ISOCHRONOUS:
if (ohci->ed_isotail == ed)
ohci->ed_isotail = ed->ed_prev;
if (ed->hwNextED != 0)
@@ -969,16 +1061,17 @@
td_t * td;
ed_t * ed_ret;
volatile ed_t * ed;
+ unsigned long flags;
- spin_lock (&usb_ed_lock);
+ spin_lock_irqsave (&usb_ed_lock, flags);
ed = ed_ret = &(usb_to_ohci (usb_dev)->ed[(usb_pipeendpoint (pipe) << 1) |
(usb_pipecontrol (pipe)? 0: usb_pipeout (pipe))]);
if ((ed->state & ED_DEL) || (ed->state & ED_URB_DEL)) {
/* pending delete request */
- spin_unlock (&usb_ed_lock);
+ spin_unlock_irqrestore (&usb_ed_lock, flags);
return NULL;
}
@@ -987,7 +1080,7 @@
OHCI_ALLOC (td, sizeof (*td)); /* dummy td; end of td list for ed */
if (!td) {
/* out of memory */
- spin_unlock (&usb_ed_lock);
+ spin_unlock_irqrestore (&usb_ed_lock, flags);
return NULL;
}
ed->hwTailP = cpu_to_le32 (virt_to_bus (td));
@@ -1006,12 +1099,12 @@
| usb_pipeslow (pipe) << 13
| usb_maxpacket (usb_dev, pipe, usb_pipeout (pipe)) << 16);
- if (ed->type == INT && ed->state == ED_UNLINK) {
+ if (ed->type == PIPE_INTERRUPT && ed->state == ED_UNLINK) {
ed->int_period = interval;
ed->int_load = load;
}
- spin_unlock(&usb_ed_lock);
+ spin_unlock_irqrestore (&usb_ed_lock, flags);
return ed_ret;
}
@@ -1029,28 +1122,29 @@
if ((ed->state & ED_DEL) || (ed->state & ED_URB_DEL))
return;
- ed->hwINFO |= cpu_to_le32 (OHCI_ED_SKIP);
+ ed->hwINFO |= cpu_to_le32 (OHCI_ED_SKIP);
if (!ohci->disabled) {
- /* enable SOF interrupt */
- writel (OHCI_INTR_SF, &ohci->regs->intrstatus);
- writel (OHCI_INTR_SF, &ohci->regs->intrenable);
+ switch (ed->type) {
+ case PIPE_CONTROL: /* stop control list */
+ ohci->hc_control &= ~OHCI_CTRL_CLE;
+ writel (ohci->hc_control, &ohci->regs->control);
+ break;
+ case PIPE_BULK: /* stop bulk list */
+ ohci->hc_control &= ~OHCI_CTRL_BLE;
+ writel (ohci->hc_control, &ohci->regs->control);
+ break;
+ }
}
frame = le16_to_cpu (ohci->hcca.frame_no) & 0x1;
ed->ed_rm_list = ohci->ed_rm_list[frame];
ohci->ed_rm_list[frame] = ed;
- if (ohci->disabled)
- return;
-
- switch (ed->type) {
- case CTRL: /* stop CTRL list */
- writel (ohci->hc_control &= ~OHCI_CTRL_CLE, &ohci->regs->control);
- break;
- case BULK: /* stop BULK list */
- writel (ohci->hc_control &= ~OHCI_CTRL_BLE, &ohci->regs->control);
- break;
+ if (!ohci->disabled) {
+ /* enable SOF interrupt */
+ writel (OHCI_INTR_SF, &ohci->regs->intrstatus);
+ writel (OHCI_INTR_SF, &ohci->regs->intrenable);
}
}
@@ -1072,25 +1166,30 @@
td_pt = urb_priv->td [index];
/* fill the old dummy TD */
- td = urb_priv->td [index] = (td_t *) bus_to_virt (le32_to_cpup (&urb_priv->ed->hwTailP) & 0xfffffff0);
+ td = urb_priv->td [index] = (td_t *)
+ bus_to_virt (le32_to_cpup (&urb_priv->ed->hwTailP) & 0xfffffff0);
td->ed = urb_priv->ed;
+ td->next_dl_td = NULL;
td->index = index;
td->urb = urb;
td->hwINFO = cpu_to_le32 (info);
- if ((td->ed->type & 3) == PIPE_ISOCHRONOUS) {
- td->hwCBP = cpu_to_le32 (((!data || !len)?
- 0 : virt_to_bus (data)) & 0xFFFFF000);
+ if ((td->ed->type) == PIPE_ISOCHRONOUS) {
+ td->hwCBP = cpu_to_le32 (((!data || !len)
+ ? 0
+ : virt_to_bus (data)) & 0xFFFFF000);
td->ed->last_iso = info & 0xffff;
} else {
- td->hwCBP = cpu_to_le32 (((!data || !len)? 0 : virt_to_bus (data)));
+ td->hwCBP = cpu_to_le32 (((!data || !len)
+ ? 0
+ : virt_to_bus (data)));
}
- td->hwBE = cpu_to_le32 ((!data || !len )? 0: virt_to_bus (data + len - 1));
+ td->hwBE = cpu_to_le32 ((!data || !len )
+ ? 0
+ : virt_to_bus (data + len - 1));
td->hwNextTD = cpu_to_le32 (virt_to_bus (td_pt));
td->hwPSW [0] = cpu_to_le16 ((virt_to_bus (data) & 0x0FFF) | 0xE000);
td_pt->hwNextTD = 0;
td->ed->hwTailP = td->hwNextTD;
-
- td->next_dl_td = NULL; //td_pt;
}
/*-------------------------------------------------------------------------*/
@@ -1215,6 +1314,27 @@
}
}
+/* handle an urb that is being unlinked */
+
+static void dl_del_urb (urb_t * urb)
+{
+ wait_queue_head_t * wait_head = ((urb_priv_t *)(urb->hcpriv))->wait;
+
+ urb_rm_priv_locked (urb);
+
+ if (urb->transfer_flags & USB_ASYNC_UNLINK) {
+ urb->status = -ECONNRESET;
+ if (urb->complete)
+ urb->complete (urb);
+ } else {
+ urb->status = -ENOENT;
+
+ /* unblock sohci_unlink_urb */
+ if (wait_head)
+ wake_up (wait_head);
+ }
+}
+
/*-------------------------------------------------------------------------*/
/* replies to the request have to be on a FIFO basis so
@@ -1276,6 +1396,7 @@
int ctrl = 0, bulk = 0;
spin_lock_irqsave (&usb_ed_lock, flags);
+
for (ed = ohci->ed_rm_list[frame]; ed != NULL; ed = ed->ed_rm_list) {
tdTailP = bus_to_virt (le32_to_cpup (&ed->hwTailP) & 0xfffffff0);
@@ -1290,22 +1411,13 @@
td_next = bus_to_virt (le32_to_cpup (&td->hwNextTD) & 0xfffffff0);
if ((urb_priv->state == URB_DEL) || (ed->state & ED_DEL)) {
tdINFO = le32_to_cpup (&td->hwINFO);
- if (TD_CC_GET (tdINFO) < 0xE) dl_transfer_length (td);
+ if (TD_CC_GET (tdINFO) < 0xE)
+ dl_transfer_length (td);
*td_p = td->hwNextTD | (*td_p & cpu_to_le32 (0x3));
- /* URB is done; clean up */
- if (++(urb_priv->td_cnt) == urb_priv->length) {
- void *condition = urb_priv->wait;
- urb_rm_priv (urb);
- if (urb->transfer_flags & USB_ASYNC_UNLINK) {
- usb_dec_dev_use (urb->dev);
- urb->status = -ECONNRESET;
- urb->complete (urb);
- } else if (condition) {
- /* unblock sohci_unlink_urb */
- wake_up (condition);
- }
- }
+ /* URB is done; clean up */
+ if (++(urb_priv->td_cnt) == urb_priv->length)
+ dl_del_urb (urb);
} else {
td_p = &td->hwNextTD;
}
@@ -1317,31 +1429,54 @@
ed->hwINFO = cpu_to_le32 (OHCI_ED_SKIP);
ed->state = ED_NEW;
/* if all eds are removed wake up sohci_free_dev */
- if (!--dev->ed_cnt && dev->wait)
- wake_up (dev->wait);
- }
- else {
+ if (!--dev->ed_cnt) {
+ wait_queue_head_t *wait_head = dev->wait;
+
+ dev->wait = 0;
+ if (wait_head)
+ wake_up (wait_head);
+ }
+ } else {
ed->state &= ~ED_URB_DEL;
- ed->hwINFO &= ~cpu_to_le32 (OHCI_ED_SKIP);
+ tdHeadP = bus_to_virt (le32_to_cpup (&ed->hwHeadP) & 0xfffffff0);
+
+ if (tdHeadP == tdTailP) {
+ if (ed->state == ED_OPER)
+ ep_unlink(ohci, ed);
+ OHCI_FREE (tdTailP);
+ ed->hwINFO = cpu_to_le32 (OHCI_ED_SKIP);
+ ed->state = ED_NEW;
+ --(usb_to_ohci (ohci->dev[edINFO & 0x7F]))->ed_cnt;
+ } else
+ ed->hwINFO &= ~cpu_to_le32 (OHCI_ED_SKIP);
}
-
- if ((ed->type & 3) == CTRL) ctrl |= 1;
- if ((ed->type & 3) == BULK) bulk |= 1;
+
+ switch (ed->type) {
+ case PIPE_CONTROL:
+ ctrl = 1;
+ break;
+ case PIPE_BULK:
+ bulk = 1;
+ break;
+ }
}
- /* maybe reenable CTRL and BULK lists */
+ /* maybe reenable control and bulk lists */
if (!ohci->disabled) {
- if (ctrl) /* reset CTRL list */
+ if (ctrl) /* reset control list */
writel (0, &ohci->regs->ed_controlcurrent);
- if (bulk) /* reset BULK list */
+ if (bulk) /* reset bulk list */
writel (0, &ohci->regs->ed_bulkcurrent);
if (!ohci->ed_rm_list[!frame]) {
- ohci->hc_control |= OHCI_CTRL_CLE | OHCI_CTRL_BLE;
+ if (ohci->ed_controltail)
+ ohci->hc_control |= OHCI_CTRL_CLE;
+ if (ohci->ed_bulktail)
+ ohci->hc_control |= OHCI_CTRL_BLE;
writel (ohci->hc_control, &ohci->regs->control);
}
}
- ohci->ed_rm_list[frame] = NULL;
+ ohci->ed_rm_list[frame] = NULL;
spin_unlock_irqrestore (&usb_ed_lock, flags);
}
@@ -1375,17 +1510,25 @@
/* error code of transfer */
cc = TD_CC_GET (tdINFO);
- if( cc == TD_CC_STALL) usb_endpoint_halt(urb->dev, usb_pipeendpoint(urb->pipe), usb_pipeout(urb->pipe));
+ if (cc == TD_CC_STALL)
+ usb_endpoint_halt(urb->dev,
+ usb_pipeendpoint(urb->pipe),
+ usb_pipeout(urb->pipe));
- if (!(urb->transfer_flags & USB_DISABLE_SPD) && (cc == TD_DATAUNDERRUN))
- cc = TD_CC_NOERROR;
+ if (!(urb->transfer_flags & USB_DISABLE_SPD)
+ && (cc == TD_DATAUNDERRUN))
+ cc = TD_CC_NOERROR;
+
if (++(urb_priv->td_cnt) == urb_priv->length) {
- if (urb_priv->state != URB_DEL && !(ed->state & ED_DEL) && ed->state != ED_NEW) {
+ if ((ed->state & (ED_OPER | ED_UNLINK))
+ && (urb_priv->state != URB_DEL)) {
urb->status = cc_to_error[cc];
sohci_return_urb (urb);
} else {
- urb_rm_priv (urb);
- }
+ spin_lock_irqsave (&usb_ed_lock, flags);
+ dl_del_urb (urb);
+ spin_unlock_irqrestore (&usb_ed_lock, flags);
+ }
}
spin_lock_irqsave (&usb_ed_lock, flags);
@@ -1393,13 +1536,13 @@
edHeadP = le32_to_cpup (&ed->hwHeadP) & 0xfffffff0;
edTailP = le32_to_cpup (&ed->hwTailP);
- if((edHeadP == edTailP) && (ed->state == ED_OPER))
- ep_unlink (ohci, ed); /* unlink eds if they are not busy */
-
- }
- spin_unlock_irqrestore (&usb_ed_lock, flags);
+ /* unlink eds if they are not busy */
+ if ((edHeadP == edTailP) && (ed->state == ED_OPER))
+ ep_unlink (ohci, ed);
+ }
+ spin_unlock_irqrestore (&usb_ed_lock, flags);
- td_list = td_list_next;
+ td_list = td_list_next;
}
}
@@ -1530,7 +1673,7 @@
/* ignore timers firing during PM suspend, etc */
if ((ohci->hc_control & OHCI_CTRL_HCFS) != OHCI_USB_OPER)
- return;
+ goto out;
if(ohci->rh.send) {
len = rh_send_irq (ohci, urb->transfer_buffer, urb->transfer_buffer_length);
@@ -1539,9 +1682,11 @@
#ifdef DEBUG
urb_print (urb, "RET-t(rh)", usb_pipeout (urb->pipe));
#endif
- if (urb->complete) urb->complete (urb);
+ if (urb->complete)
+ urb->complete (urb);
}
- }
+ }
+ out:
rh_init_int_timer (urb);
}
@@ -1594,7 +1739,6 @@
__u16 wLength;
if (usb_pipeint(pipe)) {
-
ohci->rh.urb = urb;
ohci->rh.send = 1;
ohci->rh.interval = urb->interval;
@@ -1765,9 +1909,11 @@
urb_print (urb, "RET(rh)", usb_pipeout (urb->pipe));
#endif
+ urb->hcpriv = NULL;
+ usb_dec_dev_use (usb_dev);
+ urb->dev = NULL;
if (urb->complete)
urb->complete (urb);
- usb_dec_dev_use (urb->dev);
return 0;
}
@@ -1780,6 +1926,17 @@
if (ohci->rh.urb == urb) {
ohci->rh.send = 0;
del_timer (&ohci->rh.rh_int_timer);
+ ohci->rh.urb = NULL;
+
+ urb->hcpriv = NULL;
+ 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;
}
return 0;
}
@@ -2290,6 +2447,12 @@
ohci->disabled = 0;
ohci->hc_control = OHCI_CONTROL_INIT | OHCI_USB_OPER;
+ if (!ohci->ed_rm_list[0] & !ohci->ed_rm_list[1]) {
+ if (ohci->ed_controltail)
+ ohci->hc_control |= OHCI_CTRL_CLE;
+ if (ohci->ed_bulktail)
+ ohci->hc_control |= OHCI_CTRL_BLE;
+ }
writel (ohci->hc_control, &ohci->regs->control);
#ifdef CONFIG_PMAC_PBOOK
enable_irq (ohci->irq);
@@ -2386,7 +2549,6 @@
return ret;
}
-
/*-------------------------------------------------------------------------*/
static void __exit ohci_hcd_cleanup (void)
@@ -2401,6 +2563,5 @@
module_exit (ohci_hcd_cleanup);
-
-MODULE_AUTHOR ("Roman Weissgaerber <weissg@vienna.at>");
+MODULE_AUTHOR ("Roman Weissgaerber <weissg@vienna.at>, David Brownell");
MODULE_DESCRIPTION ("USB OHCI Host Controller Driver");
FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen (who was at: slshen@lbl.gov)