Here are some changes for UHCI power management. Martin, maybe you can try them out and see if they help with getting your USB mouse to work after a resume. Duncan, included in the patch but commented out is some code to handle the VIA problem you mentioned.
I haven't tested this very much, beyond making sure that it works okay in normal operation. In particular, I haven't tried doing a suspend/resume. So don't be too surprised if it doesn't work quite right. There's actually two patches below. The first is an accumulation of all the work I've done up till now that has been sent to various people but not yet applied. It should apply cleanly to Greg's current usb-2.5 tree (including Duncan's recent TD-free patch). The second patch has the PM changes; it goes on top of the first one. Alan Stern First patch-------------------------- ===== uhci-debug.c 1.9 vs edited ===== --- 1.9/drivers/usb/host/uhci-debug.c Wed Jun 25 13:02:56 2003 +++ edited/drivers/usb/host/uhci-debug.c Wed Sep 3 11:42:41 2003 @@ -320,8 +320,8 @@ out += sprintf(out, "%s", (urbp->fsbr ? "FSBR " : "")); out += sprintf(out, "%s", (urbp->fsbr_timeout ? "FSBR_TO " : "")); - if (urbp->status != -EINPROGRESS) - out += sprintf(out, "Status=%d ", urbp->status); + if (urbp->urb->status != -EINPROGRESS) + out += sprintf(out, "Status=%d ", urbp->urb->status); //out += sprintf(out, "Inserttime=%lx ",urbp->inserttime); //out += sprintf(out, "FSBRtime=%lx ",urbp->fsbrtime); ===== uhci-hcd.c 1.64 vs edited ===== --- 1.64/drivers/usb/host/uhci-hcd.c Fri Jul 18 09:22:32 2003 +++ edited/drivers/usb/host/uhci-hcd.c Wed Sep 3 11:44:53 2003 @@ -41,6 +41,7 @@ #include <linux/interrupt.h> #include <linux/spinlock.h> #include <linux/proc_fs.h> +#include <linux/wait.h> #ifdef CONFIG_USB_DEBUG #define DEBUG #else @@ -525,8 +526,12 @@ lltd = list_entry(lurbp->td_list.prev, struct uhci_td, list); - usb_settoggle(urb->dev, usb_pipeendpoint(urb->pipe), usb_pipeout(urb->pipe), - uhci_fixup_toggle(urb, uhci_toggle(td_token(lltd)) ^ 1)); + /* Control transfers always start with toggle 0 */ + if (!usb_pipecontrol(urb->pipe)) + usb_settoggle(urb->dev, usb_pipeendpoint(urb->pipe), + usb_pipeout(urb->pipe), + uhci_fixup_toggle(urb, + uhci_toggle(td_token(lltd)) ^ 1)); /* All qh's in the queue need to link to the next queue */ urbp->qh->link = eurbp->qh->link; @@ -560,39 +565,44 @@ nurbp = list_entry(urbp->queue_list.next, struct urb_priv, queue_list); - /* Fix up the toggle for the next URB's */ - if (!urbp->queued) - /* We just set the toggle in uhci_unlink_generic */ - toggle = usb_gettoggle(urb->dev, usb_pipeendpoint(urb->pipe), usb_pipeout(urb->pipe)); - else { - /* If we're in the middle of the queue, grab the toggle */ - /* from the TD previous to us */ - purbp = list_entry(urbp->queue_list.prev, struct urb_priv, - queue_list); - - pltd = list_entry(purbp->td_list.prev, struct uhci_td, list); - - toggle = uhci_toggle(td_token(pltd)) ^ 1; - } - - head = &urbp->queue_list; - tmp = head->next; - while (head != tmp) { - struct urb_priv *turbp; + /* + * Fix up the toggle for the following URBs in the queue. + * Only needed for bulk and interrupt: control and isochronous + * endpoints don't propagate toggles between messages. + */ + if (usb_pipebulk(urb->pipe) || usb_pipeint(urb->pipe)) { + if (!urbp->queued) + /* We just set the toggle in uhci_unlink_generic */ + toggle = usb_gettoggle(urb->dev, + usb_pipeendpoint(urb->pipe), + usb_pipeout(urb->pipe)); + else { + /* If we're in the middle of the queue, grab the */ + /* toggle from the TD previous to us */ + purbp = list_entry(urbp->queue_list.prev, + struct urb_priv, queue_list); + pltd = list_entry(purbp->td_list.prev, + struct uhci_td, list); + toggle = uhci_toggle(td_token(pltd)) ^ 1; + } - turbp = list_entry(tmp, struct urb_priv, queue_list); + head = &urbp->queue_list; + tmp = head->next; + while (head != tmp) { + struct urb_priv *turbp; - tmp = tmp->next; + turbp = list_entry(tmp, struct urb_priv, queue_list); + tmp = tmp->next; - if (!turbp->queued) - break; + if (!turbp->queued) + break; + toggle = uhci_fixup_toggle(turbp->urb, toggle); + } - toggle = uhci_fixup_toggle(turbp->urb, toggle); + usb_settoggle(urb->dev, usb_pipeendpoint(urb->pipe), + usb_pipeout(urb->pipe), toggle); } - usb_settoggle(urb->dev, usb_pipeendpoint(urb->pipe), - usb_pipeout(urb->pipe), toggle); - if (!urbp->queued) { struct uhci_qh *pqh; @@ -792,16 +802,13 @@ else return -EILSEQ; } - if (status & TD_CTRL_NAK) /* NAK */ - return -ETIMEDOUT; if (status & TD_CTRL_BABBLE) /* Babble */ return -EOVERFLOW; if (status & TD_CTRL_DBUFERR) /* Buffer error */ return -ENOSR; if (status & TD_CTRL_STALLED) /* Stalled */ return -EPIPE; - if (status & TD_CTRL_ACTIVE) /* Active */ - return 0; + BUG_ON (status & TD_CTRL_ACTIVE); /* Active */ return -EINVAL; } @@ -819,6 +826,9 @@ int len = urb->transfer_buffer_length; dma_addr_t data = urb->transfer_dma; + if (maxsze <= 0) + return -EMSGSIZE; + /* The "pipe" thing contains the destination in bits 8--18 */ destination = (urb->pipe & PIPE_DEVEP_MASK) | USB_PID_SETUP; @@ -1051,10 +1061,11 @@ return 0; #endif + status = uhci_status_bits(status); if (status & TD_CTRL_ACTIVE) return -EINPROGRESS; - if (uhci_status_bits(status)) + if (status) goto td_error; return 0; @@ -1093,6 +1104,8 @@ if (len < 0) return -EINVAL; + if (maxsze <= 0) + return -EMSGSIZE; /* The "pipe" thing contains the destination in bits 8--18 */ destination = (urb->pipe & PIPE_DEVEP_MASK) | usb_packetid(urb->pipe); @@ -1547,11 +1560,11 @@ break; } - urbp->status = ret; - if (ret == -EINPROGRESS) goto out; + urb->status = ret; + switch (usb_pipetype(urb->pipe)) { case PIPE_CONTROL: case PIPE_BULK: @@ -1654,10 +1667,8 @@ /* If this is an interrupt URB that is being killed in urb->complete, */ /* then just set its status and return */ - if (!urbp) { - urb->status = -ECONNRESET; - return 0; - } + if (!urbp) + return 0; spin_lock_irqsave(&uhci->urb_list_lock, flags); @@ -1670,7 +1681,7 @@ /* If we're the first, set the next interrupt bit */ if (list_empty(&uhci->urb_remove_list)) uhci_set_next_interrupt(uhci); - list_add(&urbp->urb_list, &uhci->urb_remove_list); + list_add_tail(&urbp->urb_list, &uhci->urb_remove_list); spin_unlock(&uhci->urb_remove_list_lock); spin_unlock_irqrestore(&uhci->urb_list_lock, flags); @@ -1836,17 +1847,11 @@ static void uhci_finish_urb(struct usb_hcd *hcd, struct urb *urb, struct pt_regs *regs) { - struct urb_priv *urbp = (struct urb_priv *)urb->hcpriv; struct uhci_hcd *uhci = hcd_to_uhci(hcd); - int status; unsigned long flags; spin_lock_irqsave(&urb->lock, flags); - status = urbp->status; uhci_destroy_urb_priv(uhci, urb); - - if (urb->status != -ENOENT && urb->status != -ECONNRESET) - urb->status = status; spin_unlock_irqrestore(&urb->lock, flags); usb_hcd_giveback_urb(hcd, urb, regs); @@ -1877,7 +1882,7 @@ spin_unlock_irqrestore(&uhci->complete_list_lock, flags); } -static void uhci_remove_pending_qhs(struct uhci_hcd *uhci) +static void uhci_remove_pending_urbps(struct uhci_hcd *uhci) { struct list_head *tmp, *head; unsigned long flags; @@ -1892,9 +1897,6 @@ tmp = tmp->next; list_del_init(&urbp->urb_list); - - urbp->status = urb->status = -ECONNRESET; - uhci_add_complete(uhci, urb); } spin_unlock_irqrestore(&uhci->urb_remove_list_lock, flags); @@ -1934,7 +1936,7 @@ uhci_free_pending_tds(uhci); - uhci_remove_pending_qhs(uhci); + uhci_remove_pending_urbps(uhci); uhci_clear_next_interrupt(uhci); @@ -1954,6 +1956,32 @@ spin_unlock(&uhci->urb_list_lock); uhci_finish_completion(hcd, regs); + + /* Wake up anyone waiting for a SOF irq */ + wake_up_all(&uhci->ep_waitqh); +} + +static int urbs_for_ep(struct uhci_hcd *uhci, struct hcd_dev *dev, + int endpoint) +{ + int count = 0; + struct urb *urb; + struct urb *tmp; + + list_for_each_entry_safe(urb, tmp, &dev->urb_list, urb_list) { + if (endpoint == (usb_pipeendpoint(urb->pipe) | + usb_pipein(urb->pipe))) + ++count; + } + return count; +} + +/* Wait until all the URBs for a particular device/endpoint are gone */ +static void uhci_wait_for_endpoint(struct uhci_hcd *uhci, struct hcd_dev *dev, + int endpoint) +{ + wait_event_interruptible(uhci->ep_waitqh, + urbs_for_ep(uhci, dev, endpoint) == 0); } static void reset_hc(struct uhci_hcd *uhci) @@ -2256,6 +2284,8 @@ spin_lock_init(&uhci->frame_list_lock); + init_waitqueue_head(&uhci->ep_waitqh); + uhci->fl = pci_alloc_consistent(hcd->pdev, sizeof(*uhci->fl), &dma_handle); if (!uhci->fl) { err("unable to allocate consistent memory for frame list"); @@ -2457,7 +2487,7 @@ */ uhci_free_pending_qhs(uhci); uhci_free_pending_tds(uhci); - uhci_remove_pending_qhs(uhci); + uhci_remove_pending_urbps(uhci); reset_hc(uhci); @@ -2515,6 +2545,12 @@ kfree(hcd_to_uhci(hcd)); } +static void uhci_hcd_endpoint_disable(struct usb_hcd *hcd, + struct hcd_dev *dev, int endpoint) +{ + uhci_wait_for_endpoint(hcd_to_uhci(hcd), dev, endpoint); +} + static int uhci_hcd_get_frame_number(struct usb_hcd *hcd) { return uhci_get_current_frame_number(hcd_to_uhci(hcd)); @@ -2544,6 +2580,7 @@ .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, ===== uhci-hcd.h 1.14 vs edited ===== --- 1.14/drivers/usb/host/uhci-hcd.h Fri Jul 18 09:22:32 2003 +++ edited/drivers/usb/host/uhci-hcd.h Wed Sep 3 11:44:53 2003 @@ -134,7 +134,7 @@ TD_CTRL_BABBLE | TD_CTRL_CRCTIME | TD_CTRL_BITSTUFF) #define uhci_maxerr(err) ((err) << TD_CTRL_C_ERR_SHIFT) -#define uhci_status_bits(ctrl_sts) ((ctrl_sts) & 0xFE0000) +#define uhci_status_bits(ctrl_sts) ((ctrl_sts) & 0xF60000) #define uhci_actual_length(ctrl_sts) (((ctrl_sts) + 1) & TD_CTRL_ACTLEN_MASK) /* 1-based */ /* @@ -342,6 +342,7 @@ enum uhci_state state; /* FIXME: needs a spinlock */ unsigned long state_end; /* Time of next transition */ int resume_detect; /* Need a Global Resume */ + unsigned long c_p_r; /* Changed-Port-Reset bits */ /* Main list of URB's currently controlled by this HC */ spinlock_t urb_list_lock; @@ -366,6 +367,8 @@ int rh_numports; struct timer_list stall_timer; + + wait_queue_head_t ep_waitqh; }; struct urb_priv { @@ -383,8 +386,6 @@ int short_control_packet : 1; /* If we get a short packet during */ /* a control transfer, retrigger */ /* the status phase */ - - int status; /* Final status */ unsigned long inserttime; /* In jiffies */ unsigned long fsbrtime; /* In jiffies */ ===== uhci-hub.c 1.3 vs edited ===== --- 1.3/drivers/usb/host/uhci-hub.c Sun Jun 9 03:03:14 2002 +++ edited/drivers/usb/host/uhci-hub.c Wed Sep 3 11:44:26 2003 @@ -32,7 +32,9 @@ *buf = 0; for (i = 0; i < uhci->rh_numports; i++) { - *buf |= ((inw(io_addr + USBPORTSC1 + i * 2) & 0xa) > 0 ? (1 << (i + 1)) : 0); + *buf |= ((inw(io_addr + USBPORTSC1 + i * 2) & 0xa) > 0 || + test_bit(i, &uhci->c_p_r) + ? (1 << (i + 1)) : 0); len = (i + 1) / 8 + 1; } @@ -42,14 +44,14 @@ #define OK(x) len = (x); break #define CLR_RH_PORTSTAT(x) \ - status = inw(io_addr + USBPORTSC1 + 2 * (wIndex-1)); \ + status = inw(io_addr + USBPORTSC1 + 2 * wIndex); \ status = (status & 0xfff5) & ~(x); \ - outw(status, io_addr + USBPORTSC1 + 2 * (wIndex-1)) + outw(status, io_addr + USBPORTSC1 + 2 * wIndex) #define SET_RH_PORTSTAT(x) \ - status = inw(io_addr + USBPORTSC1 + 2 * (wIndex-1)); \ + status = inw(io_addr + USBPORTSC1 + 2 * wIndex); \ status = (status & 0xfff5) | (x); \ - outw(status, io_addr + USBPORTSC1 + 2 * (wIndex-1)) + outw(status, io_addr + USBPORTSC1 + 2 * wIndex) /* size of returned buffer is part of USB spec */ @@ -57,13 +59,12 @@ u16 wIndex, char *buf, u16 wLength) { struct uhci_hcd *uhci = hcd_to_uhci(hcd); - int i, status, retval = 0, len = 0; + int status, retval = 0, len = 0; unsigned int io_addr = uhci->io_addr; __u16 cstatus; - char c_p_r[8]; - for (i = 0; i < 8; i++) - c_p_r[i] = 0; + /* Convert wIndex to origin 0; if it was 0, it will wrap around */ + --wIndex; switch (typeReq) { /* Request Destination: @@ -78,11 +79,14 @@ *(__u32 *)buf = cpu_to_le32(0); OK(4); /* hub power */ case GetPortStatus: - status = inw(io_addr + USBPORTSC1 + 2 * (wIndex - 1)); + if (wIndex >= uhci->rh_numports) + goto err; + + status = inw(io_addr + USBPORTSC1 + 2 * wIndex); cstatus = ((status & USBPORTSC_CSC) >> (1 - 0)) | ((status & USBPORTSC_PEC) >> (3 - 1)) | - (c_p_r[wIndex - 1] << (0 + 4)); - status = (status & USBPORTSC_CCS) | + (!!test_bit(wIndex, &uhci->c_p_r) << (0 + 4)); + status = (status & USBPORTSC_CCS) | ((status & USBPORTSC_PE) >> (2 - 1)) | ((status & USBPORTSC_SUSP) >> (12 - 2)) | ((status & USBPORTSC_PR) >> (9 - 4)) | @@ -110,7 +114,7 @@ } break; case SetPortFeature: - if (!wIndex || wIndex > uhci->rh_numports) + if (wIndex >= uhci->rh_numports) goto err; switch (wValue) { @@ -120,7 +124,7 @@ case USB_PORT_FEAT_RESET: SET_RH_PORTSTAT(USBPORTSC_PR); mdelay(50); /* USB v1.1 7.1.7.3 */ - c_p_r[wIndex - 1] = 1; + set_bit(wIndex, &uhci->c_p_r); CLR_RH_PORTSTAT(USBPORTSC_PR); udelay(10); SET_RH_PORTSTAT(USBPORTSC_PE); @@ -137,7 +141,7 @@ } break; case ClearPortFeature: - if (!wIndex || wIndex > uhci->rh_numports) + if (wIndex >= uhci->rh_numports) goto err; switch (wValue) { @@ -161,7 +165,7 @@ case USB_PORT_FEAT_C_OVER_CURRENT: OK(0); /* port power over current */ case USB_PORT_FEAT_C_RESET: - c_p_r[wIndex - 1] = 0; + clear_bit(wIndex, &uhci->c_p_r); OK(0); default: goto err; Second patch (as92) -------------------------- --- 2.6.0/drivers/usb/host/uhci-hcd.h.4 Wed Sep 3 11:45:57 2003 +++ 2.6.0/drivers/usb/host/uhci-hcd.h Thu Sep 4 09:42:39 2003 @@ -295,17 +295,19 @@ * The resume process is divided into substates in order to avoid * potentially length delays during the timer handler. * - * States in which the host controller is halted must have values <= 0. + * States in which the host controller is suspended have values < UHCI_RESET, + * states in which the controller is waking up or running have larger values. */ enum uhci_state { + UHCI_SUSPENDED, /* When no devices are attached */ + UHCI_SUSPENDED_PM, /* Power Management suspend */ UHCI_RESET, - UHCI_RUNNING_GRACE, /* Before RUNNING */ + UHCI_RESUMING_1, + UHCI_RESUMING_2, UHCI_RUNNING, /* The normal state */ UHCI_SUSPENDING_GRACE, /* Before SUSPENDED */ - UHCI_SUSPENDED = -10, /* When no devices are attached */ - UHCI_RESUMING_1, - UHCI_RESUMING_2 }; +#define uhci_active(u) (u->state > UHCI_RESET) #define hcd_to_uhci(hcd_ptr) container_of(hcd_ptr, struct uhci_hcd, hcd) @@ -339,7 +341,8 @@ int fsbr; /* Full speed bandwidth reclamation */ unsigned long fsbrtimeout; /* FSBR delay */ - enum uhci_state state; /* FIXME: needs a spinlock */ + struct semaphore state_mutex; + enum uhci_state state; /* P: uhci->state_mutex */ unsigned long state_end; /* Time of next transition */ int resume_detect; /* Need a Global Resume */ unsigned long c_p_r; /* Changed-Port-Reset bits */ @@ -368,7 +371,7 @@ struct timer_list stall_timer; - wait_queue_head_t ep_waitqh; + wait_queue_head_t waitqh; }; struct urb_priv { --- 2.6.0/drivers/usb/host/uhci-debug.c.4 Wed Sep 3 16:53:05 2003 +++ 2.6.0/drivers/usb/host/uhci-debug.c Wed Sep 3 17:06:03 2003 @@ -421,9 +421,18 @@ struct uhci_qh *qh; struct uhci_td *td; struct list_head *tmp, *head; + static char *states[] = { + "SUSPENDED", "SUSPENDED_PM", "RESET", "RESUMING_1", + "RESUMING_2", "RUNNING", "SUSPENDING_GRACE"}; + char *s; spin_lock_irqsave(&uhci->frame_list_lock, flags); + s = "?"; + if (uhci->state >= 0 && + uhci->state < sizeof(states) / sizeof(states[0])) + s = states[uhci->state]; + out += sprintf(out, "HC driver state %s\n", s); out += sprintf(out, "HC status\n"); out += uhci_show_status(uhci, out, len - (out - buf)); --- 2.6.0/drivers/usb/host/uhci-hub.c.4 Wed Sep 3 16:00:19 2003 +++ 2.6.0/drivers/usb/host/uhci-hub.c Thu Sep 4 09:57:42 2003 @@ -122,6 +122,14 @@ SET_RH_PORTSTAT(USBPORTSC_SUSP); OK(0); case USB_PORT_FEAT_RESET: + /* VIA workaround: + int was_active = 0; + down(&uhci->state_mutex); + if (uhci_active(uhci)) { + was_active = 1; + suspend_hc(uhci); + } + */ SET_RH_PORTSTAT(USBPORTSC_PR); mdelay(50); /* USB v1.1 7.1.7.3 */ set_bit(wIndex, &uhci->c_p_r); @@ -130,6 +138,11 @@ SET_RH_PORTSTAT(USBPORTSC_PE); mdelay(10); SET_RH_PORTSTAT(0xa); + /* VIA workaround: + if (was_active) + wakeup_hc(uhci); + up(&uhci->state_mutex); + */ OK(0); case USB_PORT_FEAT_POWER: OK(0); /* port power ** */ --- 2.6.0/drivers/usb/host/uhci-hcd.c.4 Wed Sep 3 11:45:53 2003 +++ 2.6.0/drivers/usb/host/uhci-hcd.c Thu Sep 4 10:35:05 2003 @@ -1735,8 +1735,6 @@ return inw(uhci->io_addr + USBFRNUM); } -static int init_stall_timer(struct usb_hcd *hcd); - static void stall_callback(unsigned long ptr) { struct usb_hcd *hcd = (struct usb_hcd *)ptr; @@ -1744,6 +1742,9 @@ struct list_head list, *tmp, *head; unsigned long flags; + if (!uhci_active(uhci)) + return; + INIT_LIST_HEAD(&list); spin_lock_irqsave(&uhci->urb_list_lock, flags); @@ -1789,7 +1790,7 @@ /* Poll for and perform state transitions */ hc_state_transitions(uhci); - init_stall_timer(hcd); + mod_timer(&uhci->stall_timer, jiffies + (HZ / 10)); } static int init_stall_timer(struct usb_hcd *hcd) @@ -1923,9 +1924,10 @@ err("%x: host system error, PCI problems?", io_addr); if (status & USBSTS_HCPE) err("%x: host controller process error. something bad happened", io_addr); - if ((status & USBSTS_HCH) && uhci->state > 0) { + if ((status & USBSTS_HCH) && uhci_active(uhci)) { err("%x: host controller halted. very bad", io_addr); /* FIXME: Reset the controller, fix the offending TD */ + uhci->state = UHCI_RESET; } } @@ -1958,7 +1960,10 @@ uhci_finish_completion(hcd, regs); /* Wake up anyone waiting for a SOF irq */ - wake_up_all(&uhci->ep_waitqh); + wake_up_all(&uhci->waitqh); + + if (uhci->resume_detect && uhci->state == UHCI_SUSPENDED) + hc_state_transitions(uhci); } static int urbs_for_ep(struct uhci_hcd *uhci, struct hcd_dev *dev, @@ -1980,8 +1985,8 @@ static void uhci_wait_for_endpoint(struct uhci_hcd *uhci, struct hcd_dev *dev, int endpoint) { - wait_event_interruptible(uhci->ep_waitqh, - urbs_for_ep(uhci, dev, endpoint) == 0); + wait_event_interruptible(uhci->waitqh, (uhci->state == UHCI_RESET || + urbs_for_ep(uhci, dev, endpoint) == 0)); } static void reset_hc(struct uhci_hcd *uhci) @@ -2000,7 +2005,6 @@ set_current_state(TASK_UNINTERRUPTIBLE); schedule_timeout((HZ*10+999) / 1000); set_current_state(TASK_RUNNING); - uhci->resume_detect = 0; } static void suspend_hc(struct uhci_hcd *uhci) @@ -2019,12 +2023,14 @@ switch (uhci->state) { case UHCI_SUSPENDED: /* Start the resume */ + case UHCI_SUSPENDED_PM: dbg("%x: wakeup_hc", io_addr); /* 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; + mod_timer(&uhci->stall_timer, jiffies + (HZ / 10)); break; case UHCI_RESUMING_1: /* End global resume */ @@ -2036,16 +2042,16 @@ if (inw(io_addr + USBCMD) & USBCMD_FGR) break; - /* Run for at least 1 second, and - * mark it configured with a 64-byte max packet */ - uhci->state = UHCI_RUNNING_GRACE; + /* Run and mark it configured with a 64-byte + * max packet */ + uhci->state = UHCI_RUNNING; uhci->state_end = jiffies + HZ; outw(USBCMD_RS | USBCMD_CF | USBCMD_MAXP, io_addr + USBCMD); - break; - case UHCI_RUNNING_GRACE: /* Now allowed to suspend */ - uhci->state = UHCI_RUNNING; + /* Wake up anyone waiting for the resume to end */ + wake_up_all(&uhci->waitqh); + uhci->resume_detect = 0; break; default: @@ -2094,6 +2100,8 @@ static void hc_state_transitions(struct uhci_hcd *uhci) { + if (down_trylock(&uhci->state_mutex) != 0) + return; switch (uhci->state) { case UHCI_RUNNING: @@ -2120,7 +2128,6 @@ case UHCI_RESUMING_1: case UHCI_RESUMING_2: - case UHCI_RUNNING_GRACE: if (time_after_eq(jiffies, uhci->state_end)) wakeup_hc(uhci); break; @@ -2128,12 +2135,15 @@ default: break; } + up(&uhci->state_mutex); } static void start_hc(struct uhci_hcd *uhci) { unsigned int io_addr = uhci->io_addr; - int timeout = 1000; + unsigned long timeout; + + down(&uhci->state_mutex); /* * Reset the HC - this will force us to get a @@ -2142,8 +2152,10 @@ * implies. */ outw(USBCMD_HCRESET, io_addr + USBCMD); + timeout = jiffies + HZ/10; while (inw(io_addr + USBCMD) & USBCMD_HCRESET) { - if (!--timeout) { + schedule(); + if (time_after(jiffies, timeout)) { printk(KERN_ERR "uhci: USBCMD_HCRESET timed out!\n"); break; } @@ -2158,15 +2170,14 @@ outl(uhci->fl->dma_handle, io_addr + USBFLBASEADD); /* Run and mark it configured with a 64-byte max packet */ - uhci->state = UHCI_RUNNING_GRACE; - uhci->state_end = jiffies + HZ; + uhci->state = UHCI_RUNNING; outw(USBCMD_RS | USBCMD_CF | USBCMD_MAXP, io_addr + USBCMD); - uhci->hcd.state = USB_STATE_RUNNING; + up(&uhci->state_mutex); } /* - * De-allocate all resources.. + * De-allocate all resources. */ static void release_uhci(struct uhci_hcd *uhci) { @@ -2284,7 +2295,9 @@ spin_lock_init(&uhci->frame_list_lock); - init_waitqueue_head(&uhci->ep_waitqh); + init_MUTEX(&uhci->state_mutex); + + init_waitqueue_head(&uhci->waitqh); uhci->fl = pci_alloc_consistent(hcd->pdev, sizeof(*uhci->fl), &dma_handle); if (!uhci->fl) { @@ -2417,6 +2430,7 @@ } start_hc(uhci); + uhci->hcd.state = USB_STATE_RUNNING; init_stall_timer(hcd); @@ -2435,6 +2449,7 @@ */ err_start_root_hub: reset_hc(uhci); + uhci->hcd.state = USB_STATE_HALT; del_timer_sync(&uhci->stall_timer); @@ -2489,11 +2504,16 @@ uhci_free_pending_tds(uhci); uhci_remove_pending_urbps(uhci); + down(&uhci->state_mutex); reset_hc(uhci); + up(&uhci->state_mutex); uhci_free_pending_qhs(uhci); uhci_free_pending_tds(uhci); + /* Wake up anyone waiting for things to settle down */ + wake_up_all(&uhci->waitqh); + release_uhci(uhci); } @@ -2501,27 +2521,38 @@ static int uhci_suspend(struct usb_hcd *hcd, u32 state) { struct uhci_hcd *uhci = hcd_to_uhci(hcd); + int rc = 0; - /* Don't try to suspend broken motherboards, reset instead */ - if (suspend_allowed(uhci)) - suspend_hc(uhci); - else - reset_hc(uhci); - return 0; + /* This takes precedence over other state settings */ + down(&uhci->state_mutex); + if (uhci->state == UHCI_RESET) + rc = -EINVAL; + else { + if (uhci_active(uhci)) + suspend_hc(uhci); + uhci->state = UHCI_SUSPENDED_PM; + } + up(&uhci->state_mutex); + return rc; } static int uhci_resume(struct usb_hcd *hcd) { struct uhci_hcd *uhci = hcd_to_uhci(hcd); + down(&uhci->state_mutex); pci_set_master(uhci->hcd.pdev); - - if (uhci->state == UHCI_SUSPENDED) - uhci->resume_detect = 1; - else { - reset_hc(uhci); - start_hc(uhci); + if (uhci->state != UHCI_SUSPENDED_PM) { + up(&uhci->state_mutex); + return -EINVAL; } + + wakeup_hc(uhci); + up(&uhci->state_mutex); + wait_event_interruptible(uhci->waitqh, (uhci->state == UHCI_RUNNING || + uhci->state == UHCI_RESET)); + if (uhci->state != UHCI_RUNNING) + return -EINTR; uhci->hcd.state = USB_STATE_RUNNING; return 0; } ------------------------------------------------------- This sf.net email is sponsored by:ThinkGeek Welcome to geek heaven. http://thinkgeek.com/sf _______________________________________________ [EMAIL PROTECTED] To unsubscribe, use the last form field at: https://lists.sourceforge.net/lists/listinfo/linux-usb-devel