*/
static inline void uhci_set_next_interrupt(struct uhci_hcd *uhci)
{
- unsigned long flags;
-
- spin_lock_irqsave(&uhci->frame_list_lock, flags);
uhci->term_td->status |= cpu_to_le32(TD_CTRL_IOC);
- spin_unlock_irqrestore(&uhci->frame_list_lock, flags);
}
static inline void uhci_clear_next_interrupt(struct uhci_hcd *uhci)
{
- spin_lock(&uhci->frame_list_lock);
uhci->term_td->status &= ~cpu_to_le32(TD_CTRL_IOC);
- spin_unlock(&uhci->frame_list_lock);
}
static inline void uhci_moveto_complete(struct uhci_hcd *uhci,
struct urb_priv *urbp)
{
- spin_lock(&uhci->complete_list_lock);
list_move_tail(&urbp->urb_list, &uhci->complete_list);
- spin_unlock(&uhci->complete_list_lock);
}
static struct uhci_td *uhci_alloc_td(struct uhci_hcd *uhci, struct usb_device *dev)
*/
static void uhci_insert_td_frame_list(struct uhci_hcd *uhci, struct uhci_td *td, unsigned framenum)
{
- unsigned long flags;
-
framenum %= UHCI_NUMFRAMES;
- spin_lock_irqsave(&uhci->frame_list_lock, flags);
-
td->frame = framenum;
/* Is there a TD already mapped there? */
uhci->fl->frame[framenum] = cpu_to_le32(td->dma_handle);
uhci->fl->frame_cpu[framenum] = td;
}
-
- spin_unlock_irqrestore(&uhci->frame_list_lock, flags);
}
static void uhci_remove_td(struct uhci_hcd *uhci, struct uhci_td *td)
{
- unsigned long flags;
-
/* If it's not inserted, don't remove it */
- spin_lock_irqsave(&uhci->frame_list_lock, flags);
if (td->frame == -1 && list_empty(&td->fl_list))
- goto out;
+ return;
if (td->frame != -1 && uhci->fl->frame_cpu[td->frame] == td) {
if (list_empty(&td->fl_list)) {
list_del_init(&td->fl_list);
td->frame = -1;
-
-out:
- spin_unlock_irqrestore(&uhci->frame_list_lock, flags);
}
/*
/*
* Append this urb's qh after the last qh in skelqh->list
- * MUST be called with uhci->frame_list_lock acquired
*
* Note that urb_priv.queue_list doesn't have a separate queue head;
* it's a ring with every element "live".
*/
-static void _uhci_insert_qh(struct uhci_hcd *uhci, struct uhci_qh *skelqh, struct urb *urb)
+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;
list_add_tail(&urbp->qh->list, &skelqh->list);
}
-static void uhci_insert_qh(struct uhci_hcd *uhci, struct uhci_qh *skelqh, struct urb *urb)
-{
- unsigned long flags;
-
- spin_lock_irqsave(&uhci->frame_list_lock, flags);
- _uhci_insert_qh(uhci, skelqh, urb);
- spin_unlock_irqrestore(&uhci->frame_list_lock, flags);
-}
-
/*
* Start removal of QH from schedule; it finishes next frame.
* TDs should be unlinked before this is called.
*/
static void uhci_remove_qh(struct uhci_hcd *uhci, struct uhci_qh *qh)
{
- unsigned long flags;
struct uhci_qh *pqh;
if (!qh)
/*
* Only go through the hoops if it's actually linked in
*/
- spin_lock_irqsave(&uhci->frame_list_lock, flags);
if (!list_empty(&qh->list)) {
pqh = list_entry(qh->list.prev, struct uhci_qh, list);
}
list_del_init(&qh->list);
}
- spin_unlock_irqrestore(&uhci->frame_list_lock, flags);
qh->urbp = NULL;
- spin_lock_irqsave(&uhci->qh_remove_list_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);
list_add(&qh->remove_list, &uhci->qh_remove_list);
-
- spin_unlock_irqrestore(&uhci->qh_remove_list_lock, flags);
}
static int uhci_fixup_toggle(struct urb *urb, unsigned int toggle)
struct urb_priv *eurbp, *urbp, *furbp, *lurbp;
struct list_head *tmp;
struct uhci_td *lltd;
- unsigned long flags;
eurbp = eurb->hcpriv;
urbp = urb->hcpriv;
- spin_lock_irqsave(&uhci->frame_list_lock, flags);
-
/* Find the first URB in the queue */
if (eurbp->queued) {
struct list_head *head = &eurbp->queue_list;
list_add_tail(&urbp->queue_list, &furbp->queue_list);
urbp->queued = 1;
-
- spin_unlock_irqrestore(&uhci->frame_list_lock, flags);
}
static void uhci_delete_queued_urb(struct uhci_hcd *uhci, struct urb *urb)
struct urb_priv *purbp;
struct uhci_td *pltd;
unsigned int toggle;
- unsigned long flags;
urbp = urb->hcpriv;
- spin_lock_irqsave(&uhci->frame_list_lock, flags);
-
if (list_empty(&urbp->queue_list))
- goto out;
+ return;
nurbp = list_entry(urbp->queue_list.next, struct urb_priv, queue_list);
}
list_del_init(&urbp->queue_list);
-
-out:
- spin_unlock_irqrestore(&uhci->frame_list_lock, flags);
}
static struct urb_priv *uhci_alloc_urb_priv(struct uhci_hcd *uhci, struct urb *urb)
return urbp;
}
-/*
- * MUST be called with urb->lock acquired
- */
static void uhci_add_td_to_urb(struct urb *urb, struct uhci_td *td)
{
struct urb_priv *urbp = (struct urb_priv *)urb->hcpriv;
list_add_tail(&td->list, &urbp->td_list);
}
-/*
- * MUST be called with urb->lock acquired
- */
static void uhci_remove_td_from_urb(struct uhci_td *td)
{
if (list_empty(&td->list))
td->urb = NULL;
}
-/*
- * MUST be called with urb->lock acquired
- */
static void uhci_destroy_urb_priv(struct uhci_hcd *uhci, struct urb *urb)
{
struct list_head *head, *tmp;
struct urb_priv *urbp;
- unsigned long flags;
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);
- spin_lock_irqsave(&uhci->td_remove_list_lock, flags);
-
/* 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))
list_add(&td->remove_list, &uhci->td_remove_list);
}
- spin_unlock_irqrestore(&uhci->td_remove_list_lock, flags);
-
urb->hcpriv = NULL;
kmem_cache_free(uhci_up_cachep, urbp);
}
static void uhci_inc_fsbr(struct uhci_hcd *uhci, struct urb *urb)
{
- unsigned long flags;
struct urb_priv *urbp = (struct urb_priv *)urb->hcpriv;
- spin_lock_irqsave(&uhci->frame_list_lock, flags);
-
if ((!(urb->transfer_flags & URB_NO_FSBR)) && !urbp->fsbr) {
urbp->fsbr = 1;
if (!uhci->fsbr++ && !uhci->fsbrtimeout)
uhci->skel_term_qh->link = cpu_to_le32(uhci->skel_hs_control_qh->dma_handle) | UHCI_PTR_QH;
}
-
- spin_unlock_irqrestore(&uhci->frame_list_lock, flags);
}
static void uhci_dec_fsbr(struct uhci_hcd *uhci, struct urb *urb)
{
- unsigned long flags;
struct urb_priv *urbp = (struct urb_priv *)urb->hcpriv;
- spin_lock_irqsave(&uhci->frame_list_lock, flags);
-
if ((!(urb->transfer_flags & URB_NO_FSBR)) && urbp->fsbr) {
urbp->fsbr = 0;
if (!--uhci->fsbr)
uhci->fsbrtimeout = jiffies + FSBR_DELAY;
}
-
- spin_unlock_irqrestore(&uhci->frame_list_lock, flags);
}
/*
return ret;
}
-/*
- * MUST be called with uhci->urb_list_lock acquired
- */
static struct urb *uhci_find_urb_ep(struct uhci_hcd *uhci, struct urb *urb)
{
struct list_head *tmp, *head;
struct urb *eurb;
int bustime;
- spin_lock_irqsave(&uhci->urb_list_lock, flags);
+ spin_lock_irqsave(&uhci->schedule_lock, flags);
if (urb->status != -EINPROGRESS) /* URB already unlinked! */
goto out;
ret = 0;
out:
- spin_unlock_irqrestore(&uhci->urb_list_lock, flags);
+ spin_unlock_irqrestore(&uhci->schedule_lock, flags);
return ret;
}
/*
* Return the result of a transfer
- *
- * MUST be called with urb_list_lock acquired
*/
static void uhci_transfer_result(struct uhci_hcd *uhci, struct urb *urb)
{
case PIPE_BULK:
case PIPE_ISOCHRONOUS:
/* Release bandwidth for Interrupt or Isoc. transfers */
- /* Spinlock needed ? */
if (urb->bandwidth)
usb_release_bandwidth(urb->dev, urb, 1);
uhci_unlink_generic(uhci, urb);
case PIPE_INTERRUPT:
/* Release bandwidth for Interrupt or Isoc. transfers */
/* Make sure we don't release if we have a queued URB */
- spin_lock(&uhci->frame_list_lock);
- /* Spinlock needed ? */
if (list_empty(&urbp->queue_list) && urb->bandwidth)
usb_release_bandwidth(urb->dev, urb, 0);
else
/* bandwidth was passed on to queued URB, */
/* so don't let usb_unlink_urb() release it */
urb->bandwidth = 0;
- spin_unlock(&uhci->frame_list_lock);
uhci_unlink_generic(uhci, urb);
break;
default:
spin_unlock(&urb->lock);
}
-/*
- * MUST be called with urb->lock acquired
- */
static void uhci_unlink_generic(struct uhci_hcd *uhci, struct urb *urb)
{
struct list_head *head, *tmp;
unsigned long flags;
struct urb_priv *urbp;
- spin_lock_irqsave(&uhci->urb_list_lock, flags);
+ spin_lock_irqsave(&uhci->schedule_lock, flags);
urbp = urb->hcpriv;
if (!urbp) /* URB was never linked! */
goto done;
uhci_unlink_generic(uhci, urb);
- spin_lock(&uhci->urb_remove_list_lock);
-
/* If we're the first, set the next interrupt bit */
if (list_empty(&uhci->urb_remove_list))
uhci_set_next_interrupt(uhci);
list_add_tail(&urbp->urb_list, &uhci->urb_remove_list);
- spin_unlock(&uhci->urb_remove_list_lock);
done:
- spin_unlock_irqrestore(&uhci->urb_list_lock, flags);
+ spin_unlock_irqrestore(&uhci->schedule_lock, flags);
return 0;
}
INIT_LIST_HEAD(&list);
- spin_lock_irqsave(&uhci->urb_list_lock, flags);
+ spin_lock_irqsave(&uhci->schedule_lock, flags);
head = &uhci->urb_list;
tmp = head->next;
while (tmp != head) {
uhci_fsbr_timeout(uhci, u);
/* Check if the URB timed out */
- if (u->timeout && time_after_eq(jiffies, up->inserttime + u->timeout))
+ 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->urb_list_lock, flags);
+ spin_unlock_irqrestore(&uhci->schedule_lock, flags);
head = &list;
tmp = head->next;
{
struct list_head *tmp, *head;
- spin_lock(&uhci->qh_remove_list_lock);
head = &uhci->qh_remove_list;
tmp = head->next;
while (tmp != head) {
uhci_free_qh(uhci, qh);
}
- spin_unlock(&uhci->qh_remove_list_lock);
}
static void uhci_free_pending_tds(struct uhci_hcd *uhci)
{
struct list_head *tmp, *head;
- spin_lock(&uhci->td_remove_list_lock);
head = &uhci->td_remove_list;
tmp = head->next;
while (tmp != head) {
uhci_free_td(uhci, td);
}
- spin_unlock(&uhci->td_remove_list_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);
- spin_lock(&urb->lock);
uhci_destroy_urb_priv(uhci, urb);
- spin_unlock(&urb->lock);
+ spin_unlock(&uhci->schedule_lock);
usb_hcd_giveback_urb(hcd, urb, regs);
+ spin_lock(&uhci->schedule_lock);
}
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;
- spin_lock(&uhci->complete_list_lock);
head = &uhci->complete_list;
tmp = head->next;
while (tmp != head) {
struct urb *urb = urbp->urb;
list_del_init(&urbp->urb_list);
- spin_unlock(&uhci->complete_list_lock);
-
uhci_finish_urb(hcd, urb, regs);
- spin_lock(&uhci->complete_list_lock);
head = &uhci->complete_list;
tmp = head->next;
}
- spin_unlock(&uhci->complete_list_lock);
}
static void uhci_remove_pending_urbps(struct uhci_hcd *uhci)
{
- spin_lock(&uhci->urb_remove_list_lock);
- spin_lock(&uhci->complete_list_lock);
/* Splice the urb_remove_list onto the end of the complete_list */
list_splice_init(&uhci->urb_remove_list, uhci->complete_list.prev);
- spin_unlock(&uhci->complete_list_lock);
- spin_unlock(&uhci->urb_remove_list_lock);
}
static irqreturn_t uhci_irq(struct usb_hcd *hcd, struct pt_regs *regs)
if (status & USBSTS_RD)
uhci->resume_detect = 1;
- uhci_free_pending_qhs(uhci);
+ 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);
/* Walk the list of pending URB's to see which ones completed */
- spin_lock(&uhci->urb_list_lock);
head = &uhci->urb_list;
tmp = head->next;
while (tmp != head) {
/* Checks the status and does all of the magic necessary */
uhci_transfer_result(uhci, urb);
}
- spin_unlock(&uhci->urb_list_lock);
-
uhci_finish_completion(hcd, regs);
+
+ spin_unlock(&uhci->schedule_lock);
+
return IRQ_HANDLED;
}
uhci->fsbr = 0;
uhci->fsbrtimeout = 0;
- spin_lock_init(&uhci->qh_remove_list_lock);
+ spin_lock_init(&uhci->schedule_lock);
INIT_LIST_HEAD(&uhci->qh_remove_list);
- spin_lock_init(&uhci->td_remove_list_lock);
INIT_LIST_HEAD(&uhci->td_remove_list);
- spin_lock_init(&uhci->urb_remove_list_lock);
INIT_LIST_HEAD(&uhci->urb_remove_list);
- spin_lock_init(&uhci->urb_list_lock);
INIT_LIST_HEAD(&uhci->urb_list);
- spin_lock_init(&uhci->complete_list_lock);
INIT_LIST_HEAD(&uhci->complete_list);
- spin_lock_init(&uhci->frame_list_lock);
-
uhci->fl = dma_alloc_coherent(uhci_dev(uhci), sizeof(*uhci->fl),
&dma_handle, 0);
if (!uhci->fl) {
static void uhci_stop(struct usb_hcd *hcd)
{
struct uhci_hcd *uhci = hcd_to_uhci(hcd);
- unsigned long flags;
del_timer_sync(&uhci->stall_timer);
* At this point, we're guaranteed that no new connects can be made
* to this bus since there are no more parents
*/
- local_irq_save(flags);
+ spin_lock_irq(&uhci->schedule_lock);
uhci_free_pending_qhs(uhci);
uhci_free_pending_tds(uhci);
uhci_remove_pending_urbps(uhci);
+ spin_unlock_irq(&uhci->schedule_lock);
reset_hc(uhci);
+ spin_lock_irq(&uhci->schedule_lock);
uhci_free_pending_qhs(uhci);
uhci_free_pending_tds(uhci);
- local_irq_restore(flags);
+ spin_unlock_irq(&uhci->schedule_lock);
release_uhci(uhci);
}