#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>
static kmem_cache_t *uhci_up_cachep; /* urb_priv */
-static unsigned int uhci_get_current_frame_number(struct uhci_hcd *uhci);
+static 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_insert_td_frame_list(struct uhci_hcd *uhci, struct uhci_td *td, unsigned framenum)
{
- framenum &= (UHCI_NUMFRAMES - 1);
+ framenum %= UHCI_NUMFRAMES;
td->frame = framenum;
}
/*
- * Inserts a td list into qh.
+ * Inserts a td into qh list at the top.
*/
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;
- u32 *plink;
+ struct uhci_td *td, *ptd;
+
+ if (list_empty(&urbp->td_list))
+ return;
+
+ head = &urbp->td_list;
+ tmp = head->next;
/* Ordering isn't important here yet since the QH hasn't been */
- /* 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;
+ /* 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;
}
- *plink = UHCI_PTR_TERM;
+
+ ptd->link = 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 urb_priv *turbp;
+ struct list_head *tmp;
struct uhci_qh *lqh;
/* Grab the last QH */
*/
lqh->link = cpu_to_le32(urbp->qh->dma_handle) | UHCI_PTR_QH;
if (lqh->urbp) {
- list_for_each_entry(turbp, &lqh->urbp->queue_list, queue_list)
+ list_for_each (tmp, &lqh->urbp->queue_list) {
+ struct urb_priv *turbp =
+ list_entry(tmp, struct urb_priv, queue_list);
+
turbp->qh->link = lqh->link;
+ }
}
list_add_tail(&urbp->qh->list, &skelqh->list);
pqh = list_entry(qh->list.prev, struct uhci_qh, list);
pqh->link = newlink;
if (pqh->urbp) {
- struct urb_priv *turbp;
+ 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;
- list_for_each_entry(turbp, &pqh->urbp->queue_list,
- queue_list)
turbp->qh->link = newlink;
+ }
}
wmb();
static int uhci_fixup_toggle(struct urb *urb, unsigned int toggle)
{
struct urb_priv *urbp = (struct urb_priv *)urb->hcpriv;
- struct uhci_td *td;
+ 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;
- 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) {
- list_for_each_entry(furbp, &eurbp->queue_list, queue_list)
- if (!furbp->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)
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);
static void uhci_delete_queued_urb(struct uhci_hcd *uhci, struct urb *urb)
{
- struct urb_priv *urbp, *nurbp, *purbp, *turbp;
+ struct urb_priv *urbp, *nurbp;
+ struct list_head *head, *tmp;
+ struct urb_priv *purbp;
struct uhci_td *pltd;
unsigned int toggle;
toggle = uhci_toggle(td_token(pltd)) ^ 1;
}
- list_for_each_entry(turbp, &urbp->queue_list, queue_list) {
+ 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;
+
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 uhci_td *td, *tmp;
+ struct list_head *head, *tmp;
struct urb_priv *urbp;
unsigned int age;
if (list_empty(&uhci->td_remove_list))
uhci_set_next_interrupt(uhci);
- list_for_each_entry_safe(td, tmp, &urbp->td_list, list) {
+ head = &urbp->td_list;
+ tmp = head->next;
+ while (tmp != head) {
+ struct uhci_td *td = list_entry(tmp, struct uhci_td, list);
+
+ tmp = tmp->next;
+
uhci_remove_td_from_urb(td);
uhci_remove_td(uhci, td);
list_add(&td->remove_list, &uhci->td_remove_list);
uhci_insert_tds_in_qh(qh, urb, UHCI_PTR_BREADTH);
- /* 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)
+ /* Low-speed transfers get a different queue, and won't hog the bus */
+ if (urb->dev->speed == USB_SPEED_LOW)
skelqh = uhci->skel_ls_control_qh;
else {
skelqh = uhci->skel_fs_control_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;
- list_for_each_entry(td, &urbp->td_list, list) {
+ head = &urbp->td_list;
+ tmp = head->next;
+ while (tmp != head) {
+ td = list_entry(tmp, struct uhci_td, list);
+
+ tmp = tmp->next;
+
status = uhci_status_bits(td_status(td));
if (status & TD_CTRL_ACTIVE)
return -EINPROGRESS;
static int isochronous_find_limits(struct uhci_hcd *uhci, struct urb *urb, unsigned int *start, unsigned int *end)
{
struct urb *last_urb = NULL;
- struct urb_priv *up;
+ struct list_head *tmp, *head;
int ret = 0;
- list_for_each_entry(up, &uhci->urb_list, urb_list) {
+ 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;
+ 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)
- urb->start_frame =
- (uhci_get_current_frame_number(uhci) +
- 10) & (UHCI_NUMFRAMES - 1);
- else
+ if (limits) {
+ int curframe;
+
+ curframe = uhci_get_current_frame_number(uhci) % UHCI_NUMFRAMES;
+ urb->start_frame = (curframe + 10) % UHCI_NUMFRAMES;
+ } else
urb->start_frame = end;
} else {
- urb->start_frame &= (UHCI_NUMFRAMES - 1);
+ urb->start_frame %= UHCI_NUMFRAMES;
/* FIXME: Sanity check */
}
static int uhci_result_isochronous(struct uhci_hcd *uhci, struct urb *urb)
{
- struct uhci_td *td;
+ struct list_head *tmp, *head;
struct urb_priv *urbp = (struct urb_priv *)urb->hcpriv;
int status;
int i, ret = 0;
urb->actual_length = 0;
i = 0;
- list_for_each_entry(td, &urbp->td_list, list) {
+ head = &urbp->td_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) & TD_CTRL_ACTIVE)
return -EINPROGRESS;
static struct urb *uhci_find_urb_ep(struct uhci_hcd *uhci, struct urb *urb)
{
- struct urb_priv *up;
+ struct list_head *tmp, *head;
/* We don't match Isoc transfers since they are special */
if (usb_pipeisoc(urb->pipe))
return NULL;
- list_for_each_entry(up, &uhci->urb_list, urb_list) {
+ 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;
+ tmp = tmp->next;
+
if (u->dev == urb->dev && u->status == -EINPROGRESS) {
/* For control, ignore the direction */
if (usb_pipecontrol(urb->pipe) &&
static void uhci_unlink_generic(struct uhci_hcd *uhci, struct urb *urb)
{
- struct list_head *head;
- struct uhci_td *td;
+ struct list_head *head, *tmp;
struct urb_priv *urbp = (struct urb_priv *)urb->hcpriv;
- int prevactive = 0;
+ int prevactive = 1;
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 are 2 ways the last successful completed TD is found:
+ * There's 3 way's 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;
- list_for_each_entry(td, head, list) {
+ tmp = head->next;
+ while (tmp != head) {
+ struct uhci_td *td = list_entry(tmp, struct uhci_td, list);
+
+ tmp = tmp->next;
+
if (!(td_status(td) & TD_CTRL_ACTIVE) &&
- (uhci_actual_length(td_status(td)) <
- uhci_expected_length(td_token(td)) ||
- td->list.next == head))
+ (uhci_actual_length(td_status(td)) < uhci_expected_length(td_token(td)) ||
+ tmp == head))
usb_settoggle(urb->dev, uhci_endpoint(td_token(td)),
uhci_packetout(td_token(td)),
uhci_toggle(td_token(td)) ^ 1);
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;
- struct uhci_td *td;
+ struct list_head *head, *tmp;
int count = 0;
uhci_dec_fsbr(uhci, urb);
*/
head = &urbp->td_list;
- list_for_each_entry(td, head, list) {
+ tmp = head->next;
+ while (tmp != head) {
+ struct uhci_td *td = list_entry(tmp, struct uhci_td, list);
+
+ tmp = tmp->next;
+
/*
* 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 (td->list.next != head && (count % DEPTH_INTERVAL) ==
- (DEPTH_INTERVAL - 1))
+ if (tmp != head && (count % DEPTH_INTERVAL) == (DEPTH_INTERVAL - 1))
td->link |= UHCI_PTR_DEPTH;
count++;
*
* returns the current frame number for a USB bus/controller.
*/
-static unsigned int uhci_get_current_frame_number(struct uhci_hcd *uhci)
+static 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 urb_priv *up;
+ struct list_head list, *tmp, *head;
unsigned long flags;
int called_uhci_finish_completion = 0;
+ INIT_LIST_HEAD(&list);
+
spin_lock_irqsave(&uhci->schedule_lock, flags);
if (!list_empty(&uhci->urb_remove_list) &&
uhci_get_current_frame_number(uhci) != uhci->urb_remove_age) {
called_uhci_finish_completion = 1;
}
- list_for_each_entry(up, &uhci->urb_list, urb_list) {
+ 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;
+ tmp = tmp->next;
+
spin_lock(&u->lock);
/* Check if the FSBR timed out */
if (called_uhci_finish_completion)
wake_up_all(&uhci->waitqh);
+ 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);
+ }
+
/* Really disable FSBR */
if (!uhci->fsbr && uhci->fsbrtimeout && time_after_eq(jiffies, uhci->fsbrtimeout)) {
uhci->fsbrtimeout = 0;
/* 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);
}
static void uhci_free_pending_qhs(struct uhci_hcd *uhci)
{
- struct uhci_qh *qh, *tmp;
+ 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;
- 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 uhci_td *td, *tmp;
+ 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;
- 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)
-__releases(uhci->schedule_lock)
-__acquires(uhci->schedule_lock)
+static void uhci_finish_urb(struct usb_hcd *hcd, struct urb *urb, struct pt_regs *regs)
{
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 urb_priv *urbp, *tmp;
+ struct list_head *tmp, *head;
- list_for_each_entry_safe(urbp, tmp, &uhci->complete_list, urb_list) {
+ head = &uhci->complete_list;
+ tmp = head->next;
+ while (tmp != head) {
+ struct urb_priv *urbp = list_entry(tmp, struct urb_priv, 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;
}
}
struct uhci_hcd *uhci = hcd_to_uhci(hcd);
unsigned long io_addr = uhci->io_addr;
unsigned short status;
- struct urb_priv *urbp, *tmp;
+ struct list_head *tmp, *head;
unsigned int age;
/*
else
uhci_set_next_interrupt(uhci);
- /* 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) {
+ /* 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);
struct urb *urb = urbp->urb;
+ tmp = tmp->next;
+
/* Checks the status and does all of the magic necessary */
uhci_transfer_result(uhci, urb);
}
}
}
-static int start_hc(struct uhci_hcd *uhci)
+static void start_hc(struct uhci_hcd *uhci)
{
unsigned long io_addr = uhci->io_addr;
- int timeout = 10;
+ int timeout = 1000;
/*
* 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 < 0) {
+ if (!--timeout) {
dev_err(uhci_dev(uhci), "USBCMD_HCRESET timed out!\n");
- return -ETIMEDOUT;
+ break;
}
- msleep(1);
}
/* Turn on PIRQ and all interrupts */
outw(USBCMD_RS | USBCMD_CF | USBCMD_MAXP, io_addr + USBCMD);
uhci->hcd.state = USB_STATE_RUNNING;
- return 0;
}
/*
* the memory writes above before the I/O transfers in start_hc().
*/
mb();
- if ((retval = start_hc(uhci)) != 0)
- goto err_alloc_skelqh;
+ start_hc(uhci);
init_stall_timer(hcd);
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)));
USBLEGSUP_DEFAULT);
} else {
reset_hc(uhci);
- if ((rc = start_hc(uhci)) != 0)
- return rc;
+ start_hc(uhci);
}
uhci->hcd.state = USB_STATE_RUNNING;
return 0;
return &uhci->hcd;
}
+static void uhci_hcd_free(struct usb_hcd *hcd)
+{
+ kfree(hcd_to_uhci(hcd));
+}
+
/* Are there any URBs for a particular device/endpoint on a given list? */
static int urbs_for_ep_list(struct list_head *head,
struct hcd_dev *hdev, int ep)
.stop = uhci_stop,
.hcd_alloc = uhci_hcd_alloc,
+ .hcd_free = uhci_hcd_free,
.urb_enqueue = uhci_urb_enqueue,
.urb_dequeue = uhci_urb_dequeue,
if (!uhci_up_cachep)
goto up_failed;
- retval = pci_register_driver(&uhci_pci_driver);
+ retval = pci_module_init(&uhci_pci_driver);
if (retval)
goto init_failed;