#include <linux/unistd.h>
#include <linux/interrupt.h>
#include <linux/spinlock.h>
-#include <linux/proc_fs.h>
+#include <linux/debugfs.h>
#include <linux/pm.h>
#include <linux/dmapool.h>
#include <linux/dma-mapping.h>
#include <linux/usb.h>
+#include <linux/bitops.h>
-#include <asm/bitops.h>
#include <asm/uaccess.h>
#include <asm/io.h>
#include <asm/irq.h>
* debug = 0, no debugging messages
* debug = 1, dump failed URB's except for stalls
* debug = 2, dump all failed URB's (including stalls)
- * show all queues in /proc/driver/uhci/[pci_addr]
+ * show all queues in /debug/uhci/[pci_addr]
* debug = 3, show all TD's in URB's when dumping
*/
#ifdef DEBUG
#else
static int debug = 0;
#endif
-MODULE_PARM(debug, "i");
+module_param(debug, int, S_IRUGO | S_IWUSR);
MODULE_PARM_DESC(debug, "Debug level");
static char *errbuf;
#define ERRBUF_LEN (32 * 1024)
static kmem_cache_t *uhci_up_cachep; /* urb_priv */
-static int uhci_get_current_frame_number(struct uhci_hcd *uhci);
+static unsigned int uhci_get_current_frame_number(struct uhci_hcd *uhci);
static int uhci_urb_dequeue(struct usb_hcd *hcd, struct urb *urb);
static void uhci_unlink_generic(struct uhci_hcd *uhci, struct urb *urb);
+static void uhci_remove_pending_urbps(struct uhci_hcd *uhci);
+static void uhci_finish_completion(struct usb_hcd *hcd, struct pt_regs *regs);
+static void uhci_free_pending_qhs(struct uhci_hcd *uhci);
+static void uhci_free_pending_tds(struct uhci_hcd *uhci);
static void hc_state_transitions(struct uhci_hcd *uhci);
/* If a transfer is still active after this much time, turn off FSBR */
-#define IDLE_TIMEOUT (HZ / 20) /* 50 ms */
-#define FSBR_DELAY (HZ / 20) /* 50 ms */
+#define IDLE_TIMEOUT msecs_to_jiffies(50)
+#define FSBR_DELAY msecs_to_jiffies(50)
/* When we timeout an idle transfer for FSBR, we'll switch it over to */
/* depth first traversal. We'll do it in groups of this number of TD's */
return td;
}
-static inline void uhci_fill_td(struct uhci_td *td, __u32 status,
- __u32 token, __u32 buffer)
+static inline void uhci_fill_td(struct uhci_td *td, u32 status,
+ u32 token, u32 buffer)
{
td->status = cpu_to_le32(status);
td->token = cpu_to_le32(token);
*/
static void uhci_insert_td_frame_list(struct uhci_hcd *uhci, struct uhci_td *td, unsigned framenum)
{
- framenum %= UHCI_NUMFRAMES;
+ framenum &= (UHCI_NUMFRAMES - 1);
td->frame = framenum;
list_add_tail(&td->fl_list, &ftd->fl_list);
td->link = ltd->link;
- mb();
+ wmb();
ltd->link = cpu_to_le32(td->dma_handle);
} else {
td->link = uhci->fl->frame[framenum];
- mb();
+ wmb();
uhci->fl->frame[framenum] = cpu_to_le32(td->dma_handle);
uhci->fl->frame_cpu[framenum] = td;
}
ptd->link = td->link;
}
- mb();
+ wmb();
td->link = UHCI_PTR_TERM;
list_del_init(&td->fl_list);
}
/*
- * Inserts a td into qh list at the top.
+ * Inserts a td list into qh.
*/
-static void uhci_insert_tds_in_qh(struct uhci_qh *qh, struct urb *urb, u32 breadth)
+static void uhci_insert_tds_in_qh(struct uhci_qh *qh, struct urb *urb, __le32 breadth)
{
- struct list_head *tmp, *head;
struct urb_priv *urbp = (struct urb_priv *)urb->hcpriv;
- struct uhci_td *td, *ptd;
-
- if (list_empty(&urbp->td_list))
- return;
-
- head = &urbp->td_list;
- tmp = head->next;
+ struct uhci_td *td;
+ __le32 *plink;
/* Ordering isn't important here yet since the QH hasn't been */
- /* inserted into the schedule yet */
- td = list_entry(tmp, struct uhci_td, list);
-
- /* Add the first TD to the QH element pointer */
- qh->element = cpu_to_le32(td->dma_handle) | breadth;
-
- ptd = td;
-
- /* Then link the rest of the TD's */
- tmp = tmp->next;
- while (tmp != head) {
- td = list_entry(tmp, struct uhci_td, list);
-
- tmp = tmp->next;
-
- ptd->link = cpu_to_le32(td->dma_handle) | breadth;
-
- ptd = td;
+ /* inserted into the schedule yet */
+ plink = &qh->element;
+ list_for_each_entry(td, &urbp->td_list, list) {
+ *plink = cpu_to_le32(td->dma_handle) | breadth;
+ plink = &td->link;
}
-
- ptd->link = UHCI_PTR_TERM;
+ *plink = UHCI_PTR_TERM;
}
static void uhci_free_td(struct uhci_hcd *uhci, struct uhci_td *td)
static void uhci_insert_qh(struct uhci_hcd *uhci, struct uhci_qh *skelqh, struct urb *urb)
{
struct urb_priv *urbp = (struct urb_priv *)urb->hcpriv;
- struct list_head *tmp;
+ struct urb_priv *turbp;
struct uhci_qh *lqh;
/* Grab the last QH */
lqh = list_entry(skelqh->list.prev, struct uhci_qh, list);
- /*
- * Patch this endpoint's URB's QHs to point to the next skelqh:
- * skelqh --> ... lqh --> newqh --> next skelqh
- * Do this first, so the HC always sees the right QH after this one.
- */
- list_for_each (tmp, &urbp->queue_list) {
- struct urb_priv *turbp =
- list_entry(tmp, struct urb_priv, queue_list);
-
- turbp->qh->link = lqh->link;
- }
+ /* Point to the next skelqh */
urbp->qh->link = lqh->link;
wmb(); /* Ordering is important */
*
* The HC could see (and use!) any of these as we write them.
*/
+ lqh->link = cpu_to_le32(urbp->qh->dma_handle) | UHCI_PTR_QH;
if (lqh->urbp) {
- list_for_each (tmp, &lqh->urbp->queue_list) {
- struct urb_priv *turbp =
- list_entry(tmp, struct urb_priv, queue_list);
-
- turbp->qh->link = cpu_to_le32(urbp->qh->dma_handle) | UHCI_PTR_QH;
- }
+ list_for_each_entry(turbp, &lqh->urbp->queue_list, queue_list)
+ turbp->qh->link = lqh->link;
}
- lqh->link = cpu_to_le32(urbp->qh->dma_handle) | UHCI_PTR_QH;
list_add_tail(&urbp->qh->list, &skelqh->list);
}
static void uhci_remove_qh(struct uhci_hcd *uhci, struct uhci_qh *qh)
{
struct uhci_qh *pqh;
- __u32 newlink;
+ __le32 newlink;
+ unsigned int age;
if (!qh)
return;
pqh = list_entry(qh->list.prev, struct uhci_qh, list);
pqh->link = newlink;
if (pqh->urbp) {
- struct list_head *head, *tmp;
-
- head = &pqh->urbp->queue_list;
- tmp = head->next;
- while (head != tmp) {
- struct urb_priv *turbp =
- list_entry(tmp, struct urb_priv, queue_list);
-
- tmp = tmp->next;
+ struct urb_priv *turbp;
+ list_for_each_entry(turbp, &pqh->urbp->queue_list,
+ queue_list)
turbp->qh->link = newlink;
- }
}
- mb();
+ wmb();
/* Leave qh->link in case the HC is on the QH now, it will */
/* continue the rest of the schedule */
list_del_init(&qh->urbp->queue_list);
qh->urbp = NULL;
+ age = uhci_get_current_frame_number(uhci);
+ if (age != uhci->qh_remove_age) {
+ uhci_free_pending_qhs(uhci);
+ uhci->qh_remove_age = age;
+ }
+
/* 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))
static int uhci_fixup_toggle(struct urb *urb, unsigned int toggle)
{
struct urb_priv *urbp = (struct urb_priv *)urb->hcpriv;
- struct list_head *head, *tmp;
-
- head = &urbp->td_list;
- tmp = head->next;
- while (head != tmp) {
- struct uhci_td *td = list_entry(tmp, struct uhci_td, list);
-
- tmp = tmp->next;
+ struct uhci_td *td;
+ list_for_each_entry(td, &urbp->td_list, list) {
if (toggle)
td->token |= cpu_to_le32(TD_TOKEN_TOGGLE);
else
td->token &= ~cpu_to_le32(TD_TOKEN_TOGGLE);
-
toggle ^= 1;
}
static void uhci_append_queued_urb(struct uhci_hcd *uhci, struct urb *eurb, struct urb *urb)
{
struct urb_priv *eurbp, *urbp, *furbp, *lurbp;
- struct list_head *tmp;
struct uhci_td *lltd;
eurbp = eurb->hcpriv;
urbp = urb->hcpriv;
/* Find the first URB in the queue */
+ furbp = eurbp;
if (eurbp->queued) {
- struct list_head *head = &eurbp->queue_list;
-
- tmp = head->next;
- while (tmp != head) {
- struct urb_priv *turbp =
- list_entry(tmp, struct urb_priv, queue_list);
-
- if (!turbp->queued)
+ list_for_each_entry(furbp, &eurbp->queue_list, queue_list)
+ if (!furbp->queued)
break;
+ }
- tmp = tmp->next;
- }
- } else
- tmp = &eurbp->queue_list;
-
- furbp = list_entry(tmp, struct urb_priv, queue_list);
lurbp = list_entry(furbp->queue_list.prev, struct urb_priv, queue_list);
lltd = list_entry(lurbp->td_list.prev, struct uhci_td, list);
/* All qh's in the queue need to link to the next queue */
urbp->qh->link = eurbp->qh->link;
- mb(); /* Make sure we flush everything */
+ wmb(); /* Make sure we flush everything */
lltd->link = cpu_to_le32(urbp->qh->dma_handle) | UHCI_PTR_QH;
static void uhci_delete_queued_urb(struct uhci_hcd *uhci, struct urb *urb)
{
- struct urb_priv *urbp, *nurbp;
- struct list_head *head, *tmp;
- struct urb_priv *purbp;
+ struct urb_priv *urbp, *nurbp, *purbp, *turbp;
struct uhci_td *pltd;
unsigned int toggle;
toggle = uhci_toggle(td_token(pltd)) ^ 1;
}
- head = &urbp->queue_list;
- tmp = head->next;
- while (head != tmp) {
- struct urb_priv *turbp;
-
- turbp = list_entry(tmp, struct urb_priv, queue_list);
- tmp = tmp->next;
-
+ list_for_each_entry(turbp, &urbp->queue_list, queue_list) {
if (!turbp->queued)
break;
toggle = uhci_fixup_toggle(turbp->urb, toggle);
static void uhci_destroy_urb_priv(struct uhci_hcd *uhci, struct urb *urb)
{
- struct list_head *head, *tmp;
+ struct uhci_td *td, *tmp;
struct urb_priv *urbp;
+ unsigned int age;
urbp = (struct urb_priv *)urb->hcpriv;
if (!urbp)
dev_warn(uhci_dev(uhci), "urb %p still on uhci->urb_list "
"or uhci->remove_list!\n", urb);
+ age = uhci_get_current_frame_number(uhci);
+ if (age != uhci->td_remove_age) {
+ uhci_free_pending_tds(uhci);
+ uhci->td_remove_age = age;
+ }
+
/* Check to see if the remove list is empty. Set the IOC bit */
/* to force an interrupt so we can remove the TD's*/
if (list_empty(&uhci->td_remove_list))
uhci_set_next_interrupt(uhci);
- head = &urbp->td_list;
- tmp = head->next;
- while (tmp != head) {
- struct uhci_td *td = list_entry(tmp, struct uhci_td, list);
-
- tmp = tmp->next;
-
+ list_for_each_entry_safe(td, tmp, &urbp->td_list, list) {
uhci_remove_td_from_urb(td);
uhci_remove_td(uhci, td);
list_add(&td->remove_list, &uhci->td_remove_list);
/*
* Map status to standard result codes
*
- * <status> is (td->status & 0xF60000) [a.k.a. uhci_status_bits(td->status)]
- * Note: status does not include the TD_CTRL_NAK bit.
+ * <status> is (td_status(td) & 0xF60000), a.k.a.
+ * uhci_status_bits(td_status(td)).
+ * Note: <status> does not include the TD_CTRL_NAK bit.
* <dir_out> is True for output TDs and False for input TDs.
*/
static int uhci_map_status(int status, int dir_out)
uhci_insert_tds_in_qh(qh, urb, UHCI_PTR_BREADTH);
- /* Low-speed transfers get a different queue, and won't hog the bus */
- if (urb->dev->speed == USB_SPEED_LOW)
+ /* Low-speed transfers get a different queue, and won't hog the bus.
+ * Also, some devices enumerate better without FSBR; the easiest way
+ * to do that is to put URBs on the low-speed queue while the device
+ * is in the DEFAULT state. */
+ if (urb->dev->speed == USB_SPEED_LOW ||
+ urb->dev->state == USB_STATE_DEFAULT)
skelqh = uhci->skel_ls_control_qh;
else {
skelqh = uhci->skel_fs_control_qh;
urbp->short_control_packet = 1;
td = list_entry(urbp->td_list.prev, struct uhci_td, list);
- urbp->qh->element = td->dma_handle;
+ urbp->qh->element = cpu_to_le32(td->dma_handle);
return -EINPROGRESS;
}
/* The rest of the TD's (but the last) are data */
tmp = tmp->next;
while (tmp != head && tmp->next != head) {
- td = list_entry(tmp, struct uhci_td, list);
+ unsigned int ctrlstat;
+ td = list_entry(tmp, struct uhci_td, list);
tmp = tmp->next;
- status = uhci_status_bits(td_status(td));
+ ctrlstat = td_status(td);
+ status = uhci_status_bits(ctrlstat);
if (status & TD_CTRL_ACTIVE)
return -EINPROGRESS;
- urb->actual_length += uhci_actual_length(td_status(td));
+ urb->actual_length += uhci_actual_length(ctrlstat);
if (status)
goto td_error;
/* Check to see if we received a short packet */
- if (uhci_actual_length(td_status(td)) < uhci_expected_length(td_token(td))) {
+ if (uhci_actual_length(ctrlstat) <
+ uhci_expected_length(td_token(td))) {
if (urb->transfer_flags & URB_SHORT_NOT_OK) {
ret = -EREMOTEIO;
goto err;
usb_pipeout(urb->pipe));
}
- /* Set the flag on the last packet */
- if (!(urb->transfer_flags & URB_NO_INTERRUPT))
- td->status |= cpu_to_le32(TD_CTRL_IOC);
+ /* Set the interrupt-on-completion flag on the last packet.
+ * A more-or-less typical 4 KB URB (= size of one memory page)
+ * will require about 3 ms to transfer; that's a little on the
+ * fast side but not enough to justify delaying an interrupt
+ * more than 2 or 3 URBs, so we will ignore the URB_NO_INTERRUPT
+ * flag setting. */
+ td->status |= cpu_to_le32(TD_CTRL_IOC);
qh = uhci_alloc_qh(uhci, urb->dev);
if (!qh)
*/
static int uhci_result_common(struct uhci_hcd *uhci, struct urb *urb)
{
- struct list_head *tmp, *head;
struct urb_priv *urbp = urb->hcpriv;
struct uhci_td *td;
unsigned int status = 0;
urb->actual_length = 0;
- head = &urbp->td_list;
- tmp = head->next;
- while (tmp != head) {
- td = list_entry(tmp, struct uhci_td, list);
-
- tmp = tmp->next;
+ list_for_each_entry(td, &urbp->td_list, list) {
+ unsigned int ctrlstat = td_status(td);
- status = uhci_status_bits(td_status(td));
+ status = uhci_status_bits(ctrlstat);
if (status & TD_CTRL_ACTIVE)
return -EINPROGRESS;
- urb->actual_length += uhci_actual_length(td_status(td));
+ urb->actual_length += uhci_actual_length(ctrlstat);
if (status)
goto td_error;
- if (uhci_actual_length(td_status(td)) < uhci_expected_length(td_token(td))) {
+ if (uhci_actual_length(ctrlstat) <
+ uhci_expected_length(td_token(td))) {
if (urb->transfer_flags & URB_SHORT_NOT_OK) {
ret = -EREMOTEIO;
goto err;
td_error:
ret = uhci_map_status(status, uhci_packetout(td_token(td)));
- if (ret == -EPIPE)
- /* endpoint has stalled - mark it halted */
- usb_endpoint_halt(urb->dev, uhci_endpoint(td_token(td)),
- uhci_packetout(td_token(td)));
err:
/*
static int isochronous_find_limits(struct uhci_hcd *uhci, struct urb *urb, unsigned int *start, unsigned int *end)
{
struct urb *last_urb = NULL;
- struct list_head *tmp, *head;
+ struct urb_priv *up;
int ret = 0;
- head = &uhci->urb_list;
- tmp = head->next;
- while (tmp != head) {
- struct urb_priv *up = list_entry(tmp, struct urb_priv, urb_list);
+ list_for_each_entry(up, &uhci->urb_list, urb_list) {
struct urb *u = up->urb;
- tmp = tmp->next;
-
/* look for pending URB's with identical pipe handle */
if ((urb->pipe == u->pipe) && (urb->dev == u->dev) &&
(u->status == -EINPROGRESS) && (u != urb)) {
limits = isochronous_find_limits(uhci, urb, &start, &end);
if (urb->transfer_flags & URB_ISO_ASAP) {
- if (limits) {
- int curframe;
-
- curframe = uhci_get_current_frame_number(uhci) % UHCI_NUMFRAMES;
- urb->start_frame = (curframe + 10) % UHCI_NUMFRAMES;
- } else
+ if (limits)
+ urb->start_frame =
+ (uhci_get_current_frame_number(uhci) +
+ 10) & (UHCI_NUMFRAMES - 1);
+ else
urb->start_frame = end;
} else {
- urb->start_frame %= UHCI_NUMFRAMES;
+ urb->start_frame &= (UHCI_NUMFRAMES - 1);
/* FIXME: Sanity check */
}
static int uhci_result_isochronous(struct uhci_hcd *uhci, struct urb *urb)
{
- struct list_head *tmp, *head;
+ struct uhci_td *td;
struct urb_priv *urbp = (struct urb_priv *)urb->hcpriv;
int status;
int i, ret = 0;
urb->actual_length = 0;
i = 0;
- head = &urbp->td_list;
- tmp = head->next;
- while (tmp != head) {
- struct uhci_td *td = list_entry(tmp, struct uhci_td, list);
+ list_for_each_entry(td, &urbp->td_list, list) {
int actlength;
+ unsigned int ctrlstat = td_status(td);
- tmp = tmp->next;
-
- if (td_status(td) & TD_CTRL_ACTIVE)
+ if (ctrlstat & TD_CTRL_ACTIVE)
return -EINPROGRESS;
- actlength = uhci_actual_length(td_status(td));
+ actlength = uhci_actual_length(ctrlstat);
urb->iso_frame_desc[i].actual_length = actlength;
urb->actual_length += actlength;
- status = uhci_map_status(uhci_status_bits(td_status(td)),
+ status = uhci_map_status(uhci_status_bits(ctrlstat),
usb_pipeout(urb->pipe));
urb->iso_frame_desc[i].status = status;
if (status) {
static struct urb *uhci_find_urb_ep(struct uhci_hcd *uhci, struct urb *urb)
{
- struct list_head *tmp, *head;
+ struct urb_priv *up;
/* We don't match Isoc transfers since they are special */
if (usb_pipeisoc(urb->pipe))
return NULL;
- head = &uhci->urb_list;
- tmp = head->next;
- while (tmp != head) {
- struct urb_priv *up = list_entry(tmp, struct urb_priv, urb_list);
+ list_for_each_entry(up, &uhci->urb_list, urb_list) {
struct urb *u = up->urb;
- tmp = tmp->next;
-
if (u->dev == urb->dev && u->status == -EINPROGRESS) {
/* For control, ignore the direction */
if (usb_pipecontrol(urb->pipe) &&
return NULL;
}
-static int uhci_urb_enqueue(struct usb_hcd *hcd, struct urb *urb, int mem_flags)
+static int uhci_urb_enqueue(struct usb_hcd *hcd,
+ struct usb_host_endpoint *ep,
+ struct urb *urb, int mem_flags)
{
- int ret = -EINVAL;
+ int ret;
struct uhci_hcd *uhci = hcd_to_uhci(hcd);
unsigned long flags;
struct urb *eurb;
spin_lock_irqsave(&uhci->schedule_lock, flags);
- if (urb->status != -EINPROGRESS) /* URB already unlinked! */
+ ret = urb->status;
+ if (ret != -EINPROGRESS) /* URB already unlinked! */
goto out;
eurb = uhci_find_urb_ep(uhci, urb);
static void uhci_unlink_generic(struct uhci_hcd *uhci, struct urb *urb)
{
- struct list_head *head, *tmp;
+ struct list_head *head;
+ struct uhci_td *td;
struct urb_priv *urbp = (struct urb_priv *)urb->hcpriv;
- int prevactive = 1;
+ int prevactive = 0;
uhci_dec_fsbr(uhci, urb); /* Safe since it checks */
* Now we need to find out what the last successful toggle was
* so we can update the local data toggle for the next transfer
*
- * There's 3 way's the last successful completed TD is found:
+ * There are 2 ways the last successful completed TD is found:
*
* 1) The TD is NOT active and the actual length < expected length
* 2) The TD is NOT active and it's the last TD in the chain
+ *
+ * and a third way the first uncompleted TD is found:
+ *
* 3) The TD is active and the previous TD is NOT active
*
* Control and Isochronous ignore the toggle, so this is safe
* for all types
+ *
+ * FIXME: The toggle fixups won't be 100% reliable until we
+ * change over to using a single queue for each endpoint and
+ * stop the queue before unlinking.
*/
head = &urbp->td_list;
- tmp = head->next;
- while (tmp != head) {
- struct uhci_td *td = list_entry(tmp, struct uhci_td, list);
+ list_for_each_entry(td, head, list) {
+ unsigned int ctrlstat = td_status(td);
- tmp = tmp->next;
-
- if (!(td_status(td) & TD_CTRL_ACTIVE) &&
- (uhci_actual_length(td_status(td)) < uhci_expected_length(td_token(td)) ||
- tmp == head))
+ if (!(ctrlstat & TD_CTRL_ACTIVE) &&
+ (uhci_actual_length(ctrlstat) <
+ uhci_expected_length(td_token(td)) ||
+ td->list.next == head))
usb_settoggle(urb->dev, uhci_endpoint(td_token(td)),
uhci_packetout(td_token(td)),
uhci_toggle(td_token(td)) ^ 1);
- else if ((td_status(td) & TD_CTRL_ACTIVE) && !prevactive)
+ else if ((ctrlstat & TD_CTRL_ACTIVE) && !prevactive)
usb_settoggle(urb->dev, uhci_endpoint(td_token(td)),
uhci_packetout(td_token(td)),
uhci_toggle(td_token(td)));
- prevactive = td_status(td) & TD_CTRL_ACTIVE;
+ prevactive = ctrlstat & TD_CTRL_ACTIVE;
}
uhci_delete_queued_urb(uhci, urb);
struct uhci_hcd *uhci = hcd_to_uhci(hcd);
unsigned long flags;
struct urb_priv *urbp;
+ unsigned int age;
spin_lock_irqsave(&uhci->schedule_lock, flags);
urbp = urb->hcpriv;
uhci_unlink_generic(uhci, urb);
+ age = uhci_get_current_frame_number(uhci);
+ if (age != uhci->urb_remove_age) {
+ uhci_remove_pending_urbps(uhci);
+ uhci->urb_remove_age = age;
+ }
+
/* If we're the first, set the next interrupt bit */
if (list_empty(&uhci->urb_remove_list))
uhci_set_next_interrupt(uhci);
static int uhci_fsbr_timeout(struct uhci_hcd *uhci, struct urb *urb)
{
struct urb_priv *urbp = (struct urb_priv *)urb->hcpriv;
- struct list_head *head, *tmp;
+ struct list_head *head;
+ struct uhci_td *td;
int count = 0;
uhci_dec_fsbr(uhci, urb);
*/
head = &urbp->td_list;
- tmp = head->next;
- while (tmp != head) {
- struct uhci_td *td = list_entry(tmp, struct uhci_td, list);
-
- tmp = tmp->next;
-
+ list_for_each_entry(td, head, list) {
/*
* Make sure we don't do the last one (since it'll have the
* TERM bit set) as well as we skip every so many TD's to
* make sure it doesn't hog the bandwidth
*/
- if (tmp != head && (count % DEPTH_INTERVAL) == (DEPTH_INTERVAL - 1))
+ if (td->list.next != head && (count % DEPTH_INTERVAL) ==
+ (DEPTH_INTERVAL - 1))
td->link |= UHCI_PTR_DEPTH;
count++;
*
* returns the current frame number for a USB bus/controller.
*/
-static int uhci_get_current_frame_number(struct uhci_hcd *uhci)
+static unsigned int uhci_get_current_frame_number(struct uhci_hcd *uhci)
{
return inw(uhci->io_addr + USBFRNUM);
}
{
struct usb_hcd *hcd = (struct usb_hcd *)ptr;
struct uhci_hcd *uhci = hcd_to_uhci(hcd);
- struct list_head list, *tmp, *head;
+ struct urb_priv *up;
unsigned long flags;
-
- INIT_LIST_HEAD(&list);
+ int called_uhci_finish_completion = 0;
spin_lock_irqsave(&uhci->schedule_lock, flags);
- head = &uhci->urb_list;
- tmp = head->next;
- while (tmp != head) {
- struct urb_priv *up = list_entry(tmp, struct urb_priv, urb_list);
- struct urb *u = up->urb;
+ if (!list_empty(&uhci->urb_remove_list) &&
+ uhci_get_current_frame_number(uhci) != uhci->urb_remove_age) {
+ uhci_remove_pending_urbps(uhci);
+ uhci_finish_completion(hcd, NULL);
+ called_uhci_finish_completion = 1;
+ }
- tmp = tmp->next;
+ list_for_each_entry(up, &uhci->urb_list, urb_list) {
+ struct urb *u = up->urb;
spin_lock(&u->lock);
if (up->fsbr && !up->fsbr_timeout && time_after_eq(jiffies, up->fsbrtime + IDLE_TIMEOUT))
uhci_fsbr_timeout(uhci, u);
- /* Check if the URB timed out */
- if (u->timeout && u->status == -EINPROGRESS &&
- time_after_eq(jiffies, up->inserttime + u->timeout)) {
- u->status = -ETIMEDOUT;
- list_move_tail(&up->urb_list, &list);
- }
-
spin_unlock(&u->lock);
}
spin_unlock_irqrestore(&uhci->schedule_lock, flags);
- head = &list;
- tmp = head->next;
- while (tmp != head) {
- struct urb_priv *up = list_entry(tmp, struct urb_priv, urb_list);
- struct urb *u = up->urb;
-
- tmp = tmp->next;
-
- uhci_urb_dequeue(hcd, u);
- }
+ /* Wake up anyone waiting for an URB to complete */
+ if (called_uhci_finish_completion)
+ wake_up_all(&uhci->waitqh);
/* Really disable FSBR */
if (!uhci->fsbr && uhci->fsbrtimeout && time_after_eq(jiffies, uhci->fsbrtimeout)) {
/* Poll for and perform state transitions */
hc_state_transitions(uhci);
+ if (unlikely(uhci->suspended_ports && uhci->state != UHCI_SUSPENDED))
+ uhci_check_resume(uhci);
init_stall_timer(hcd);
}
init_timer(&uhci->stall_timer);
uhci->stall_timer.function = stall_callback;
uhci->stall_timer.data = (unsigned long)hcd;
- uhci->stall_timer.expires = jiffies + (HZ / 10);
+ uhci->stall_timer.expires = jiffies + msecs_to_jiffies(100);
add_timer(&uhci->stall_timer);
return 0;
static void uhci_free_pending_qhs(struct uhci_hcd *uhci)
{
- struct list_head *tmp, *head;
-
- head = &uhci->qh_remove_list;
- tmp = head->next;
- while (tmp != head) {
- struct uhci_qh *qh = list_entry(tmp, struct uhci_qh, remove_list);
-
- tmp = tmp->next;
+ struct uhci_qh *qh, *tmp;
+ list_for_each_entry_safe(qh, tmp, &uhci->qh_remove_list, remove_list) {
list_del_init(&qh->remove_list);
uhci_free_qh(uhci, qh);
static void uhci_free_pending_tds(struct uhci_hcd *uhci)
{
- struct list_head *tmp, *head;
-
- head = &uhci->td_remove_list;
- tmp = head->next;
- while (tmp != head) {
- struct uhci_td *td = list_entry(tmp, struct uhci_td, remove_list);
-
- tmp = tmp->next;
+ struct uhci_td *td, *tmp;
+ list_for_each_entry_safe(td, tmp, &uhci->td_remove_list, remove_list) {
list_del_init(&td->remove_list);
uhci_free_td(uhci, td);
}
}
-static void uhci_finish_urb(struct usb_hcd *hcd, struct urb *urb, struct pt_regs *regs)
+static void
+uhci_finish_urb(struct usb_hcd *hcd, struct urb *urb, struct pt_regs *regs)
+__releases(uhci->schedule_lock)
+__acquires(uhci->schedule_lock)
{
struct uhci_hcd *uhci = hcd_to_uhci(hcd);
static void uhci_finish_completion(struct usb_hcd *hcd, struct pt_regs *regs)
{
struct uhci_hcd *uhci = hcd_to_uhci(hcd);
- struct list_head *tmp, *head;
+ struct urb_priv *urbp, *tmp;
- head = &uhci->complete_list;
- tmp = head->next;
- while (tmp != head) {
- struct urb_priv *urbp = list_entry(tmp, struct urb_priv, urb_list);
+ list_for_each_entry_safe(urbp, tmp, &uhci->complete_list, urb_list) {
struct urb *urb = urbp->urb;
list_del_init(&urbp->urb_list);
uhci_finish_urb(hcd, urb, regs);
-
- head = &uhci->complete_list;
- tmp = head->next;
}
}
static irqreturn_t uhci_irq(struct usb_hcd *hcd, struct pt_regs *regs)
{
struct uhci_hcd *uhci = hcd_to_uhci(hcd);
- unsigned int io_addr = uhci->io_addr;
+ unsigned long io_addr = uhci->io_addr;
unsigned short status;
- struct list_head *tmp, *head;
+ struct urb_priv *urbp, *tmp;
+ unsigned int age;
/*
* Read the interrupt status, and write it back to clear the
spin_lock(&uhci->schedule_lock);
- uhci_free_pending_qhs(uhci);
- uhci_free_pending_tds(uhci);
- uhci_remove_pending_urbps(uhci);
-
- uhci_clear_next_interrupt(uhci);
+ age = uhci_get_current_frame_number(uhci);
+ if (age != uhci->qh_remove_age)
+ uhci_free_pending_qhs(uhci);
+ if (age != uhci->td_remove_age)
+ uhci_free_pending_tds(uhci);
+ if (age != uhci->urb_remove_age)
+ uhci_remove_pending_urbps(uhci);
+
+ if (list_empty(&uhci->urb_remove_list) &&
+ list_empty(&uhci->td_remove_list) &&
+ list_empty(&uhci->qh_remove_list))
+ uhci_clear_next_interrupt(uhci);
+ else
+ uhci_set_next_interrupt(uhci);
- /* Walk the list of pending URB's to see which ones completed */
- head = &uhci->urb_list;
- tmp = head->next;
- while (tmp != head) {
- struct urb_priv *urbp = list_entry(tmp, struct urb_priv, urb_list);
+ /* Walk the list of pending URBs to see which ones completed
+ * (must be _safe because uhci_transfer_result() dequeues URBs) */
+ list_for_each_entry_safe(urbp, tmp, &uhci->urb_list, urb_list) {
struct urb *urb = urbp->urb;
- tmp = tmp->next;
-
/* Checks the status and does all of the magic necessary */
uhci_transfer_result(uhci, urb);
}
spin_unlock(&uhci->schedule_lock);
+ /* Wake up anyone waiting for an URB to complete */
+ wake_up_all(&uhci->waitqh);
+
return IRQ_HANDLED;
}
static void reset_hc(struct uhci_hcd *uhci)
{
- unsigned int io_addr = uhci->io_addr;
+ unsigned long io_addr = uhci->io_addr;
+
+ /* Turn off PIRQ, SMI, and all interrupts. This also turns off
+ * the BIOS's USB Legacy Support.
+ */
+ pci_write_config_word(to_pci_dev(uhci_dev(uhci)), USBLEGSUP, 0);
+ outw(0, uhci->io_addr + USBINTR);
/* Global reset for 50ms */
uhci->state = UHCI_RESET;
outw(USBCMD_GRESET, io_addr + USBCMD);
- set_current_state(TASK_UNINTERRUPTIBLE);
- schedule_timeout((HZ*50+999) / 1000);
+ msleep(50);
outw(0, io_addr + USBCMD);
/* Another 10ms delay */
- set_current_state(TASK_UNINTERRUPTIBLE);
- schedule_timeout((HZ*10+999) / 1000);
+ msleep(10);
uhci->resume_detect = 0;
}
static void suspend_hc(struct uhci_hcd *uhci)
{
- unsigned int io_addr = uhci->io_addr;
+ unsigned long io_addr = uhci->io_addr;
dev_dbg(uhci_dev(uhci), "%s\n", __FUNCTION__);
uhci->state = UHCI_SUSPENDED;
static void wakeup_hc(struct uhci_hcd *uhci)
{
- unsigned int io_addr = uhci->io_addr;
+ unsigned long io_addr = uhci->io_addr;
switch (uhci->state) {
case UHCI_SUSPENDED: /* Start the resume */
/* Global resume for >= 20ms */
outw(USBCMD_FGR | USBCMD_EGSM, io_addr + USBCMD);
uhci->state = UHCI_RESUMING_1;
- uhci->state_end = jiffies + (20*HZ+999) / 1000;
+ uhci->state_end = jiffies + msecs_to_jiffies(20);
break;
case UHCI_RESUMING_1: /* End global resume */
static int ports_active(struct uhci_hcd *uhci)
{
- unsigned int io_addr = uhci->io_addr;
+ unsigned long io_addr = uhci->io_addr;
int connection = 0;
int i;
static int suspend_allowed(struct uhci_hcd *uhci)
{
- unsigned int io_addr = uhci->io_addr;
+ unsigned long io_addr = uhci->io_addr;
int i;
if (to_pci_dev(uhci_dev(uhci))->vendor != PCI_VENDOR_ID_INTEL)
}
}
-static void start_hc(struct uhci_hcd *uhci)
+static int start_hc(struct uhci_hcd *uhci)
{
- unsigned int io_addr = uhci->io_addr;
- int timeout = 1000;
+ unsigned long io_addr = uhci->io_addr;
+ int timeout = 10;
/*
* Reset the HC - this will force us to get a
*/
outw(USBCMD_HCRESET, io_addr + USBCMD);
while (inw(io_addr + USBCMD) & USBCMD_HCRESET) {
- if (!--timeout) {
+ if (--timeout < 0) {
dev_err(uhci_dev(uhci), "USBCMD_HCRESET timed out!\n");
- break;
+ return -ETIMEDOUT;
}
+ msleep(1);
}
- /* Turn on all interrupts */
+ /* Turn on PIRQ and all interrupts */
+ pci_write_config_word(to_pci_dev(uhci_dev(uhci)), USBLEGSUP,
+ USBLEGSUP_DEFAULT);
outw(USBINTR_TIMEOUT | USBINTR_RESUME | USBINTR_IOC | USBINTR_SP,
io_addr + USBINTR);
uhci->state_end = jiffies + HZ;
outw(USBCMD_RS | USBCMD_CF | USBCMD_MAXP, io_addr + USBCMD);
- uhci->hcd.state = USB_STATE_RUNNING;
+ uhci_to_hcd(uhci)->state = USB_STATE_RUNNING;
+ return 0;
}
/*
uhci->fl = NULL;
}
-#ifdef CONFIG_PROC_FS
- if (uhci->proc_entry) {
- remove_proc_entry(uhci->hcd.self.bus_name, uhci_proc_root);
- uhci->proc_entry = NULL;
+ if (uhci->dentry) {
+ debugfs_remove(uhci->dentry);
+ uhci->dentry = NULL;
}
-#endif
}
static int uhci_reset(struct usb_hcd *hcd)
uhci->io_addr = (unsigned long) hcd->regs;
- /* Turn off all interrupts */
- outw(0, uhci->io_addr + USBINTR);
-
- /* Maybe kick BIOS off this hardware. Then reset, so we won't get
+ /* Kick BIOS off this hardware and reset, so we won't get
* interrupts from any previous setup.
*/
reset_hc(uhci);
- pci_write_config_word(to_pci_dev(uhci_dev(uhci)), USBLEGSUP,
- USBLEGSUP_DEFAULT);
return 0;
}
unsigned io_size;
dma_addr_t dma_handle;
struct usb_device *udev;
-#ifdef CONFIG_PROC_FS
- struct proc_dir_entry *ent;
-#endif
+ struct dentry *dentry;
io_size = pci_resource_len(to_pci_dev(uhci_dev(uhci)), hcd->region);
-#ifdef CONFIG_PROC_FS
- ent = create_proc_entry(hcd->self.bus_name, S_IFREG|S_IRUGO|S_IWUSR, uhci_proc_root);
- if (!ent) {
- dev_err(uhci_dev(uhci), "couldn't create uhci proc entry\n");
+ dentry = debugfs_create_file(hcd->self.bus_name, S_IFREG|S_IRUGO|S_IWUSR, uhci_debugfs_root, uhci, &uhci_debug_operations);
+ if (!dentry) {
+ dev_err(uhci_dev(uhci), "couldn't create uhci debugfs entry\n");
retval = -ENOMEM;
- goto err_create_proc_entry;
+ goto err_create_debug_entry;
}
-
- ent->data = uhci;
- ent->proc_fops = &uhci_proc_operations;
- ent->size = 0;
- uhci->proc_entry = ent;
-#endif
+ uhci->dentry = dentry;
uhci->fsbr = 0;
uhci->fsbrtimeout = 0;
INIT_LIST_HEAD(&uhci->complete_list);
+ init_waitqueue_head(&uhci->waitqh);
+
uhci->fl = dma_alloc_coherent(uhci_dev(uhci), sizeof(*uhci->fl),
&dma_handle, 0);
if (!uhci->fl) {
uhci->rh_numports = port;
- hcd->self.root_hub = udev = usb_alloc_dev(NULL, &hcd->self, 0);
+ udev = usb_alloc_dev(NULL, &hcd->self, 0);
if (!udev) {
dev_err(uhci_dev(uhci), "unable to allocate root hub\n");
goto err_alloc_root_hub;
irq = 7;
/* Only place we don't use the frame list routines */
- uhci->fl->frame[i] = cpu_to_le32(uhci->skelqh[irq]->dma_handle);
+ uhci->fl->frame[i] = UHCI_PTR_QH |
+ cpu_to_le32(uhci->skelqh[irq]->dma_handle);
}
- start_hc(uhci);
+ /*
+ * Some architectures require a full mb() to enforce completion of
+ * the memory writes above before the I/O transfers in start_hc().
+ */
+ mb();
+ if ((retval = start_hc(uhci)) != 0)
+ goto err_alloc_skelqh;
init_stall_timer(hcd);
udev->speed = USB_SPEED_FULL;
- if (usb_register_root_hub(udev, uhci_dev(uhci)) != 0) {
+ if (hcd_register_root(udev, hcd) != 0) {
dev_err(uhci_dev(uhci), "unable to start root hub\n");
retval = -ENOMEM;
goto err_start_root_hub;
err_alloc_term_td:
usb_put_dev(udev);
- hcd->self.root_hub = NULL;
err_alloc_root_hub:
dma_pool_destroy(uhci->qh_pool);
uhci->fl = NULL;
err_alloc_fl:
-#ifdef CONFIG_PROC_FS
- remove_proc_entry(hcd->self.bus_name, uhci_proc_root);
- uhci->proc_entry = NULL;
-
-err_create_proc_entry:
-#endif
+ debugfs_remove(uhci->dentry);
+ uhci->dentry = NULL;
+err_create_debug_entry:
return retval;
}
uhci_free_pending_qhs(uhci);
uhci_free_pending_tds(uhci);
spin_unlock_irq(&uhci->schedule_lock);
+
+ /* Wake up anyone waiting for an URB to complete */
+ wake_up_all(&uhci->waitqh);
release_uhci(uhci);
}
static int uhci_resume(struct usb_hcd *hcd)
{
struct uhci_hcd *uhci = hcd_to_uhci(hcd);
+ int rc;
pci_set_master(to_pci_dev(uhci_dev(uhci)));
/*
* Some systems don't maintain the UHCI register values
* during a PM suspend/resume cycle, so reinitialize
- * the Frame Number, the Framelist Base Address, and the
- * Interrupt Enable registers.
+ * the Frame Number, Framelist Base Address, Interrupt
+ * Enable, and Legacy Support registers.
*/
+ pci_write_config_word(to_pci_dev(uhci_dev(uhci)), USBLEGSUP,
+ 0);
outw(uhci->saved_framenumber, uhci->io_addr + USBFRNUM);
outl(uhci->fl->dma_handle, uhci->io_addr + USBFLBASEADD);
outw(USBINTR_TIMEOUT | USBINTR_RESUME | USBINTR_IOC |
USBINTR_SP, uhci->io_addr + USBINTR);
uhci->resume_detect = 1;
+ pci_write_config_word(to_pci_dev(uhci_dev(uhci)), USBLEGSUP,
+ USBLEGSUP_DEFAULT);
} else {
reset_hc(uhci);
- start_hc(uhci);
+ if ((rc = start_hc(uhci)) != 0)
+ return rc;
}
- uhci->hcd.state = USB_STATE_RUNNING;
+ hcd->state = USB_STATE_RUNNING;
return 0;
}
#endif
-static struct usb_hcd *uhci_hcd_alloc(void)
+/* Wait until all the URBs for a particular device/endpoint are gone */
+static void uhci_hcd_endpoint_disable(struct usb_hcd *hcd,
+ struct usb_host_endpoint *ep)
{
- struct uhci_hcd *uhci;
-
- uhci = (struct uhci_hcd *)kmalloc(sizeof(*uhci), GFP_KERNEL);
- if (!uhci)
- return NULL;
-
- memset(uhci, 0, sizeof(*uhci));
- uhci->hcd.product_desc = "UHCI Host Controller";
- return &uhci->hcd;
-}
+ struct uhci_hcd *uhci = hcd_to_uhci(hcd);
-static void uhci_hcd_free(struct usb_hcd *hcd)
-{
- kfree(hcd_to_uhci(hcd));
+ wait_event_interruptible(uhci->waitqh, list_empty(&ep->urb_list));
}
static int uhci_hcd_get_frame_number(struct usb_hcd *hcd)
static const struct hc_driver uhci_driver = {
.description = hcd_name,
+ .product_desc = "UHCI Host Controller",
+ .hcd_priv_size = sizeof(struct uhci_hcd),
/* Generic hardware linkage */
.irq = uhci_irq,
#endif
.stop = uhci_stop,
- .hcd_alloc = uhci_hcd_alloc,
- .hcd_free = uhci_hcd_free,
-
.urb_enqueue = uhci_urb_enqueue,
.urb_dequeue = uhci_urb_dequeue,
+ .endpoint_disable = uhci_hcd_endpoint_disable,
.get_frame_number = uhci_hcd_get_frame_number,
.hub_status_data = uhci_hub_status_data,
goto errbuf_failed;
}
-#ifdef CONFIG_PROC_FS
- uhci_proc_root = create_proc_entry("driver/uhci", S_IFDIR, 0);
- if (!uhci_proc_root)
- goto proc_failed;
-#endif
+ uhci_debugfs_root = debugfs_create_dir("uhci", NULL);
+ if (!uhci_debugfs_root)
+ goto debug_failed;
uhci_up_cachep = kmem_cache_create("uhci_urb_priv",
sizeof(struct urb_priv), 0, 0, NULL, NULL);
if (!uhci_up_cachep)
goto up_failed;
- retval = pci_module_init(&uhci_pci_driver);
+ retval = pci_register_driver(&uhci_pci_driver);
if (retval)
goto init_failed;
warn("not all urb_priv's were freed!");
up_failed:
+ debugfs_remove(uhci_debugfs_root);
-#ifdef CONFIG_PROC_FS
- remove_proc_entry("driver/uhci", 0);
-
-proc_failed:
-#endif
+debug_failed:
if (errbuf)
kfree(errbuf);
if (kmem_cache_destroy(uhci_up_cachep))
warn("not all urb_priv's were freed!");
-#ifdef CONFIG_PROC_FS
- remove_proc_entry("driver/uhci", 0);
-#endif
+ debugfs_remove(uhci_debugfs_root);
if (errbuf)
kfree(errbuf);