summaryrefslogtreecommitdiffstats
path: root/drivers/usb/uhci.c
diff options
context:
space:
mode:
authorRalf Baechle <ralf@linux-mips.org>2000-04-19 04:00:00 +0000
committerRalf Baechle <ralf@linux-mips.org>2000-04-19 04:00:00 +0000
commit46e045034336a2cc90c1798cd7cc07af744ddfd6 (patch)
tree3b9b51fc482e729f663d25333e77fbed9aaa939a /drivers/usb/uhci.c
parent31dc59d503a02e84c4de98826452acaeb56dc15a (diff)
Merge with Linux 2.3.99-pre4.
Diffstat (limited to 'drivers/usb/uhci.c')
-rw-r--r--drivers/usb/uhci.c404
1 files changed, 287 insertions, 117 deletions
diff --git a/drivers/usb/uhci.c b/drivers/usb/uhci.c
index 9927b5382..2fa4e30b6 100644
--- a/drivers/usb/uhci.c
+++ b/drivers/usb/uhci.c
@@ -56,11 +56,11 @@ static kmem_cache_t *uhci_up_cachep; /* urb_priv */
static LIST_HEAD(uhci_list);
-static int rh_submit_urb(urb_t *urb);
-static int rh_unlink_urb(urb_t *urb);
+static int rh_submit_urb(struct urb *urb);
+static int rh_unlink_urb(struct urb *urb);
static int uhci_get_current_frame_number(struct usb_device *dev);
-static int uhci_unlink_generic(urb_t *urb);
-static int uhci_unlink_urb(urb_t *urb);
+static int uhci_unlink_generic(struct urb *urb);
+static int uhci_unlink_urb(struct urb *urb);
#define min(a,b) (((a)<(b))?(a):(b))
@@ -77,9 +77,8 @@ static int uhci_alloc_dev(struct usb_device *dev)
static int uhci_free_dev(struct usb_device *dev)
{
- urb_t *u;
struct uhci *uhci = (struct uhci *)dev->bus->hcpriv;
- struct list_head *tmp, *next, *head = &uhci->urb_list;
+ struct list_head *tmp, *head = &uhci->urb_list;
unsigned long flags;
/* Walk through the entire URB list and forcefully remove any */
@@ -87,14 +86,12 @@ static int uhci_free_dev(struct usb_device *dev)
nested_lock(&uhci->urblist_lock, flags);
tmp = head->next;
while (tmp != head) {
- u = list_entry(tmp, urb_t, urb_list);
+ struct urb *u = list_entry(tmp, struct urb, urb_list);
- next = tmp->next;
+ tmp = tmp->next;
if (u->dev == dev)
uhci_unlink_urb(u);
-
- tmp = next;
}
nested_unlock(&uhci->urblist_lock, flags);
@@ -115,13 +112,31 @@ static void uhci_remove_urb_list(struct uhci *uhci, struct urb *urb)
unsigned long flags;
nested_lock(&uhci->urblist_lock, flags);
- if (urb->urb_list.next != &urb->urb_list) {
+ if (!list_empty(&urb->urb_list)) {
list_del(&urb->urb_list);
INIT_LIST_HEAD(&urb->urb_list);
}
nested_unlock(&uhci->urblist_lock, flags);
}
+void uhci_set_next_interrupt(struct uhci *uhci)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&uhci->framelist_lock, flags);
+ uhci->skel_term_td.status |= TD_CTRL_IOC;
+ spin_unlock_irqrestore(&uhci->framelist_lock, flags);
+}
+
+void uhci_clear_next_interrupt(struct uhci *uhci)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&uhci->framelist_lock, flags);
+ uhci->skel_term_td.status &= ~TD_CTRL_IOC;
+ spin_unlock_irqrestore(&uhci->framelist_lock, flags);
+}
+
static struct uhci_td *uhci_alloc_td(struct usb_device *dev)
{
struct uhci_td *td;
@@ -135,8 +150,8 @@ static struct uhci_td *uhci_alloc_td(struct usb_device *dev)
td->frameptr = NULL;
td->nexttd = td->prevtd = NULL;
- td->list.next = td->list.prev = NULL;
td->dev = dev;
+ INIT_LIST_HEAD(&td->list);
usb_inc_dev_use(dev);
@@ -235,23 +250,32 @@ static void uhci_remove_td(struct uhci *uhci, struct uhci_td *td)
*/
static void uhci_insert_tds_in_qh(struct uhci_qh *qh, struct urb *urb, int breadth)
{
+ struct list_head *tmp, *head;
struct urb_priv *urbp = (struct urb_priv *)urb->hcpriv;
struct uhci_td *td, *prevtd;
if (!urbp)
return;
- td = urbp->list.begin;
- if (!td)
+ head = &urbp->list;
+ tmp = head->next;
+ if (head == tmp)
return;
+ td = list_entry(tmp, struct uhci_td, list);
+
/* Add the first TD to the QH element pointer */
qh->element = virt_to_bus(td) | (breadth ? 0 : UHCI_PTR_DEPTH);
prevtd = td;
/* Then link the rest of the TD's */
- for (td = td->list.next; td; td = td->list.next) {
+ tmp = tmp->next;
+ while (tmp != head) {
+ td = list_entry(tmp, struct uhci_td, list);
+
+ tmp = tmp->next;
+
prevtd->link = virt_to_bus(td) | (breadth ? 0 : UHCI_PTR_DEPTH);
prevtd = td;
@@ -260,9 +284,45 @@ static void uhci_insert_tds_in_qh(struct uhci_qh *qh, struct urb *urb, int bread
prevtd->link = UHCI_PTR_TERM;
}
+/* This function will append one URB's QH to another URB's QH. This is for */
+/* USB_QUEUE_BULK support */
+static void uhci_append_urb_qh(struct uhci *uhci, struct urb *eurb, struct urb *urb)
+{
+ struct urb *nurb;
+ struct urb_priv *eurbp, *urbp, *nurbp;
+ struct list_head *tmp;
+ struct uhci_td *td, *ntd;
+ unsigned long flags;
+
+ eurbp = eurb->hcpriv;
+ urbp = urb->hcpriv;
+
+ spin_lock_irqsave(&eurb->lock, flags);
+
+ /* Grab the last URB in the queue */
+ tmp = eurbp->urb_queue_list.prev;
+
+ /* Add this one to the end */
+ list_add_tail(&urbp->urb_queue_list, &eurbp->urb_queue_list);
+
+ spin_unlock_irqrestore(&eurb->lock, flags);
+
+ nurbp = list_entry(tmp, struct urb_priv, urb_queue_list);
+ nurb = nurbp->urb;
+
+ tmp = nurbp->list.prev;
+ td = list_entry(tmp, struct uhci_td, list);
+
+ tmp = urbp->list.next;
+ ntd = list_entry(tmp, struct uhci_td, list);
+
+ /* No breadth since this will only be called for bulk transfers */
+ td->link = virt_to_bus(ntd);
+}
+
static void uhci_free_td(struct uhci_td *td)
{
- if (td->list.next || td->list.prev)
+ if (!list_empty(&td->list))
dbg("td is still in URB list!");
kmem_cache_free(uhci_td_cachep, td);
@@ -322,6 +382,10 @@ static void uhci_insert_qh(struct uhci *uhci, struct uhci_qh *skelqh, struct uhc
static void uhci_remove_qh(struct uhci *uhci, struct uhci_qh *qh)
{
unsigned long flags;
+ int delayed;
+
+ /* If the QH isn't queued, then we don't need to delay unlink it */
+ delayed = (qh->prevqh || qh->nextqh);
spin_lock_irqsave(&uhci->framelist_lock, flags);
if (qh->prevqh) {
@@ -333,9 +397,20 @@ static void uhci_remove_qh(struct uhci *uhci, struct uhci_qh *qh)
qh->prevqh = qh->nextqh = NULL;
spin_unlock_irqrestore(&uhci->framelist_lock, flags);
- spin_lock_irqsave(&uhci->qh_remove_lock, flags);
- list_add(&qh->remove_list, &uhci->qh_remove_list);
- spin_unlock_irqrestore(&uhci->qh_remove_lock, flags);
+ if (delayed) {
+ spin_lock_irqsave(&uhci->qh_remove_lock, flags);
+
+ /* Check to see if the remove list is empty */
+ /* Set the IOC bit to force an interrupt so we can remove the QH */
+ if (list_empty(&uhci->qh_remove_list))
+ uhci_set_next_interrupt(uhci);
+
+ /* Add it */
+ list_add(&qh->remove_list, &uhci->qh_remove_list);
+
+ spin_unlock_irqrestore(&uhci->qh_remove_lock, flags);
+ } else
+ uhci_free_qh(qh);
}
struct urb_priv *uhci_alloc_urb_priv(struct urb *urb)
@@ -348,91 +423,86 @@ struct urb_priv *uhci_alloc_urb_priv(struct urb *urb)
memset((void *)urbp, 0, sizeof(*urbp));
- urbp->list.begin = urbp->list.end = NULL;
+ urbp->inserttime = jiffies;
+ urbp->urb = urb;
+
+ INIT_LIST_HEAD(&urbp->list);
+ INIT_LIST_HEAD(&urbp->urb_queue_list);
urb->hcpriv = urbp;
- urbp->inserttime = jiffies;
-
usb_inc_dev_use(urb->dev);
return urbp;
}
-static void uhci_add_td_to_urb(urb_t *urb, struct uhci_td *td)
+static void uhci_add_td_to_urb(struct urb *urb, struct uhci_td *td)
{
struct urb_priv *urbp = (struct urb_priv *)urb->hcpriv;
td->urb = urb;
- if (!urbp->list.begin)
- urbp->list.begin = td;
-
- if (urbp->list.end) {
- urbp->list.end->list.next = td;
- td->list.prev = urbp->list.end;
- }
- urbp->list.end = td;
+ list_add_tail(&td->list, &urbp->list);
}
-static void uhci_remove_td_from_urb(urb_t *urb, struct uhci_td *td)
+static void uhci_remove_td_from_urb(struct urb *urb, struct uhci_td *td)
{
- struct urb_priv *urbp = (struct urb_priv *)urb->hcpriv;
+ urb = NULL; /* No warnings */
- if (!urbp->list.begin && !urbp->list.end)
+ if (list_empty(&td->list))
return;
- if (td->list.prev)
- td->list.prev->list.next = td->list.next;
- else
- urbp->list.begin = td->list.next;
-
- if (td->list.next)
- td->list.next->list.prev = td->list.prev;
- else
- urbp->list.end = td->list.prev;
+ list_del(&td->list);
+ INIT_LIST_HEAD(&td->list);
- td->list.next = td->list.prev = NULL;
td->urb = NULL;
}
-static void uhci_destroy_urb_priv(urb_t *urb)
+static void uhci_destroy_urb_priv(struct urb *urb)
{
+ struct list_head *tmp, *head;
struct urb_priv *urbp;
struct uhci *uhci;
- struct uhci_td *td, *nexttd;
+ struct uhci_td *td;
unsigned long flags;
spin_lock_irqsave(&urb->lock, flags);
urbp = (struct urb_priv *)urb->hcpriv;
if (!urbp)
- return;
+ goto unlock;
if (!urb->dev || !urb->dev->bus || !urb->dev->bus->hcpriv)
- return;
+ goto unlock;
uhci = urb->dev->bus->hcpriv;
- td = urbp->list.begin;
- while (td) {
- nexttd = td->list.next;
+ head = &urbp->list;
+ tmp = head->next;
+ while (tmp != head) {
+ td = list_entry(tmp, struct uhci_td, list);
+
+ tmp = tmp->next;
uhci_remove_td_from_urb(urb, td);
uhci_remove_td(uhci, td);
uhci_free_td(td);
-
- td = nexttd;
+ }
+
+ if (!list_empty(&urbp->urb_queue_list)) {
+ list_del(&urbp->urb_queue_list);
+ INIT_LIST_HEAD(&urbp->urb_queue_list);
}
urb->hcpriv = NULL;
kmem_cache_free(uhci_up_cachep, urbp);
- spin_unlock_irqrestore(&urb->lock, flags);
-
usb_dec_dev_use(urb->dev);
+
+unlock:
+ spin_unlock_irqrestore(&urb->lock, flags);
}
static void uhci_inc_fsbr(struct uhci *uhci, struct urb *urb)
@@ -448,7 +518,7 @@ static void uhci_inc_fsbr(struct uhci *uhci, struct urb *urb)
if (!urbp->fsbr) {
urbp->fsbr = 1;
if (!uhci->fsbr++)
- uhci->skel_term_td.link = virt_to_bus(&uhci->skel_hs_control_qh) | UHCI_PTR_QH;
+ uhci->skel_term_qh.link = virt_to_bus(&uhci->skel_hs_control_qh) | UHCI_PTR_QH;
}
spin_unlock_irqrestore(&uhci->framelist_lock, flags);
@@ -467,7 +537,7 @@ static void uhci_dec_fsbr(struct uhci *uhci, struct urb *urb)
if (urbp->fsbr) {
urbp->fsbr = 0;
if (!--uhci->fsbr)
- uhci->skel_term_td.link = UHCI_PTR_TERM;
+ uhci->skel_term_qh.link = UHCI_PTR_TERM;
}
spin_unlock_irqrestore(&uhci->framelist_lock, flags);
@@ -508,7 +578,7 @@ static int uhci_map_status(int status, int dir_out)
/*
* Control transfers
*/
-static int uhci_submit_control(urb_t *urb)
+static int uhci_submit_control(struct urb *urb)
{
struct urb_priv *urbp = (struct urb_priv *)urb->hcpriv;
struct uhci *uhci = (struct uhci *)urb->dev->bus->hcpriv;
@@ -602,10 +672,12 @@ static int uhci_submit_control(urb_t *urb)
if (urb->pipe & TD_CTRL_LS) {
uhci_insert_tds_in_qh(qh, urb, 0);
uhci_insert_qh(uhci, &uhci->skel_ls_control_qh, qh);
+ urbp->queued = 0;
} else {
uhci_insert_tds_in_qh(qh, urb, 1);
uhci_insert_qh(uhci, &uhci->skel_hs_control_qh, qh);
uhci_inc_fsbr(uhci, urb);
+ urbp->queued = 0;
}
urbp->qh = qh;
@@ -617,10 +689,11 @@ static int uhci_submit_control(urb_t *urb)
return -EINPROGRESS;
}
-static int usb_control_retrigger_status(urb_t *urb);
+static int usb_control_retrigger_status(struct urb *urb);
-static int uhci_result_control(urb_t *urb)
+static int uhci_result_control(struct urb *urb)
{
+ struct list_head *tmp, *head;
struct urb_priv *urbp = urb->hcpriv;
struct uhci_td *td;
unsigned int status;
@@ -628,13 +701,16 @@ static int uhci_result_control(urb_t *urb)
if (!urbp)
return -EINVAL;
- td = urbp->list.begin;
- if (!td)
+ head = &urbp->list;
+ tmp = head->next;
+ if (head == tmp)
return -EINVAL;
if (urbp->short_control_packet)
goto status_phase;
+ td = list_entry(tmp, struct uhci_td, list);
+
/* The first TD is the SETUP phase, check the status, but skip */
/* the count */
status = uhci_status_bits(td->status);
@@ -646,10 +722,13 @@ static int uhci_result_control(urb_t *urb)
urb->actual_length = 0;
- td = td->list.next;
-
/* The rest of the TD's (but the last) are data */
- while (td && td->list.next) {
+ tmp = tmp->next;
+ while (tmp != head && tmp->next != head) {
+ td = list_entry(tmp, struct uhci_td, list);
+
+ tmp = tmp->next;
+
status = uhci_status_bits(td->status);
if (status & TD_CTRL_ACTIVE)
return -EINPROGRESS;
@@ -664,18 +743,18 @@ static int uhci_result_control(urb_t *urb)
if (status)
goto td_error;
-
- td = td->list.next;
}
status_phase:
+ td = list_entry(tmp, struct uhci_td, list);
+
/* Control status phase */
status = uhci_status_bits(td->status);
/* APC BackUPS Pro kludge */
/* It tries to send all of the descriptor instead of the amount */
/* we requested */
- if (td->status & TD_CTRL_IOC &&
+ if (td->status & TD_CTRL_IOC && /* IOC is masked out by uhci_status_bits */
status & TD_CTRL_ACTIVE &&
status & TD_CTRL_NAK)
return 0;
@@ -704,34 +783,37 @@ td_error:
return uhci_map_status(status, uhci_packetout(td->info));
}
-static int usb_control_retrigger_status(urb_t *urb)
+static int usb_control_retrigger_status(struct urb *urb)
{
+ struct list_head *tmp, *head;
struct urb_priv *urbp = (struct urb_priv *)urb->hcpriv;
struct uhci *uhci = urb->dev->bus->hcpriv;
- struct uhci_td *td, *nexttd;
urbp->short_control_packet = 1;
+ /* Create a new QH to avoid pointer overwriting problems */
+ uhci_remove_qh(uhci, urbp->qh);
+
/* Delete all of the TD's except for the status TD at the end */
- td = urbp->list.begin;
- while (td && td->list.next) {
- nexttd = td->list.next;
+ head = &urbp->list;
+ tmp = head->next;
+ while (tmp != head && tmp->next != head) {
+ struct uhci_td *td = list_entry(tmp, struct uhci_td, list);
+
+ tmp = tmp->next;
uhci_remove_td_from_urb(urb, td);
uhci_remove_td(uhci, td);
uhci_free_td(td);
-
- td = nexttd;
}
- /* Create a new QH to avoid pointer overwriting problems */
- uhci_remove_qh(uhci, urbp->qh);
-
urbp->qh = uhci_alloc_qh(urb->dev);
- if (!urbp->qh)
+ if (!urbp->qh) {
+ err("unable to allocate new QH for control retrigger");
return -ENOMEM;
+ }
/* One TD, who cares about Breadth first? */
uhci_insert_tds_in_qh(urbp->qh, urb, 0);
@@ -741,6 +823,7 @@ static int usb_control_retrigger_status(urb_t *urb)
uhci_insert_qh(uhci, &uhci->skel_ls_control_qh, urbp->qh);
else
uhci_insert_qh(uhci, &uhci->skel_hs_control_qh, urbp->qh);
+ urbp->queued = 0;
return -EINPROGRESS;
}
@@ -748,7 +831,7 @@ static int usb_control_retrigger_status(urb_t *urb)
/*
* Interrupt transfers
*/
-static int uhci_submit_interrupt(urb_t *urb)
+static int uhci_submit_interrupt(struct urb *urb)
{
struct uhci_td *td;
unsigned long destination, status;
@@ -782,18 +865,25 @@ static int uhci_submit_interrupt(urb_t *urb)
return -EINPROGRESS;
}
-static int uhci_result_interrupt(urb_t *urb)
+static int uhci_result_interrupt(struct urb *urb)
{
+ struct list_head *tmp, *head;
struct urb_priv *urbp = urb->hcpriv;
- struct uhci_td *td;
unsigned int status;
+ struct uhci_td *td;
if (!urbp)
return -EINVAL;
urb->actual_length = 0;
- for (td = urbp->list.begin; td; td = td->list.next) {
+ head = &urbp->list;
+ tmp = head->next;
+ while (tmp != head) {
+ td = list_entry(tmp, struct uhci_td, list);
+
+ tmp = tmp->next;
+
status = uhci_status_bits(td->status);
if (status & TD_CTRL_ACTIVE)
return -EINPROGRESS;
@@ -836,15 +926,17 @@ td_error:
return uhci_map_status(status, uhci_packetout(td->info));
}
-static void uhci_reset_interrupt(urb_t *urb)
+static void uhci_reset_interrupt(struct urb *urb)
{
+ struct list_head *tmp;
struct urb_priv *urbp = (struct urb_priv *)urb->hcpriv;
struct uhci_td *td;
if (!urbp)
return;
- td = urbp->list.begin;
+ tmp = urbp->list.next;
+ td = list_entry(tmp, struct uhci_td, list);
if (!td)
return;
@@ -859,7 +951,7 @@ static void uhci_reset_interrupt(urb_t *urb)
/*
* Bulk transfers
*/
-static int uhci_submit_bulk(urb_t *urb)
+static int uhci_submit_bulk(struct urb *urb, struct urb *eurb)
{
struct uhci_td *td;
struct uhci_qh *qh;
@@ -881,14 +973,14 @@ static int uhci_submit_bulk(urb_t *urb)
destination = (urb->pipe & PIPE_DEVEP_MASK) | usb_packetid(urb->pipe);
/* 3 errors */
- status = TD_CTRL_ACTIVE | (3 << 27);
+ status = TD_CTRL_ACTIVE | (3 << TD_CTRL_C_ERR_SHIFT);
if (!(urb->transfer_flags & USB_DISABLE_SPD))
status |= TD_CTRL_SPD;
/*
* Build the DATA TD's
*/
- while (len > 0) {
+ do { /* Allow zero length packets */
int pktsze = len;
if (pktsze > maxsze)
@@ -912,16 +1004,24 @@ static int uhci_submit_bulk(urb_t *urb)
usb_dotoggle(urb->dev, usb_pipeendpoint(urb->pipe),
usb_pipeout(urb->pipe));
- }
+ } while (len > 0);
qh = uhci_alloc_qh(urb->dev);
if (!qh)
return -ENOMEM;
+ urbp->qh = qh;
+
+ /* Always assume depth first */
uhci_insert_tds_in_qh(qh, urb, 1);
- uhci_insert_qh(uhci, &uhci->skel_bulk_qh, qh);
- urbp->qh = qh;
+ if (urb->transfer_flags & USB_QUEUE_BULK && eurb) {
+ uhci_append_urb_qh(uhci, eurb, urb);
+ urbp->queued = 1;
+ } else {
+ uhci_insert_qh(uhci, &uhci->skel_bulk_qh, qh);
+ urbp->queued = 0;
+ }
uhci_add_urb_list(uhci, urb);
@@ -936,9 +1036,9 @@ static int uhci_submit_bulk(urb_t *urb)
/*
* Isochronous transfers
*/
-static int isochronous_find_limits(urb_t *urb, unsigned int *start, unsigned int *end)
+static int isochronous_find_limits(struct urb *urb, unsigned int *start, unsigned int *end)
{
- urb_t *u, *last_urb = NULL;
+ struct urb *last_urb = NULL;
struct uhci *uhci = (struct uhci *)urb->dev->bus->hcpriv;
struct list_head *tmp, *head = &uhci->urb_list;
int ret = 0;
@@ -947,7 +1047,7 @@ static int isochronous_find_limits(urb_t *urb, unsigned int *start, unsigned int
nested_lock(&uhci->urblist_lock, flags);
tmp = head->next;
while (tmp != head) {
- u = list_entry(tmp, urb_t, urb_list);
+ struct urb *u = list_entry(tmp, struct urb, urb_list);
/* look for pending URB's with identical pipe handle */
if ((urb->pipe == u->pipe) && (urb->dev == u->dev) &&
@@ -970,7 +1070,7 @@ static int isochronous_find_limits(urb_t *urb, unsigned int *start, unsigned int
return ret;
}
-static int isochronous_find_start(urb_t *urb)
+static int isochronous_find_start(struct urb *urb)
{
int limits;
unsigned int start = 0, end = 0;
@@ -996,7 +1096,7 @@ static int isochronous_find_start(urb_t *urb)
return 0;
}
-static int uhci_submit_isochronous(urb_t *urb)
+static int uhci_submit_isochronous(struct urb *urb)
{
struct uhci_td *td;
struct uhci *uhci = (struct uhci *)urb->dev->bus->hcpriv;
@@ -1034,10 +1134,10 @@ static int uhci_submit_isochronous(urb_t *urb)
return -EINPROGRESS;
}
-static int uhci_result_isochronous(urb_t *urb)
+static int uhci_result_isochronous(struct urb *urb)
{
+ struct list_head *tmp, *head;
struct urb_priv *urbp = (struct urb_priv *)urb->hcpriv;
- struct uhci_td *td;
int status;
int i, ret = 0;
@@ -1046,9 +1146,15 @@ static int uhci_result_isochronous(urb_t *urb)
urb->actual_length = 0;
- for (i = 0, td = urbp->list.begin; td; i++, td = td->list.next) {
+ i = 0;
+ head = &urbp->list;
+ tmp = head->next;
+ while (tmp != head) {
+ struct uhci_td *td = list_entry(tmp, struct uhci_td, list);
int actlength;
+ tmp = tmp->next;
+
if (td->status & TD_CTRL_ACTIVE)
return -EINPROGRESS;
@@ -1062,16 +1168,47 @@ static int uhci_result_isochronous(urb_t *urb)
urb->error_count++;
ret = status;
}
+
+ i++;
}
return ret;
}
-static int uhci_submit_urb(urb_t *urb)
+static struct urb *uhci_find_urb_ep(struct uhci *uhci, struct urb *urb)
+{
+ struct list_head *tmp, *head = &uhci->urb_list;
+ unsigned long flags;
+ struct urb *u = NULL;
+
+ if (usb_pipeisoc(urb->pipe))
+ return NULL;
+
+ nested_lock(&uhci->urblist_lock, flags);
+ tmp = head->next;
+ while (tmp != head) {
+ u = list_entry(tmp, struct urb, urb_list);
+
+ tmp = tmp->next;
+
+ if (u->dev == urb->dev &&
+ u->pipe == urb->pipe)
+ goto found;
+ }
+ u = NULL;
+
+found:
+ nested_unlock(&uhci->urblist_lock, flags);
+
+ return u;
+}
+
+static int uhci_submit_urb(struct urb *urb)
{
int ret = -EINVAL;
struct uhci *uhci;
unsigned long flags;
+ struct urb *u;
if (!urb)
return -EINVAL;
@@ -1085,6 +1222,10 @@ static int uhci_submit_urb(urb_t *urb)
if (usb_pipedevice(urb->pipe) == uhci->rh.devnum)
return rh_submit_urb(urb);
+ u = uhci_find_urb_ep(uhci, urb);
+ if (u && !(urb->transfer_flags & USB_QUEUE_BULK))
+ return -ENXIO;
+
spin_lock_irqsave(&urb->lock, flags);
if (!uhci_alloc_urb_priv(urb)) {
@@ -1100,7 +1241,7 @@ static int uhci_submit_urb(urb_t *urb)
ret = uhci_submit_interrupt(urb);
break;
case PIPE_BULK:
- ret = uhci_submit_bulk(urb);
+ ret = uhci_submit_bulk(urb, u);
break;
case PIPE_ISOCHRONOUS:
ret = uhci_submit_isochronous(urb);
@@ -1124,9 +1265,9 @@ static int uhci_submit_urb(urb_t *urb)
*
* Must be called with urblist_lock acquired
*/
-static void uhci_transfer_result(urb_t *urb)
+static void uhci_transfer_result(struct urb *urb)
{
- urb_t *turb;
+ struct urb *turb;
int proceed = 0, is_ring = 0;
int ret = -EINVAL;
unsigned long flags;
@@ -1206,7 +1347,7 @@ static void uhci_transfer_result(urb_t *urb)
}
}
-static int uhci_unlink_generic(urb_t *urb)
+static int uhci_unlink_generic(struct urb *urb)
{
struct urb_priv *urbp = urb->hcpriv;
struct uhci *uhci = (struct uhci *)urb->dev->bus->hcpriv;
@@ -1222,12 +1363,21 @@ static int uhci_unlink_generic(urb_t *urb)
/* The interrupt loop will reclaim the QH's */
uhci_remove_qh(uhci, urbp->qh);
+ if (!list_empty(&urbp->urb_queue_list)) {
+ struct list_head *tmp = urbp->urb_queue_list.next;
+ struct urb_priv *nurbp = list_entry(tmp, struct urb_priv, urb_queue_list);
+ if (nurbp->queued) {
+ uhci_insert_qh(uhci, &uhci->skel_bulk_qh, nurbp->qh);
+ nurbp->queued = 0;
+ }
+ }
+
uhci_destroy_urb_priv(urb);
return 0;
}
-static int uhci_unlink_urb(urb_t *urb)
+static int uhci_unlink_urb(struct urb *urb)
{
struct uhci *uhci;
int ret = 0;
@@ -1252,7 +1402,13 @@ static int uhci_unlink_urb(urb_t *urb)
urb->status = -ECONNABORTED;
spin_lock_irqsave(&uhci->urb_remove_lock, flags);
+
+ /* Check to see if the remove list is empty */
+ if (list_empty(&uhci->urb_remove_list))
+ uhci_set_next_interrupt(uhci);
+
list_add(&urb->urb_list, &uhci->urb_remove_list);
+
spin_unlock_irqrestore(&uhci->urb_remove_lock, flags);
} else {
urb->status = -ENOENT;
@@ -1372,7 +1528,7 @@ static __u8 root_hub_hub_des[] =
/*-------------------------------------------------------------------------*/
/* prepare Interrupt pipe transaction data; HUB INTERRUPT ENDPOINT */
-static int rh_send_irq(urb_t *urb)
+static int rh_send_irq(struct urb *urb)
{
int i, len = 1;
struct uhci *uhci = (struct uhci *)urb->dev->bus->hcpriv;
@@ -1399,11 +1555,11 @@ static int rh_send_irq(urb_t *urb)
/*-------------------------------------------------------------------------*/
/* Virtual Root Hub INTs are polled by this timer every "interval" ms */
-static int rh_init_int_timer(urb_t *urb);
+static int rh_init_int_timer(struct urb *urb);
static void rh_int_timer_do(unsigned long ptr)
{
- urb_t *urb = (urb_t *)ptr, *u;
+ struct urb *urb = (struct urb *)ptr;
struct uhci *uhci = (struct uhci *)urb->dev->bus->hcpriv;
struct list_head *tmp, *head = &uhci->urb_list;
struct urb_priv *urbp;
@@ -1422,15 +1578,22 @@ static void rh_int_timer_do(unsigned long ptr)
nested_lock(&uhci->urblist_lock, flags);
tmp = head->next;
while (tmp != head) {
- u = list_entry(tmp, urb_t, urb_list);
+ struct urb *u = list_entry(tmp, urb_t, urb_list);
+
+ tmp = tmp->next;
urbp = (struct urb_priv *)u->hcpriv;
if (urbp) {
- if (urbp->fsbr && time_after(jiffies, urbp->inserttime + IDLE_TIMEOUT))
+ /* Check if the FSBR timed out */
+ if (urbp->fsbr && time_after(urbp->inserttime + IDLE_TIMEOUT, jiffies))
uhci_dec_fsbr(uhci, u);
- }
- tmp = tmp->next;
+ /* Check if the URB timed out */
+ if (u->timeout && time_after(u->timeout, jiffies)) {
+ u->transfer_flags |= USB_ASYNC_UNLINK | USB_TIMEOUT_KILLED;
+ uhci_unlink_urb(u);
+ }
+ }
}
nested_unlock(&uhci->urblist_lock, flags);
@@ -1439,7 +1602,7 @@ static void rh_int_timer_do(unsigned long ptr)
/*-------------------------------------------------------------------------*/
/* Root Hub INTs are polled by this timer */
-static int rh_init_int_timer(urb_t *urb)
+static int rh_init_int_timer(struct urb *urb)
{
struct uhci *uhci = (struct uhci *)urb->dev->bus->hcpriv;
@@ -1472,7 +1635,7 @@ static int rh_init_int_timer(urb_t *urb)
** Root Hub Control Pipe
*************************/
-static int rh_submit_urb(urb_t *urb)
+static int rh_submit_urb(struct urb *urb)
{
struct uhci *uhci = (struct uhci *)urb->dev->bus->hcpriv;
unsigned int pipe = urb->pipe;
@@ -1588,7 +1751,7 @@ static int rh_submit_urb(urb_t *urb)
OK(0);
case RH_PORT_RESET:
SET_RH_PORTSTAT(USBPORTSC_PR);
- wait_ms(10);
+ wait_ms(50); /* USB v1.1 7.1.7.3 */
uhci->rh.c_p_r[wIndex - 1] = 1;
CLR_RH_PORTSTAT(USBPORTSC_PR);
udelay(10);
@@ -1636,6 +1799,11 @@ static int rh_submit_urb(urb_t *urb)
OK(1);
case RH_SET_CONFIGURATION:
OK(0);
+ case RH_GET_INTERFACE | RH_INTERFACE:
+ *(__u8 *)data = 0x00;
+ OK(1);
+ case RH_SET_INTERFACE | RH_INTERFACE:
+ OK(0);
default:
stat = -EPIPE;
}
@@ -1649,7 +1817,7 @@ static int rh_submit_urb(urb_t *urb)
}
/*-------------------------------------------------------------------------*/
-static int rh_unlink_urb(urb_t *urb)
+static int rh_unlink_urb(struct urb *urb)
{
struct uhci *uhci = (struct uhci *)urb->dev->bus->hcpriv;
@@ -1728,6 +1896,8 @@ static void uhci_interrupt(int irq, void *__uhci, struct pt_regs *regs)
}
spin_unlock(&uhci->urb_remove_lock);
+ uhci_clear_next_interrupt(uhci);
+
/* Walk the list of pending TD's to see which ones completed */
nested_lock(&uhci->urblist_lock, flags);
head = &uhci->urb_list;
@@ -2137,7 +2307,7 @@ int uhci_init(void)
/* We only want to return an error code if ther was an error */
/* and we didn't find a UHCI controller */
- if (retval && uhci_list.next == &uhci_list)
+ if (retval && list_empty(&uhci_list))
goto init_failed;
return 0;