Commit f5510e19 authored by Greg Kroah-Hartman's avatar Greg Kroah-Hartman

Merge kroah.com:/home/greg/linux/BK/bleed-2.6

into kroah.com:/home/greg/linux/BK/usb-2.6
parents 141baf80 7e0cb221
CHANGES CHANGES
- Created based off of scanner & INSTALL from the original touchscreen - 0.3 - Created based off of scanner & INSTALL from the original touchscreen
driver on freshmeat (http://freshmeat.net/projects/3mtouchscreendriver) driver on freshmeat (http://freshmeat.net/projects/3mtouchscreendriver)
- Amended for linux-2.4.18, then 2.4.19 - Amended for linux-2.4.18, then 2.4.19
- Complete rewrite using Linux Input in 2.6.3 - 0.5 - Complete rewrite using Linux Input in 2.6.3
Unfortunately no calibration support at this time Unfortunately no calibration support at this time
- 1.4 - Multiple changes to support the EXII 5000UC and house cleaning
Changed reset from standard USB dev reset to vendor reset
Changed data sent to host from compensated to raw coordinates
Eliminated vendor/product module params
Performed multiple successfull tests with an EXII-5010UC
DRIVER NOTES: SUPPORTED HARDWARE:
All controllers have the Vendor: 0x0596 & Product: 0x0001
Installation is simple, you only need to add Linux Input, Linux USB, and the
driver to the kernel. The driver can also be optionally built as a module.
If you have another MicroTouch device that you wish to experiment with Controller Description Part Number
or try using this driver with, but the Vendor and Product ID's are not ------------------------------------------------------
coded in, don't despair. If the driver was compiled as a module, you can
pass options to the driver. Simply try:
/sbin/modprobe mtouchusb vendor=0x#### product=0x**** USB Capacitive - Pearl Case 14-205 (Discontinued)
USB Capacitive - Black Case 14-124 (Discontinued)
USB Capacitive - No Case 14-206 (Discontinued)
If it works, send me the iVendor & iProduct (or a patch) and I will add... USB Capacitive - Pearl Case EXII-5010UC
USB Capacitive - Black Case EXII-5030UC
USB Capacitive - No Case EXII-5050UC
DRIVER NOTES:
Installation is simple, you only need to add Linux Input, Linux USB, and the
driver to the kernel. The driver can also be optionally built as a module.
This driver appears to be one of possible 2 Linux USB Input Touchscreen This driver appears to be one of possible 2 Linux USB Input Touchscreen
drivers. Although 3M produces a binary only driver available for drivers. Although 3M produces a binary only driver available for
...@@ -28,53 +40,28 @@ download, I persist in updating this driver since I would like to use the ...@@ -28,53 +40,28 @@ download, I persist in updating this driver since I would like to use the
touchscreen for embedded apps using QTEmbedded, DirectFB, etc. So I feel the touchscreen for embedded apps using QTEmbedded, DirectFB, etc. So I feel the
logical choice is to use Linux Imput. logical choice is to use Linux Imput.
A little info about the MicroTouch USB controller (14-206): Currently there is no way to calibrate the device via this driver. Even if
the device could be calibrated, the driver pulls to raw coordinate data from
Y is inverted, and the device has a total possible resolution of 0 - 65535. the controller. This means calibration must be performed within the
userspace.
Y is inverted by the driver by:
The controller screen resolution is now 0 to 16384 for both X and Y reporting
input.absmin[ABS_Y] = MTOUCHUSB_MAX_YC; the raw touch data. This is the same for the old and new capacitive USB
input.absmax[ABS_Y] = MTOUCHUSB_MIN_YC; controllers.
absmin & absmax are also used to scale the data, sine it is rather high Perhaps at some point an abstract function will be placed into evdev so
resolution. generic functions like calibrations, resets, and vendor information can be
requested from the userspace (And the drivers would handle the vendor specific
---------------touch screen area----------------- tasks).
I MicroTouch (xmax,ymax) @I
I X I ADDITIONAL INFORMATION/UPDATES/X CONFIGURATION EXAMPLE:
I ########visible monitor area############## I
I #@ (xmin,ymin) # I
I # # I
I # # I
I # # I
I # # I
I # # I
I Y # # I
I # # I
I # # I
I # # I
I # # I
I # # I
I # (xmax,ymax) @# I
I ########################################## I
I I
I@ MicroTouch (xmin,ymin) I
-------------------------------------------------
Currently there is no way to calibrate the device via this driver. Perhaps
at some point an abstract function will be placed into evdev so generic
functions like calibrations, resets, and vendor information can be requested
(And the drivers would handle the vendor specific tasks).
ADDITIONAL INFORMATION/UPDATES:
http://groomlakelabs.com/grandamp/code/microtouch/ http://groomlakelabs.com/grandamp/code/microtouch/
TODO: TODO:
Implement a control urb again to handle requests to and from the device Implement a control urb again to handle requests to and from the device
such as calibration, etc. such as calibration, etc once/if it becomes available.
DISCLAMER: DISCLAMER:
...@@ -83,3 +70,7 @@ this driver! If you want touch drivers only supported within X, please go to: ...@@ -83,3 +70,7 @@ this driver! If you want touch drivers only supported within X, please go to:
http://www.3m.com/3MTouchSystems/downloads/ http://www.3m.com/3MTouchSystems/downloads/
THANKS:
A huge thank you to 3M Touch Systems for the EXII-5010UC controllers for
testing!
...@@ -1229,6 +1229,13 @@ M: James.Bottomley@HansenPartnership.com ...@@ -1229,6 +1229,13 @@ M: James.Bottomley@HansenPartnership.com
L: linux-scsi@vger.kernel.org L: linux-scsi@vger.kernel.org
S: Maintained S: Maintained
LEGO USB Tower driver
P: Juergen Stuber
M: starblue@users.sourceforge.net
L: legousb-devel@lists.sourceforge.net
W: http://legousb.sourceforge.net/
S: Maintained
LINUX FOR IBM pSERIES (RS/6000) LINUX FOR IBM pSERIES (RS/6000)
P: Paul Mackerras P: Paul Mackerras
M: paulus@au.ibm.com M: paulus@au.ibm.com
......
...@@ -98,14 +98,6 @@ struct bfusb_scb { ...@@ -98,14 +98,6 @@ struct bfusb_scb {
static void bfusb_tx_complete(struct urb *urb, struct pt_regs *regs); static void bfusb_tx_complete(struct urb *urb, struct pt_regs *regs);
static void bfusb_rx_complete(struct urb *urb, struct pt_regs *regs); static void bfusb_rx_complete(struct urb *urb, struct pt_regs *regs);
static inline void bfusb_wait_for_urb(struct urb *urb)
{
while (atomic_read(&urb->count) > 1) {
current->state = TASK_UNINTERRUPTIBLE;
schedule_timeout((5 * HZ + 999) / 1000);
}
}
static struct urb *bfusb_get_completed(struct bfusb *bfusb) static struct urb *bfusb_get_completed(struct bfusb *bfusb)
{ {
struct sk_buff *skb; struct sk_buff *skb;
...@@ -132,7 +124,6 @@ static void bfusb_unlink_urbs(struct bfusb *bfusb) ...@@ -132,7 +124,6 @@ static void bfusb_unlink_urbs(struct bfusb *bfusb)
while ((skb = skb_dequeue(&bfusb->pending_q))) { while ((skb = skb_dequeue(&bfusb->pending_q))) {
urb = ((struct bfusb_scb *) skb->cb)->urb; urb = ((struct bfusb_scb *) skb->cb)->urb;
usb_unlink_urb(urb); usb_unlink_urb(urb);
bfusb_wait_for_urb(urb);
skb_queue_tail(&bfusb->completed_q, skb); skb_queue_tail(&bfusb->completed_q, skb);
} }
......
...@@ -342,7 +342,7 @@ static int hci_usb_flush(struct hci_dev *hdev) ...@@ -342,7 +342,7 @@ static int hci_usb_flush(struct hci_dev *hdev)
static inline void hci_usb_wait_for_urb(struct urb *urb) static inline void hci_usb_wait_for_urb(struct urb *urb)
{ {
while (atomic_read(&urb->count) > 1) { while (atomic_read(&urb->kref.refcount) > 1) {
current->state = TASK_UNINTERRUPTIBLE; current->state = TASK_UNINTERRUPTIBLE;
schedule_timeout((5 * HZ + 999) / 1000); schedule_timeout((5 * HZ + 999) / 1000);
} }
......
...@@ -257,13 +257,18 @@ static void st5481B_mode(struct st5481_bcs *bcs, int mode) ...@@ -257,13 +257,18 @@ static void st5481B_mode(struct st5481_bcs *bcs, int mode)
static int st5481_setup_b_out(struct st5481_bcs *bcs) static int st5481_setup_b_out(struct st5481_bcs *bcs)
{ {
struct usb_device *dev = bcs->adapter->usb_dev; struct usb_device *dev = bcs->adapter->usb_dev;
struct usb_host_interface *altsetting; struct usb_interface *intf;
struct usb_host_interface *altsetting = NULL;
struct usb_host_endpoint *endpoint; struct usb_host_endpoint *endpoint;
struct st5481_b_out *b_out = &bcs->b_out; struct st5481_b_out *b_out = &bcs->b_out;
DBG(4,""); DBG(4,"");
altsetting = &(dev->config->interface[0]->altsetting[3]); intf = usb_ifnum_to_if(dev, 0);
if (intf)
altsetting = usb_altnum_to_altsetting(intf, 3);
if (!altsetting)
return -ENXIO;
// Allocate URBs and buffers for the B channel out // Allocate URBs and buffers for the B channel out
endpoint = &altsetting->endpoint[EP_B1_OUT - 1 + bcs->channel * 2]; endpoint = &altsetting->endpoint[EP_B1_OUT - 1 + bcs->channel * 2];
......
...@@ -652,13 +652,18 @@ static void ph_disconnect(struct st5481_adapter *adapter) ...@@ -652,13 +652,18 @@ static void ph_disconnect(struct st5481_adapter *adapter)
static int st5481_setup_d_out(struct st5481_adapter *adapter) static int st5481_setup_d_out(struct st5481_adapter *adapter)
{ {
struct usb_device *dev = adapter->usb_dev; struct usb_device *dev = adapter->usb_dev;
struct usb_host_interface *altsetting; struct usb_interface *intf;
struct usb_host_interface *altsetting = NULL;
struct usb_host_endpoint *endpoint; struct usb_host_endpoint *endpoint;
struct st5481_d_out *d_out = &adapter->d_out; struct st5481_d_out *d_out = &adapter->d_out;
DBG(2,""); DBG(2,"");
altsetting = &(dev->config->interface[0]->altsetting[3]); intf = usb_ifnum_to_if(dev, 0);
if (intf)
altsetting = usb_altnum_to_altsetting(intf, 3);
if (!altsetting)
return -ENXIO;
// Allocate URBs and buffers for the D channel out // Allocate URBs and buffers for the D channel out
endpoint = &altsetting->endpoint[EP_D_OUT-1]; endpoint = &altsetting->endpoint[EP_D_OUT-1];
......
...@@ -244,7 +244,8 @@ int st5481_setup_usb(struct st5481_adapter *adapter) ...@@ -244,7 +244,8 @@ int st5481_setup_usb(struct st5481_adapter *adapter)
struct usb_device *dev = adapter->usb_dev; struct usb_device *dev = adapter->usb_dev;
struct st5481_ctrl *ctrl = &adapter->ctrl; struct st5481_ctrl *ctrl = &adapter->ctrl;
struct st5481_intr *intr = &adapter->intr; struct st5481_intr *intr = &adapter->intr;
struct usb_host_interface *altsetting; struct usb_interface *intf;
struct usb_host_interface *altsetting = NULL;
struct usb_host_endpoint *endpoint; struct usb_host_endpoint *endpoint;
int status; int status;
struct urb *urb; struct urb *urb;
...@@ -257,8 +258,11 @@ int st5481_setup_usb(struct st5481_adapter *adapter) ...@@ -257,8 +258,11 @@ int st5481_setup_usb(struct st5481_adapter *adapter)
return status; return status;
} }
intf = usb_ifnum_to_if(dev, 0);
altsetting = &(dev->config->interface[0]->altsetting[3]); if (intf)
altsetting = usb_altnum_to_altsetting(intf, 3);
if (!altsetting)
return -ENXIO;
// Check if the config is sane // Check if the config is sane
if ( altsetting->desc.bNumEndpoints != 7 ) { if ( altsetting->desc.bNumEndpoints != 7 ) {
......
...@@ -499,7 +499,7 @@ static int cpia_probe(struct usb_interface *intf, ...@@ -499,7 +499,7 @@ static int cpia_probe(struct usb_interface *intf,
if (udev->descriptor.bNumConfigurations != 1) if (udev->descriptor.bNumConfigurations != 1)
return -ENODEV; return -ENODEV;
interface = &intf->altsetting[0]; interface = intf->cur_altsetting;
printk(KERN_INFO "USB CPiA camera found\n"); printk(KERN_INFO "USB CPiA camera found\n");
...@@ -620,8 +620,6 @@ static void cpia_disconnect(struct usb_interface *intf) ...@@ -620,8 +620,6 @@ static void cpia_disconnect(struct usb_interface *intf)
wake_up_interruptible(&ucpia->wq_stream); wake_up_interruptible(&ucpia->wq_stream);
udev = interface_to_usbdev(intf); udev = interface_to_usbdev(intf);
usb_driver_release_interface(&cpia_driver,
udev->actconfig->interface[0]);
ucpia->curbuff = ucpia->workbuff = NULL; ucpia->curbuff = ucpia->workbuff = NULL;
......
...@@ -1421,7 +1421,7 @@ static int irda_usb_probe(struct usb_interface *intf, ...@@ -1421,7 +1421,7 @@ static int irda_usb_probe(struct usb_interface *intf,
} }
/* Find our endpoints */ /* Find our endpoints */
interface = &intf->altsetting[0]; interface = intf->cur_altsetting;
if(!irda_usb_parse_endpoints(self, interface->endpoint, if(!irda_usb_parse_endpoints(self, interface->endpoint,
interface->desc.bNumEndpoints)) { interface->desc.bNumEndpoints)) {
ERROR("%s(), Bogus endpoints...\n", __FUNCTION__); ERROR("%s(), Bogus endpoints...\n", __FUNCTION__);
......
...@@ -66,3 +66,4 @@ obj-$(CONFIG_USB_SPEEDTOUCH) += misc/ ...@@ -66,3 +66,4 @@ obj-$(CONFIG_USB_SPEEDTOUCH) += misc/
obj-$(CONFIG_USB_TEST) += misc/ obj-$(CONFIG_USB_TEST) += misc/
obj-$(CONFIG_USB_TIGL) += misc/ obj-$(CONFIG_USB_TIGL) += misc/
obj-$(CONFIG_USB_USS720) += misc/ obj-$(CONFIG_USB_USS720) += misc/
obj-$(CONFIG_USB_PHIDGETSERVO) += misc/
...@@ -1234,7 +1234,7 @@ static struct tty_operations bluetooth_ops = { ...@@ -1234,7 +1234,7 @@ static struct tty_operations bluetooth_ops = {
.chars_in_buffer = bluetooth_chars_in_buffer, .chars_in_buffer = bluetooth_chars_in_buffer,
}; };
int usb_bluetooth_init(void) static int usb_bluetooth_init(void)
{ {
int i; int i;
int result; int result;
...@@ -1283,7 +1283,7 @@ int usb_bluetooth_init(void) ...@@ -1283,7 +1283,7 @@ int usb_bluetooth_init(void)
} }
void usb_bluetooth_exit(void) static void usb_bluetooth_exit(void)
{ {
usb_deregister(&usb_bluetooth_driver); usb_deregister(&usb_bluetooth_driver);
tty_unregister_driver(bluetooth_tty_driver); tty_unregister_driver(bluetooth_tty_driver);
......
...@@ -567,6 +567,8 @@ static void acm_tty_set_termios(struct tty_struct *tty, struct termios *termios_ ...@@ -567,6 +567,8 @@ static void acm_tty_set_termios(struct tty_struct *tty, struct termios *termios_
* USB probe and disconnect routines. * USB probe and disconnect routines.
*/ */
#define CHECK_XFERTYPE(descr, xfer_type) (((descr)->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) == xfer_type)
static int acm_probe (struct usb_interface *intf, static int acm_probe (struct usb_interface *intf,
const struct usb_device_id *id) const struct usb_device_id *id)
{ {
...@@ -583,143 +585,150 @@ static int acm_probe (struct usb_interface *intf, ...@@ -583,143 +585,150 @@ static int acm_probe (struct usb_interface *intf,
dev = interface_to_usbdev (intf); dev = interface_to_usbdev (intf);
cfacm = dev->actconfig; cfacm = dev->actconfig;
/* We know we're probe()d with the control interface. */ /* We know we're probe()d with the control interface. */
ifcom = intf->cur_altsetting; ifcom = intf->cur_altsetting;
/* ACM doesn't guarantee the data interface is /* ACM doesn't guarantee the data interface is
* adjacent to the control interface, or that if one * adjacent to the control interface, or that if one
* is there it's not for call management ... so find * is there it's not for call management ... so find
* it * it
*/ */
for (j = 0; j < cfacm->desc.bNumInterfaces; j++) { for (j = 0; j < cfacm->desc.bNumInterfaces; j++) {
ifdata = cfacm->interface[j]->cur_altsetting; ifdata = cfacm->interface[j]->cur_altsetting;
data = cfacm->interface[j]; data = cfacm->interface[j];
if (ifdata->desc.bInterfaceClass == 10 && if (ifdata->desc.bInterfaceClass == USB_CLASS_CDC_DATA
ifdata->desc.bNumEndpoints == 2) { && ifdata->desc.bNumEndpoints == 2) {
epctrl = &ifcom->endpoint[0].desc;
epread = &ifdata->endpoint[0].desc; epctrl = &ifcom->endpoint[0].desc;
epwrite = &ifdata->endpoint[1].desc; epread = &ifdata->endpoint[0].desc;
epwrite = &ifdata->endpoint[1].desc;
if ((epctrl->bEndpointAddress & 0x80) != 0x80 ||
(epctrl->bmAttributes & 3) != 3 || if ((epctrl->bEndpointAddress & USB_DIR_IN) != USB_DIR_IN
(epread->bmAttributes & 3) != 2 || || !CHECK_XFERTYPE(epctrl, USB_ENDPOINT_XFER_INT)
(epwrite->bmAttributes & 3) != 2 || || !CHECK_XFERTYPE(epread, USB_ENDPOINT_XFER_BULK)
((epread->bEndpointAddress & 0x80) ^ (epwrite->bEndpointAddress & 0x80)) != 0x80) || !CHECK_XFERTYPE(epwrite, USB_ENDPOINT_XFER_BULK)
goto next_interface; || ((epread->bEndpointAddress & USB_DIR_IN)
^ (epwrite->bEndpointAddress & USB_DIR_IN)) != USB_DIR_IN) {
if ((epread->bEndpointAddress & 0x80) != 0x80) { /* not suitable */
epread = &ifdata->endpoint[1].desc; goto next_interface;
epwrite = &ifdata->endpoint[0].desc; }
}
dbg("found data interface at %d\n", j); if ((epread->bEndpointAddress & USB_DIR_IN) != USB_DIR_IN) {
break; /* descriptors are swapped */
} else { epread = &ifdata->endpoint[1].desc;
next_interface: epwrite = &ifdata->endpoint[0].desc;
ifdata = NULL;
data = NULL;
}
} }
dev_dbg(&intf->dev, "found data interface at %d\n", j);
break;
} else {
next_interface:
ifdata = NULL;
data = NULL;
}
}
/* there's been a problem */ /* there's been a problem */
if (!ifdata) { if (!ifdata) {
dbg("interface not found (%p)\n", ifdata); dev_dbg(&intf->dev, "data interface not found\n");
return -ENODEV; return -ENODEV;
} }
for (minor = 0; minor < ACM_TTY_MINORS && acm_table[minor]; minor++); for (minor = 0; minor < ACM_TTY_MINORS && acm_table[minor]; minor++);
if (acm_table[minor]) { if (acm_table[minor]) {
err("no more free acm devices"); err("no more free acm devices");
return -ENODEV; return -ENODEV;
} }
if (!(acm = kmalloc(sizeof(struct acm), GFP_KERNEL))) { if (!(acm = kmalloc(sizeof(struct acm), GFP_KERNEL))) {
err("out of memory"); dev_dbg(&intf->dev, "out of memory (acm kmalloc)\n");
return -ENOMEM; return -ENOMEM;
} }
memset(acm, 0, sizeof(struct acm));
memset(acm, 0, sizeof(struct acm));
ctrlsize = epctrl->wMaxPacketSize;
readsize = epread->wMaxPacketSize; ctrlsize = epctrl->wMaxPacketSize;
acm->writesize = epwrite->wMaxPacketSize; readsize = epread->wMaxPacketSize;
acm->control = intf; acm->writesize = epwrite->wMaxPacketSize;
acm->data = data; acm->control = intf;
acm->minor = minor; acm->data = data;
acm->dev = dev; acm->minor = minor;
acm->dev = dev;
acm->bh.func = acm_rx_tasklet;
acm->bh.data = (unsigned long) acm; acm->bh.func = acm_rx_tasklet;
INIT_WORK(&acm->work, acm_softint, acm); acm->bh.data = (unsigned long) acm;
INIT_WORK(&acm->work, acm_softint, acm);
if (!(buf = kmalloc(ctrlsize + readsize + acm->writesize, GFP_KERNEL))) {
err("out of memory"); if (!(buf = kmalloc(ctrlsize + readsize + acm->writesize, GFP_KERNEL))) {
kfree(acm); dev_dbg(&intf->dev, "out of memory (buf kmalloc)\n");
return -ENOMEM; kfree(acm);
} return -ENOMEM;
}
acm->ctrlurb = usb_alloc_urb(0, GFP_KERNEL); acm->ctrlurb = usb_alloc_urb(0, GFP_KERNEL);
if (!acm->ctrlurb) { if (!acm->ctrlurb) {
err("out of memory"); dev_dbg(&intf->dev, "out of memory (ctrlurb kmalloc)\n");
kfree(acm); kfree(acm);
kfree(buf); kfree(buf);
return -ENOMEM; return -ENOMEM;
} }
acm->readurb = usb_alloc_urb(0, GFP_KERNEL); acm->readurb = usb_alloc_urb(0, GFP_KERNEL);
if (!acm->readurb) { if (!acm->readurb) {
err("out of memory"); dev_dbg(&intf->dev, "out of memory (readurb kmalloc)\n");
usb_free_urb(acm->ctrlurb); usb_free_urb(acm->ctrlurb);
kfree(acm); kfree(acm);
kfree(buf); kfree(buf);
return -ENOMEM; return -ENOMEM;
} }
acm->writeurb = usb_alloc_urb(0, GFP_KERNEL); acm->writeurb = usb_alloc_urb(0, GFP_KERNEL);
if (!acm->writeurb) { if (!acm->writeurb) {
err("out of memory"); dev_dbg(&intf->dev, "out of memory (writeurb kmalloc)\n");
usb_free_urb(acm->readurb); usb_free_urb(acm->readurb);
usb_free_urb(acm->ctrlurb); usb_free_urb(acm->ctrlurb);
kfree(acm); kfree(acm);
kfree(buf); kfree(buf);
return -ENOMEM; return -ENOMEM;
} }
usb_fill_int_urb(acm->ctrlurb, dev, usb_rcvintpipe(dev, epctrl->bEndpointAddress), usb_fill_int_urb(acm->ctrlurb, dev, usb_rcvintpipe(dev, epctrl->bEndpointAddress),
buf, ctrlsize, acm_ctrl_irq, acm, epctrl->bInterval); buf, ctrlsize, acm_ctrl_irq, acm, epctrl->bInterval);
usb_fill_bulk_urb(acm->readurb, dev, usb_rcvbulkpipe(dev, epread->bEndpointAddress), usb_fill_bulk_urb(acm->readurb, dev, usb_rcvbulkpipe(dev, epread->bEndpointAddress),
buf += ctrlsize, readsize, acm_read_bulk, acm); buf += ctrlsize, readsize, acm_read_bulk, acm);
acm->readurb->transfer_flags |= URB_NO_FSBR; acm->readurb->transfer_flags |= URB_NO_FSBR;
usb_fill_bulk_urb(acm->writeurb, dev, usb_sndbulkpipe(dev, epwrite->bEndpointAddress), usb_fill_bulk_urb(acm->writeurb, dev, usb_sndbulkpipe(dev, epwrite->bEndpointAddress),
buf += readsize, acm->writesize, acm_write_bulk, acm); buf += readsize, acm->writesize, acm_write_bulk, acm);
acm->writeurb->transfer_flags |= URB_NO_FSBR; acm->writeurb->transfer_flags |= URB_NO_FSBR;
dev_info(&intf->dev, "ttyACM%d: USB ACM device", minor); if ( (j = usb_driver_claim_interface(&acm_driver, data, acm)) != 0) {
err("claim failed");
usb_free_urb(acm->ctrlurb);
usb_free_urb(acm->readurb);
usb_free_urb(acm->writeurb);
kfree(acm);
kfree(buf);
return j;
}
acm_set_control(acm, acm->ctrlout); tty_register_device(acm_tty_driver, minor, &intf->dev);
acm->line.speed = cpu_to_le32(9600); dev_info(&intf->dev, "ttyACM%d: USB ACM device\n", minor);
acm->line.databits = 8;
acm_set_line(acm, &acm->line);
if ( (j = usb_driver_claim_interface(&acm_driver, data, acm)) != 0) { acm_set_control(acm, acm->ctrlout);
err("claim failed");
usb_free_urb(acm->ctrlurb);
usb_free_urb(acm->readurb);
usb_free_urb(acm->writeurb);
kfree(acm);
kfree(buf);
return j;
}
tty_register_device(acm_tty_driver, minor, &intf->dev); acm->line.speed = cpu_to_le32(9600);
acm->line.databits = 8;
acm_set_line(acm, &acm->line);
acm_table[minor] = acm; acm_table[minor] = acm;
usb_set_intfdata (intf, acm); usb_set_intfdata (intf, acm);
return 0; return 0;
} }
#undef CHECK_XFERTYPE
static void acm_disconnect(struct usb_interface *intf) static void acm_disconnect(struct usb_interface *intf)
{ {
......
This diff is collapsed.
...@@ -232,13 +232,21 @@ static char *usb_dump_endpoint_descriptor ( ...@@ -232,13 +232,21 @@ static char *usb_dump_endpoint_descriptor (
return start; return start;
} }
static char *usb_dump_interface_descriptor(char *start, char *end, const struct usb_interface *iface, int setno) static char *usb_dump_interface_descriptor(char *start, char *end,
const struct usb_interface_cache *intfc,
const struct usb_interface *iface,
int setno)
{ {
struct usb_interface_descriptor *desc = &iface->altsetting[setno].desc; const struct usb_interface_descriptor *desc = &intfc->altsetting[setno].desc;
char *driver_name = "";
if (start > end) if (start > end)
return start; return start;
down_read(&usb_bus_type.subsys.rwsem); down_read(&usb_bus_type.subsys.rwsem);
if (iface)
driver_name = (iface->dev.driver
? iface->dev.driver->name
: "(none)");
start += sprintf(start, format_iface, start += sprintf(start, format_iface,
desc->bInterfaceNumber, desc->bInterfaceNumber,
desc->bAlternateSetting, desc->bAlternateSetting,
...@@ -247,9 +255,7 @@ static char *usb_dump_interface_descriptor(char *start, char *end, const struct ...@@ -247,9 +255,7 @@ static char *usb_dump_interface_descriptor(char *start, char *end, const struct
class_decode(desc->bInterfaceClass), class_decode(desc->bInterfaceClass),
desc->bInterfaceSubClass, desc->bInterfaceSubClass,
desc->bInterfaceProtocol, desc->bInterfaceProtocol,
iface->dev.driver driver_name);
? iface->dev.driver->name
: "(none)");
up_read(&usb_bus_type.subsys.rwsem); up_read(&usb_bus_type.subsys.rwsem);
return start; return start;
} }
...@@ -258,13 +264,14 @@ static char *usb_dump_interface( ...@@ -258,13 +264,14 @@ static char *usb_dump_interface(
int speed, int speed,
char *start, char *start,
char *end, char *end,
const struct usb_interface_cache *intfc,
const struct usb_interface *iface, const struct usb_interface *iface,
int setno int setno
) { ) {
struct usb_host_interface *desc = &iface->altsetting[setno]; const struct usb_host_interface *desc = &intfc->altsetting[setno];
int i; int i;
start = usb_dump_interface_descriptor(start, end, iface, setno); start = usb_dump_interface_descriptor(start, end, intfc, iface, setno);
for (i = 0; i < desc->desc.bNumEndpoints; i++) { for (i = 0; i < desc->desc.bNumEndpoints; i++) {
if (start > end) if (start > end)
return start; return start;
...@@ -303,6 +310,7 @@ static char *usb_dump_config ( ...@@ -303,6 +310,7 @@ static char *usb_dump_config (
) )
{ {
int i, j; int i, j;
struct usb_interface_cache *intfc;
struct usb_interface *interface; struct usb_interface *interface;
if (start > end) if (start > end)
...@@ -311,14 +319,13 @@ static char *usb_dump_config ( ...@@ -311,14 +319,13 @@ static char *usb_dump_config (
return start + sprintf(start, "(null Cfg. desc.)\n"); return start + sprintf(start, "(null Cfg. desc.)\n");
start = usb_dump_config_descriptor(start, end, &config->desc, active); start = usb_dump_config_descriptor(start, end, &config->desc, active);
for (i = 0; i < config->desc.bNumInterfaces; i++) { for (i = 0; i < config->desc.bNumInterfaces; i++) {
intfc = config->intf_cache[i];
interface = config->interface[i]; interface = config->interface[i];
if (!interface) for (j = 0; j < intfc->num_altsetting; j++) {
break;
for (j = 0; j < interface->num_altsetting; j++) {
if (start > end) if (start > end)
return start; return start;
start = usb_dump_interface(speed, start = usb_dump_interface(speed,
start, end, interface, j); start, end, intfc, interface, j);
} }
} }
return start; return start;
...@@ -395,7 +402,7 @@ static char *usb_dump_desc(char *start, char *end, struct usb_device *dev) ...@@ -395,7 +402,7 @@ static char *usb_dump_desc(char *start, char *end, struct usb_device *dev)
return start; return start;
start = usb_dump_device_strings (start, end, dev); start = usb_dump_device_strings (start, end, dev);
for (i = 0; i < dev->descriptor.bNumConfigurations; i++) { for (i = 0; i < dev->descriptor.bNumConfigurations; i++) {
if (start > end) if (start > end)
return start; return start;
...@@ -445,6 +452,7 @@ static char *usb_dump_string(char *start, char *end, const struct usb_device *de ...@@ -445,6 +452,7 @@ static char *usb_dump_string(char *start, char *end, const struct usb_device *de
* nbytes - the maximum number of bytes to write * nbytes - the maximum number of bytes to write
* skip_bytes - the number of bytes to skip before writing anything * skip_bytes - the number of bytes to skip before writing anything
* file_offset - the offset into the devices file on completion * file_offset - the offset into the devices file on completion
* The caller must own the usbdev->serialize semaphore.
*/ */
static ssize_t usb_device_dump(char __user **buffer, size_t *nbytes, loff_t *skip_bytes, loff_t *file_offset, static ssize_t usb_device_dump(char __user **buffer, size_t *nbytes, loff_t *skip_bytes, loff_t *file_offset,
struct usb_device *usbdev, struct usb_bus *bus, int level, int index, int count) struct usb_device *usbdev, struct usb_bus *bus, int level, int index, int count)
...@@ -545,9 +553,13 @@ static ssize_t usb_device_dump(char __user **buffer, size_t *nbytes, loff_t *ski ...@@ -545,9 +553,13 @@ static ssize_t usb_device_dump(char __user **buffer, size_t *nbytes, loff_t *ski
/* Now look at all of this device's children. */ /* Now look at all of this device's children. */
for (chix = 0; chix < usbdev->maxchild; chix++) { for (chix = 0; chix < usbdev->maxchild; chix++) {
if (usbdev->children[chix]) { struct usb_device *childdev = usbdev->children[chix];
ret = usb_device_dump(buffer, nbytes, skip_bytes, file_offset, usbdev->children[chix],
if (childdev) {
down(&childdev->serialize);
ret = usb_device_dump(buffer, nbytes, skip_bytes, file_offset, childdev,
bus, level + 1, chix, ++cnt); bus, level + 1, chix, ++cnt);
up(&childdev->serialize);
if (ret == -EFAULT) if (ret == -EFAULT)
return total_written; return total_written;
total_written += ret; total_written += ret;
...@@ -575,8 +587,11 @@ static ssize_t usb_device_read(struct file *file, char __user *buf, size_t nbyte ...@@ -575,8 +587,11 @@ static ssize_t usb_device_read(struct file *file, char __user *buf, size_t nbyte
for (buslist = usb_bus_list.next; buslist != &usb_bus_list; buslist = buslist->next) { for (buslist = usb_bus_list.next; buslist != &usb_bus_list; buslist = buslist->next) {
/* print devices for this bus */ /* print devices for this bus */
bus = list_entry(buslist, struct usb_bus, bus_list); bus = list_entry(buslist, struct usb_bus, bus_list);
/* recurse through all children of the root hub */ /* recurse through all children of the root hub */
down(&bus->root_hub->serialize);
ret = usb_device_dump(&buf, &nbytes, &skip_bytes, ppos, bus->root_hub, bus, 0, 0, 0); ret = usb_device_dump(&buf, &nbytes, &skip_bytes, ppos, bus->root_hub, bus, 0, 0, 0);
up(&bus->root_hub->serialize);
if (ret < 0) { if (ret < 0) {
up(&usb_bus_list_lock); up(&usb_bus_list_lock);
return ret; return ret;
......
This diff is collapsed.
...@@ -279,41 +279,53 @@ int usb_hcd_pci_suspend (struct pci_dev *dev, u32 state) ...@@ -279,41 +279,53 @@ int usb_hcd_pci_suspend (struct pci_dev *dev, u32 state)
{ {
struct usb_hcd *hcd; struct usb_hcd *hcd;
int retval = 0; int retval = 0;
int has_pci_pm;
hcd = pci_get_drvdata(dev); hcd = pci_get_drvdata(dev);
dev_dbg (hcd->self.controller, "suspend D%d --> D%d\n",
dev->current_state, state);
if (pci_find_capability(dev, PCI_CAP_ID_PM)) { /* even when the PCI layer rejects some of the PCI calls
dev_dbg(hcd->self.controller, "No PM capability\n"); * below, HCs can try global suspend and reduce DMA traffic.
return 0; * PM-sensitive HCDs may already have done this.
} */
has_pci_pm = pci_find_capability(dev, PCI_CAP_ID_PM);
if (has_pci_pm)
dev_dbg(hcd->self.controller, "suspend D%d --> D%d\n",
dev->current_state, state);
switch (hcd->state) { switch (hcd->state) {
case USB_STATE_HALT: case USB_STATE_HALT:
dev_dbg (hcd->self.controller, "halted; hcd not suspended\n"); dev_dbg (hcd->self.controller, "halted; hcd not suspended\n");
break; break;
case USB_STATE_SUSPENDED: case HCD_STATE_SUSPENDED:
dev_dbg (hcd->self.controller, "hcd already suspended\n"); dev_dbg (hcd->self.controller, "hcd already suspended\n");
break; break;
default: default:
/* remote wakeup needs hub->suspend() cooperation */
// pci_enable_wake (dev, 3, 1);
pci_save_state (dev, hcd->pci_state);
/* driver may want to disable DMA etc */
hcd->state = USB_STATE_QUIESCING;
retval = hcd->driver->suspend (hcd, state); retval = hcd->driver->suspend (hcd, state);
if (retval) if (retval)
dev_dbg (hcd->self.controller, dev_dbg (hcd->self.controller,
"suspend fail, retval %d\n", "suspend fail, retval %d\n",
retval); retval);
else else {
hcd->state = USB_STATE_SUSPENDED; hcd->state = HCD_STATE_SUSPENDED;
pci_save_state (dev, hcd->pci_state);
#ifdef CONFIG_USB_SUSPEND
pci_enable_wake (dev, state, hcd->remote_wakeup);
pci_enable_wake (dev, 4, hcd->remote_wakeup);
#endif
/* no DMA or IRQs except in D0 */
pci_disable_device (dev);
free_irq (hcd->irq, hcd);
if (has_pci_pm)
retval = pci_set_power_state (dev, state);
if (retval < 0) {
dev_dbg (&dev->dev,
"PCI suspend fail, %d\n",
retval);
(void) usb_hcd_pci_resume (dev);
}
}
} }
pci_set_power_state (dev, state);
return retval; return retval;
} }
EXPORT_SYMBOL (usb_hcd_pci_suspend); EXPORT_SYMBOL (usb_hcd_pci_suspend);
...@@ -328,23 +340,36 @@ int usb_hcd_pci_resume (struct pci_dev *dev) ...@@ -328,23 +340,36 @@ int usb_hcd_pci_resume (struct pci_dev *dev)
{ {
struct usb_hcd *hcd; struct usb_hcd *hcd;
int retval; int retval;
int has_pci_pm;
hcd = pci_get_drvdata(dev); hcd = pci_get_drvdata(dev);
dev_dbg (hcd->self.controller, "resume from state D%d\n", has_pci_pm = pci_find_capability(dev, PCI_CAP_ID_PM);
dev->current_state); if (has_pci_pm)
dev_dbg(hcd->self.controller, "resume from state D%d\n",
dev->current_state);
if (hcd->state != USB_STATE_SUSPENDED) { if (hcd->state != HCD_STATE_SUSPENDED) {
dev_dbg (hcd->self.controller, dev_dbg (hcd->self.controller,
"can't resume, not suspended!\n"); "can't resume, not suspended!\n");
return -EL3HLT; return -EL3HLT;
} }
hcd->state = USB_STATE_RESUMING; hcd->state = USB_STATE_RESUMING;
pci_set_power_state (dev, 0); if (has_pci_pm)
pci_set_power_state (dev, 0);
retval = request_irq (dev->irq, usb_hcd_irq, SA_SHIRQ,
hcd->description, hcd);
if (retval < 0) {
dev_err (hcd->self.controller,
"can't restore IRQ after resume!\n");
return retval;
}
pci_set_master (dev);
pci_restore_state (dev, hcd->pci_state); pci_restore_state (dev, hcd->pci_state);
#ifdef CONFIG_USB_SUSPEND
/* remote wakeup needs hub->suspend() cooperation */ pci_enable_wake (dev, dev->current_state, 0);
// pci_enable_wake (dev, 3, 0); pci_enable_wake (dev, 4, 0);
#endif
retval = hcd->driver->resume (hcd); retval = hcd->driver->resume (hcd);
if (!HCD_IS_RUNNING (hcd->state)) { if (!HCD_IS_RUNNING (hcd->state)) {
......
...@@ -171,10 +171,10 @@ static const u8 fs_rh_config_descriptor [] = { ...@@ -171,10 +171,10 @@ static const u8 fs_rh_config_descriptor [] = {
0x01, /* __u8 bNumInterfaces; (1) */ 0x01, /* __u8 bNumInterfaces; (1) */
0x01, /* __u8 bConfigurationValue; */ 0x01, /* __u8 bConfigurationValue; */
0x00, /* __u8 iConfiguration; */ 0x00, /* __u8 iConfiguration; */
0x40, /* __u8 bmAttributes; 0xc0, /* __u8 bmAttributes;
Bit 7: Bus-powered, Bit 7: must be set,
6: Self-powered, 6: Self-powered,
5 Remote-wakwup, 5: Remote wakeup,
4..0: resvd */ 4..0: resvd */
0x00, /* __u8 MaxPower; */ 0x00, /* __u8 MaxPower; */
...@@ -218,10 +218,10 @@ static const u8 hs_rh_config_descriptor [] = { ...@@ -218,10 +218,10 @@ static const u8 hs_rh_config_descriptor [] = {
0x01, /* __u8 bNumInterfaces; (1) */ 0x01, /* __u8 bNumInterfaces; (1) */
0x01, /* __u8 bConfigurationValue; */ 0x01, /* __u8 bConfigurationValue; */
0x00, /* __u8 iConfiguration; */ 0x00, /* __u8 iConfiguration; */
0x40, /* __u8 bmAttributes; 0xc0, /* __u8 bmAttributes;
Bit 7: Bus-powered, Bit 7: must be set,
6: Self-powered, 6: Self-powered,
5 Remote-wakwup, 5: Remote wakeup,
4..0: resvd */ 4..0: resvd */
0x00, /* __u8 MaxPower; */ 0x00, /* __u8 MaxPower; */
...@@ -324,13 +324,15 @@ static int rh_string ( ...@@ -324,13 +324,15 @@ static int rh_string (
/* Root hub control transfers execute synchronously */ /* Root hub control transfers execute synchronously */
static int rh_call_control (struct usb_hcd *hcd, struct urb *urb) static int rh_call_control (struct usb_hcd *hcd, struct urb *urb)
{ {
struct usb_ctrlrequest *cmd = (struct usb_ctrlrequest *) urb->setup_packet; struct usb_ctrlrequest *cmd;
u16 typeReq, wValue, wIndex, wLength; u16 typeReq, wValue, wIndex, wLength;
const u8 *bufp = 0; const u8 *bufp = 0;
u8 *ubuf = urb->transfer_buffer; u8 *ubuf = urb->transfer_buffer;
int len = 0; int len = 0;
int patch_wakeup = 0;
unsigned long flags; unsigned long flags;
cmd = (struct usb_ctrlrequest *) urb->setup_packet;
typeReq = (cmd->bRequestType << 8) | cmd->bRequest; typeReq = (cmd->bRequestType << 8) | cmd->bRequest;
wValue = le16_to_cpu (cmd->wValue); wValue = le16_to_cpu (cmd->wValue);
wIndex = le16_to_cpu (cmd->wIndex); wIndex = le16_to_cpu (cmd->wIndex);
...@@ -347,13 +349,21 @@ static int rh_call_control (struct usb_hcd *hcd, struct urb *urb) ...@@ -347,13 +349,21 @@ static int rh_call_control (struct usb_hcd *hcd, struct urb *urb)
/* DEVICE REQUESTS */ /* DEVICE REQUESTS */
case DeviceRequest | USB_REQ_GET_STATUS: case DeviceRequest | USB_REQ_GET_STATUS:
// DEVICE_REMOTE_WAKEUP ubuf [0] = (hcd->remote_wakeup << USB_DEVICE_REMOTE_WAKEUP)
ubuf [0] = 1; // selfpowered | (1 << USB_DEVICE_SELF_POWERED);
ubuf [1] = 0; ubuf [1] = 0;
/* FALLTHROUGH */ break;
case DeviceOutRequest | USB_REQ_CLEAR_FEATURE: case DeviceOutRequest | USB_REQ_CLEAR_FEATURE:
if (wValue == USB_DEVICE_REMOTE_WAKEUP)
hcd->remote_wakeup = 0;
else
goto error;
break;
case DeviceOutRequest | USB_REQ_SET_FEATURE: case DeviceOutRequest | USB_REQ_SET_FEATURE:
dev_dbg (hcd->self.controller, "no device features yet yet\n"); if (hcd->can_wakeup && wValue == USB_DEVICE_REMOTE_WAKEUP)
hcd->remote_wakeup = 1;
else
goto error;
break; break;
case DeviceRequest | USB_REQ_GET_CONFIGURATION: case DeviceRequest | USB_REQ_GET_CONFIGURATION:
ubuf [0] = 1; ubuf [0] = 1;
...@@ -379,6 +389,8 @@ static int rh_call_control (struct usb_hcd *hcd, struct urb *urb) ...@@ -379,6 +389,8 @@ static int rh_call_control (struct usb_hcd *hcd, struct urb *urb)
bufp = fs_rh_config_descriptor; bufp = fs_rh_config_descriptor;
len = sizeof fs_rh_config_descriptor; len = sizeof fs_rh_config_descriptor;
} }
if (hcd->can_wakeup)
patch_wakeup = 1;
break; break;
case USB_DT_STRING << 8: case USB_DT_STRING << 8:
urb->actual_length = rh_string ( urb->actual_length = rh_string (
...@@ -444,6 +456,11 @@ static int rh_call_control (struct usb_hcd *hcd, struct urb *urb) ...@@ -444,6 +456,11 @@ static int rh_call_control (struct usb_hcd *hcd, struct urb *urb)
urb->actual_length = len; urb->actual_length = len;
// always USB_DIR_IN, toward host // always USB_DIR_IN, toward host
memcpy (ubuf, bufp, len); memcpy (ubuf, bufp, len);
/* report whether RH hardware supports remote wakeup */
if (patch_wakeup)
((struct usb_config_descriptor *)ubuf)->bmAttributes
|= USB_CONFIG_ATT_WAKEUP;
} }
/* any errors get returned through the urb completion */ /* any errors get returned through the urb completion */
...@@ -762,10 +779,22 @@ int usb_register_root_hub (struct usb_device *usb_dev, struct device *parent_dev ...@@ -762,10 +779,22 @@ int usb_register_root_hub (struct usb_device *usb_dev, struct device *parent_dev
set_bit (devnum, usb_dev->bus->devmap.devicemap); set_bit (devnum, usb_dev->bus->devmap.devicemap);
usb_dev->state = USB_STATE_ADDRESS; usb_dev->state = USB_STATE_ADDRESS;
usb_dev->epmaxpacketin[0] = usb_dev->epmaxpacketout[0] = 64;
retval = usb_get_device_descriptor(usb_dev, USB_DT_DEVICE_SIZE);
if (retval != sizeof usb_dev->descriptor) {
dev_dbg (parent_dev, "can't read %s device descriptor %d\n",
usb_dev->dev.bus_id, retval);
return (retval < 0) ? retval : -EMSGSIZE;
}
(void) usb_get_dev (usb_dev);
down (&usb_dev->serialize);
retval = usb_new_device (usb_dev); retval = usb_new_device (usb_dev);
if (retval) if (retval)
dev_err (parent_dev, "can't register root hub for %s, %d\n", dev_err (parent_dev, "can't register root hub for %s, %d\n",
usb_dev->dev.bus_id, retval); usb_dev->dev.bus_id, retval);
up (&usb_dev->serialize);
usb_put_dev (usb_dev);
return retval; return retval;
} }
EXPORT_SYMBOL (usb_register_root_hub); EXPORT_SYMBOL (usb_register_root_hub);
......
...@@ -74,6 +74,8 @@ struct usb_hcd { /* usb_bus.hcpriv points to this */ ...@@ -74,6 +74,8 @@ struct usb_hcd { /* usb_bus.hcpriv points to this */
*/ */
struct hc_driver *driver; /* hw-specific hooks */ struct hc_driver *driver; /* hw-specific hooks */
unsigned saw_irq : 1; unsigned saw_irq : 1;
unsigned can_wakeup:1; /* hw supports wakeup? */
unsigned remote_wakeup:1;/* sw should use wakeup? */
int irq; /* irq allocated */ int irq; /* irq allocated */
void *regs; /* device memory/io */ void *regs; /* device memory/io */
...@@ -94,7 +96,7 @@ struct usb_hcd { /* usb_bus.hcpriv points to this */ ...@@ -94,7 +96,7 @@ struct usb_hcd { /* usb_bus.hcpriv points to this */
# define USB_STATE_RUNNING (__ACTIVE) # define USB_STATE_RUNNING (__ACTIVE)
# define USB_STATE_QUIESCING (__SUSPEND|__TRANSIENT|__ACTIVE) # define USB_STATE_QUIESCING (__SUSPEND|__TRANSIENT|__ACTIVE)
# define USB_STATE_RESUMING (__SUSPEND|__TRANSIENT) # define USB_STATE_RESUMING (__SUSPEND|__TRANSIENT)
# define USB_STATE_SUSPENDED (__SUSPEND) # define HCD_STATE_SUSPENDED (__SUSPEND)
#define HCD_IS_RUNNING(state) ((state) & __ACTIVE) #define HCD_IS_RUNNING(state) ((state) & __ACTIVE)
#define HCD_IS_SUSPENDED(state) ((state) & __SUSPEND) #define HCD_IS_SUSPENDED(state) ((state) & __SUSPEND)
...@@ -344,9 +346,16 @@ extern void usb_deregister_bus (struct usb_bus *); ...@@ -344,9 +346,16 @@ extern void usb_deregister_bus (struct usb_bus *);
extern int usb_register_root_hub (struct usb_device *usb_dev, extern int usb_register_root_hub (struct usb_device *usb_dev,
struct device *parent_dev); struct device *parent_dev);
/* for portability to 2.4, hcds should call this */
static inline int hcd_register_root (struct usb_hcd *hcd) static inline int hcd_register_root (struct usb_hcd *hcd)
{ {
/* hcd->driver->start() reported can_wakeup, probably with
* assistance from board's boot firmware.
* NOTE: normal devices won't enable wakeup by default.
*/
if (hcd->can_wakeup)
dev_dbg (hcd->self.controller, "supports USB remote wakeup\n");
hcd->remote_wakeup = hcd->can_wakeup;
return usb_register_root_hub ( return usb_register_root_hub (
hcd_to_bus (hcd)->root_hub, hcd->self.controller); hcd_to_bus (hcd)->root_hub, hcd->self.controller);
} }
......
This diff is collapsed.
...@@ -209,6 +209,8 @@ struct usb_hub { ...@@ -209,6 +209,8 @@ struct usb_hub {
struct semaphore khubd_sem; struct semaphore khubd_sem;
struct usb_tt tt; /* Transaction Translator */ struct usb_tt tt; /* Transaction Translator */
u8 power_budget; /* in 2mA units; or zero */
unsigned has_indicators:1; unsigned has_indicators:1;
enum hub_led_mode indicator[USB_MAXCHILDREN]; enum hub_led_mode indicator[USB_MAXCHILDREN];
struct work_struct leds; struct work_struct leds;
......
...@@ -717,9 +717,6 @@ void usbfs_remove_device(struct usb_device *dev) ...@@ -717,9 +717,6 @@ void usbfs_remove_device(struct usb_device *dev)
while (!list_empty(&dev->filelist)) { while (!list_empty(&dev->filelist)) {
ds = list_entry(dev->filelist.next, struct dev_state, list); ds = list_entry(dev->filelist.next, struct dev_state, list);
list_del_init(&ds->list); list_del_init(&ds->list);
down_write(&ds->devsem);
ds->dev = NULL;
up_write(&ds->devsem);
if (ds->discsignr) { if (ds->discsignr) {
sinfo.si_signo = SIGPIPE; sinfo.si_signo = SIGPIPE;
sinfo.si_errno = EPIPE; sinfo.si_errno = EPIPE;
......
...@@ -796,10 +796,6 @@ void usb_disable_interface(struct usb_device *dev, struct usb_interface *intf) ...@@ -796,10 +796,6 @@ void usb_disable_interface(struct usb_device *dev, struct usb_interface *intf)
} }
} }
static void release_interface(struct device *dev)
{
}
/* /*
* usb_disable_device - Disable all the endpoints for a USB device * usb_disable_device - Disable all the endpoints for a USB device
* @dev: the device whose endpoints are being disabled * @dev: the device whose endpoints are being disabled
...@@ -835,6 +831,7 @@ void usb_disable_device(struct usb_device *dev, int skip_ep0) ...@@ -835,6 +831,7 @@ void usb_disable_device(struct usb_device *dev, int skip_ep0)
dev_dbg (&dev->dev, "unregistering interface %s\n", dev_dbg (&dev->dev, "unregistering interface %s\n",
interface->dev.bus_id); interface->dev.bus_id);
device_unregister (&interface->dev); device_unregister (&interface->dev);
dev->actconfig->interface[i] = NULL;
} }
dev->actconfig = 0; dev->actconfig = 0;
if (dev->state == USB_STATE_CONFIGURED) if (dev->state == USB_STATE_CONFIGURED)
...@@ -1071,6 +1068,16 @@ int usb_reset_configuration(struct usb_device *dev) ...@@ -1071,6 +1068,16 @@ int usb_reset_configuration(struct usb_device *dev)
return 0; return 0;
} }
static void release_interface(struct device *dev)
{
struct usb_interface *intf = to_usb_interface(dev);
struct usb_interface_cache *intfc =
altsetting_to_usb_interface_cache(intf->altsetting);
kref_put(&intfc->ref);
kfree(intf);
}
/* /*
* usb_set_configuration - Makes a particular device setting be current * usb_set_configuration - Makes a particular device setting be current
* @dev: the device whose configuration is being updated * @dev: the device whose configuration is being updated
...@@ -1109,19 +1116,19 @@ int usb_set_configuration(struct usb_device *dev, int configuration) ...@@ -1109,19 +1116,19 @@ int usb_set_configuration(struct usb_device *dev, int configuration)
{ {
int i, ret; int i, ret;
struct usb_host_config *cp = NULL; struct usb_host_config *cp = NULL;
struct usb_interface **new_interfaces = NULL;
int n, nintf;
/* dev->serialize guards all config changes */ /* dev->serialize guards all config changes */
for (i=0; i<dev->descriptor.bNumConfigurations; i++) { for (i = 0; i < dev->descriptor.bNumConfigurations; i++) {
if (dev->config[i].desc.bConfigurationValue == configuration) { if (dev->config[i].desc.bConfigurationValue == configuration) {
cp = &dev->config[i]; cp = &dev->config[i];
break; break;
} }
} }
if ((!cp && configuration != 0)) { if ((!cp && configuration != 0))
ret = -EINVAL; return -EINVAL;
goto out;
}
/* The USB spec says configuration 0 means unconfigured. /* The USB spec says configuration 0 means unconfigured.
* But if a device includes a configuration numbered 0, * But if a device includes a configuration numbered 0,
...@@ -1130,6 +1137,34 @@ int usb_set_configuration(struct usb_device *dev, int configuration) ...@@ -1130,6 +1137,34 @@ int usb_set_configuration(struct usb_device *dev, int configuration)
if (cp && configuration == 0) if (cp && configuration == 0)
dev_warn(&dev->dev, "config 0 descriptor??\n"); dev_warn(&dev->dev, "config 0 descriptor??\n");
/* Allocate memory for new interfaces before doing anything else,
* so that if we run out then nothing will have changed. */
n = nintf = 0;
if (cp) {
nintf = cp->desc.bNumInterfaces;
new_interfaces = kmalloc(nintf * sizeof(*new_interfaces),
GFP_KERNEL);
if (!new_interfaces) {
dev_err(&dev->dev, "Out of memory");
return -ENOMEM;
}
for (; n < nintf; ++n) {
new_interfaces[n] = kmalloc(
sizeof(struct usb_interface),
GFP_KERNEL);
if (!new_interfaces[n]) {
dev_err(&dev->dev, "Out of memory");
ret = -ENOMEM;
free_interfaces:
while (--n >= 0)
kfree(new_interfaces[n]);
kfree(new_interfaces);
return ret;
}
}
}
/* if it's already configured, clear out old state first. /* if it's already configured, clear out old state first.
* getting rid of old interfaces means unbinding their drivers. * getting rid of old interfaces means unbinding their drivers.
*/ */
...@@ -1139,7 +1174,7 @@ int usb_set_configuration(struct usb_device *dev, int configuration) ...@@ -1139,7 +1174,7 @@ int usb_set_configuration(struct usb_device *dev, int configuration)
if ((ret = usb_control_msg(dev, usb_sndctrlpipe(dev, 0), if ((ret = usb_control_msg(dev, usb_sndctrlpipe(dev, 0),
USB_REQ_SET_CONFIGURATION, 0, configuration, 0, USB_REQ_SET_CONFIGURATION, 0, configuration, 0,
NULL, 0, HZ * USB_CTRL_SET_TIMEOUT)) < 0) NULL, 0, HZ * USB_CTRL_SET_TIMEOUT)) < 0)
goto out; goto free_interfaces;
dev->actconfig = cp; dev->actconfig = cp;
if (!cp) if (!cp)
...@@ -1147,14 +1182,21 @@ int usb_set_configuration(struct usb_device *dev, int configuration) ...@@ -1147,14 +1182,21 @@ int usb_set_configuration(struct usb_device *dev, int configuration)
else { else {
dev->state = USB_STATE_CONFIGURED; dev->state = USB_STATE_CONFIGURED;
/* re-initialize hc/hcd/usbcore interface/endpoint state. /* Initialize the new interface structures and the
* this triggers binding of drivers to interfaces; and * hc/hcd/usbcore interface/endpoint state.
* maybe probe() calls will choose different altsettings.
*/ */
for (i = 0; i < cp->desc.bNumInterfaces; ++i) { for (i = 0; i < nintf; ++i) {
struct usb_interface *intf = cp->interface[i]; struct usb_interface_cache *intfc;
struct usb_interface *intf;
struct usb_host_interface *alt; struct usb_host_interface *alt;
cp->interface[i] = intf = new_interfaces[i];
memset(intf, 0, sizeof(*intf));
intfc = cp->intf_cache[i];
intf->altsetting = intfc->altsetting;
intf->num_altsetting = intfc->num_altsetting;
kref_get(&intfc->ref);
alt = usb_altnum_to_altsetting(intf, 0); alt = usb_altnum_to_altsetting(intf, 0);
/* No altsetting 0? We'll assume the first altsetting. /* No altsetting 0? We'll assume the first altsetting.
...@@ -1178,12 +1220,15 @@ int usb_set_configuration(struct usb_device *dev, int configuration) ...@@ -1178,12 +1220,15 @@ int usb_set_configuration(struct usb_device *dev, int configuration)
configuration, configuration,
alt->desc.bInterfaceNumber); alt->desc.bInterfaceNumber);
} }
kfree(new_interfaces);
/* Now that all interfaces are setup, probe() calls /* Now that all the interfaces are set up, register them
* may claim() any interface that's not yet bound. * to trigger binding of drivers to interfaces. probe()
* Many class drivers need that: CDC, audio, video, etc. * routines may install different altsettings and may
* claim() any interfaces not yet bound. Many class drivers
* need that: CDC, audio, video, etc.
*/ */
for (i = 0; i < cp->desc.bNumInterfaces; ++i) { for (i = 0; i < nintf; ++i) {
struct usb_interface *intf = cp->interface[i]; struct usb_interface *intf = cp->interface[i];
struct usb_interface_descriptor *desc; struct usb_interface_descriptor *desc;
...@@ -1204,7 +1249,6 @@ int usb_set_configuration(struct usb_device *dev, int configuration) ...@@ -1204,7 +1249,6 @@ int usb_set_configuration(struct usb_device *dev, int configuration)
} }
} }
out:
return ret; return ret;
} }
......
...@@ -13,6 +13,14 @@ ...@@ -13,6 +13,14 @@
#include <linux/usb.h> #include <linux/usb.h>
#include "hcd.h" #include "hcd.h"
#define to_urb(d) container_of(d, struct urb, kref)
static void urb_destroy(struct kref *kref)
{
struct urb *urb = to_urb(kref);
kfree(urb);
}
/** /**
* usb_init_urb - initializes a urb so that it can be used by a USB driver * usb_init_urb - initializes a urb so that it can be used by a USB driver
* @urb: pointer to the urb to initialize * @urb: pointer to the urb to initialize
...@@ -31,7 +39,7 @@ void usb_init_urb(struct urb *urb) ...@@ -31,7 +39,7 @@ void usb_init_urb(struct urb *urb)
{ {
if (urb) { if (urb) {
memset(urb, 0, sizeof(*urb)); memset(urb, 0, sizeof(*urb));
urb->count = (atomic_t)ATOMIC_INIT(1); kref_init(&urb->kref, urb_destroy);
spin_lock_init(&urb->lock); spin_lock_init(&urb->lock);
} }
} }
...@@ -80,8 +88,7 @@ struct urb *usb_alloc_urb(int iso_packets, int mem_flags) ...@@ -80,8 +88,7 @@ struct urb *usb_alloc_urb(int iso_packets, int mem_flags)
void usb_free_urb(struct urb *urb) void usb_free_urb(struct urb *urb)
{ {
if (urb) if (urb)
if (atomic_dec_and_test(&urb->count)) kref_put(&urb->kref);
kfree(urb);
} }
/** /**
...@@ -96,11 +103,9 @@ void usb_free_urb(struct urb *urb) ...@@ -96,11 +103,9 @@ void usb_free_urb(struct urb *urb)
*/ */
struct urb * usb_get_urb(struct urb *urb) struct urb * usb_get_urb(struct urb *urb)
{ {
if (urb) { if (urb)
atomic_inc(&urb->count); kref_get(&urb->kref);
return urb; return urb;
} else
return NULL;
} }
...@@ -232,6 +237,8 @@ int usb_submit_urb(struct urb *urb, int mem_flags) ...@@ -232,6 +237,8 @@ int usb_submit_urb(struct urb *urb, int mem_flags)
(dev->state < USB_STATE_DEFAULT) || (dev->state < USB_STATE_DEFAULT) ||
(!dev->bus) || (dev->devnum <= 0)) (!dev->bus) || (dev->devnum <= 0))
return -ENODEV; return -ENODEV;
if (dev->state == USB_STATE_SUSPENDED)
return -EHOSTUNREACH;
if (!(op = dev->bus->op) || !op->submit_urb) if (!(op = dev->bus->op) || !op->submit_urb)
return -ENODEV; return -ENODEV;
......
...@@ -1064,92 +1064,29 @@ static inline void usb_show_string(struct usb_device *dev, char *id, int index) ...@@ -1064,92 +1064,29 @@ static inline void usb_show_string(struct usb_device *dev, char *id, int index)
} }
/* /*
* By the time we get here, we chose a new device address * usb_new_device - perform initial device setup (usbcore-internal)
* and is in the default state. We need to identify the thing and * @dev: newly addressed device (in ADDRESS state)
* get the ball rolling..
* *
* Returns 0 for success, != 0 for error. * This is called with devices which have been enumerated, but not yet
* configured. The device descriptor is available, but not descriptors
* for any device configuration. The caller owns dev->serialize, and
* the device is not visible through sysfs or other filesystem code.
*
* Returns 0 for success (device is configured and listed, with its
* interfaces, in sysfs); else a negative errno value. On error, one
* reference count to the device has been dropped.
* *
* This call is synchronous, and may not be used in an interrupt context. * This call is synchronous, and may not be used in an interrupt context.
* *
* Only the hub driver should ever call this; root hub registration * Only the hub driver should ever call this; root hub registration
* uses it only indirectly. * uses it only indirectly.
*/ */
#define NEW_DEVICE_RETRYS 2
#define SET_ADDRESS_RETRYS 2
int usb_new_device(struct usb_device *dev) int usb_new_device(struct usb_device *dev)
{ {
int err = -EINVAL; int err;
int i; int i;
int j;
int config; int config;
/* USB 2.0 section 5.5.3 talks about ep0 maxpacket ...
* it's fixed size except for full speed devices.
*/
switch (dev->speed) {
case USB_SPEED_HIGH: /* fixed at 64 */
i = 64;
break;
case USB_SPEED_FULL: /* 8, 16, 32, or 64 */
/* to determine the ep0 maxpacket size, read the first 8
* bytes from the device descriptor to get bMaxPacketSize0;
* then correct our initial (small) guess.
*/
// FALLTHROUGH
case USB_SPEED_LOW: /* fixed at 8 */
i = 8;
break;
default:
goto fail;
}
dev->epmaxpacketin [0] = i;
dev->epmaxpacketout[0] = i;
for (i = 0; i < NEW_DEVICE_RETRYS; ++i) {
for (j = 0; j < SET_ADDRESS_RETRYS; ++j) {
err = usb_set_address(dev);
if (err >= 0)
break;
wait_ms(200);
}
if (err < 0) {
dev_err(&dev->dev,
"device not accepting address %d, error %d\n",
dev->devnum, err);
goto fail;
}
wait_ms(10); /* Let the SET_ADDRESS settle */
/* high and low speed devices don't need this... */
err = usb_get_device_descriptor(dev, 8);
if (err >= 8)
break;
wait_ms(100);
}
if (err < 8) {
dev_err(&dev->dev, "device descriptor read/8, error %d\n", err);
goto fail;
}
if (dev->speed == USB_SPEED_FULL) {
usb_disable_endpoint(dev, 0);
usb_endpoint_running(dev, 0, 1);
usb_endpoint_running(dev, 0, 0);
dev->epmaxpacketin [0] = dev->descriptor.bMaxPacketSize0;
dev->epmaxpacketout[0] = dev->descriptor.bMaxPacketSize0;
}
/* USB device state == addressed ... still not usable */
err = usb_get_device_descriptor(dev, sizeof(dev->descriptor));
if (err != (signed)sizeof(dev->descriptor)) {
dev_err(&dev->dev, "device descriptor read/all, error %d\n", err);
goto fail;
}
err = usb_get_configuration(dev); err = usb_get_configuration(dev);
if (err < 0) { if (err < 0) {
dev_err(&dev->dev, "can't read configurations, error %d\n", dev_err(&dev->dev, "can't read configurations, error %d\n",
...@@ -1170,13 +1107,10 @@ int usb_new_device(struct usb_device *dev) ...@@ -1170,13 +1107,10 @@ int usb_new_device(struct usb_device *dev)
usb_show_string(dev, "SerialNumber", dev->descriptor.iSerialNumber); usb_show_string(dev, "SerialNumber", dev->descriptor.iSerialNumber);
#endif #endif
down(&dev->serialize);
/* put device-specific files into sysfs */ /* put device-specific files into sysfs */
err = device_add (&dev->dev); err = device_add (&dev->dev);
if (err) { if (err) {
dev_err(&dev->dev, "can't device_add, error %d\n", err); dev_err(&dev->dev, "can't device_add, error %d\n", err);
up(&dev->serialize);
goto fail; goto fail;
} }
usb_create_driverfs_dev_files (dev); usb_create_driverfs_dev_files (dev);
...@@ -1193,7 +1127,7 @@ int usb_new_device(struct usb_device *dev) ...@@ -1193,7 +1127,7 @@ int usb_new_device(struct usb_device *dev)
/* heuristic: Linux is more likely to have class /* heuristic: Linux is more likely to have class
* drivers, so avoid vendor-specific interfaces. * drivers, so avoid vendor-specific interfaces.
*/ */
desc = &dev->config[i].interface[0] desc = &dev->config[i].intf_cache[0]
->altsetting->desc; ->altsetting->desc;
if (desc->bInterfaceClass == USB_CLASS_VENDOR_SPEC) if (desc->bInterfaceClass == USB_CLASS_VENDOR_SPEC)
continue; continue;
...@@ -1211,7 +1145,6 @@ int usb_new_device(struct usb_device *dev) ...@@ -1211,7 +1145,6 @@ int usb_new_device(struct usb_device *dev)
dev->descriptor.bNumConfigurations); dev->descriptor.bNumConfigurations);
} }
err = usb_set_configuration(dev, config); err = usb_set_configuration(dev, config);
up(&dev->serialize);
if (err) { if (err) {
dev_err(&dev->dev, "can't set config #%d, error %d\n", dev_err(&dev->dev, "can't set config #%d, error %d\n",
config, err); config, err);
...@@ -1226,9 +1159,10 @@ int usb_new_device(struct usb_device *dev) ...@@ -1226,9 +1159,10 @@ int usb_new_device(struct usb_device *dev)
return 0; return 0;
fail: fail:
dev->state = USB_STATE_DEFAULT; dev->state = USB_STATE_NOTATTACHED;
clear_bit(dev->devnum, dev->bus->devmap.devicemap); clear_bit(dev->devnum, dev->bus->devmap.devicemap);
dev->devnum = -1; dev->devnum = -1;
usb_put_dev(dev);
return err; return err;
} }
...@@ -1577,20 +1511,40 @@ int usb_disabled(void) ...@@ -1577,20 +1511,40 @@ int usb_disabled(void)
*/ */
static int __init usb_init(void) static int __init usb_init(void)
{ {
int retval;
if (nousb) { if (nousb) {
pr_info ("%s: USB support disabled\n", usbcore_name); pr_info ("%s: USB support disabled\n", usbcore_name);
return 0; return 0;
} }
bus_register(&usb_bus_type); retval = bus_register(&usb_bus_type);
if (retval)
goto out;
usb_host_init(); usb_host_init();
usb_major_init(); retval = usb_major_init();
usbfs_init(); if (retval)
usb_hub_init(); goto major_init_failed;
retval = usbfs_init();
driver_register(&usb_generic_driver); if (retval)
goto fs_init_failed;
retval = usb_hub_init();
if (retval)
goto hub_init_failed;
retval = driver_register(&usb_generic_driver);
if (!retval)
goto out;
return 0; usb_hub_cleanup();
hub_init_failed:
usbfs_cleanup();
fs_init_failed:
usb_major_cleanup();
major_init_failed:
usb_host_cleanup();
bus_unregister(&usb_bus_type);
out:
return retval;
} }
/* /*
......
...@@ -144,6 +144,7 @@ struct dummy { ...@@ -144,6 +144,7 @@ struct dummy {
struct usb_gadget_driver *driver; struct usb_gadget_driver *driver;
struct dummy_request fifo_req; struct dummy_request fifo_req;
u8 fifo_buf [FIFO_SIZE]; u8 fifo_buf [FIFO_SIZE];
u16 devstatus;
struct hcd_dev *hdev; struct hcd_dev *hdev;
...@@ -156,6 +157,8 @@ struct dummy { ...@@ -156,6 +157,8 @@ struct dummy {
u32 port_status; u32 port_status;
int started; int started;
struct completion released; struct completion released;
unsigned resuming:1;
unsigned long re_timeout;
}; };
static struct dummy *the_controller; static struct dummy *the_controller;
...@@ -556,8 +559,37 @@ static int dummy_g_get_frame (struct usb_gadget *_gadget) ...@@ -556,8 +559,37 @@ static int dummy_g_get_frame (struct usb_gadget *_gadget)
return tv.tv_usec / 1000; return tv.tv_usec / 1000;
} }
static int dummy_wakeup (struct usb_gadget *_gadget)
{
struct dummy *dum;
dum = container_of (_gadget, struct dummy, gadget);
if ((dum->devstatus & (1 << USB_DEVICE_REMOTE_WAKEUP)) == 0
|| !(dum->port_status & (1 << USB_PORT_FEAT_SUSPEND)))
return -EINVAL;
/* hub notices our request, issues downstream resume, etc */
dum->resuming = 1;
dum->port_status |= (1 << USB_PORT_FEAT_C_SUSPEND);
return 0;
}
static int dummy_set_selfpowered (struct usb_gadget *_gadget, int value)
{
struct dummy *dum;
dum = container_of (_gadget, struct dummy, gadget);
if (value)
dum->devstatus |= (1 << USB_DEVICE_SELF_POWERED);
else
dum->devstatus &= ~(1 << USB_DEVICE_SELF_POWERED);
return 0;
}
static const struct usb_gadget_ops dummy_ops = { static const struct usb_gadget_ops dummy_ops = {
.get_frame = dummy_g_get_frame, .get_frame = dummy_g_get_frame,
.wakeup = dummy_wakeup,
.set_selfpowered = dummy_set_selfpowered,
}; };
/*-------------------------------------------------------------------------*/ /*-------------------------------------------------------------------------*/
...@@ -653,6 +685,9 @@ usb_gadget_register_driver (struct usb_gadget_driver *driver) ...@@ -653,6 +685,9 @@ usb_gadget_register_driver (struct usb_gadget_driver *driver)
dum->gadget.ops = &dummy_ops; dum->gadget.ops = &dummy_ops;
dum->gadget.is_dualspeed = 1; dum->gadget.is_dualspeed = 1;
dum->devstatus = 0;
dum->resuming = 0;
INIT_LIST_HEAD (&dum->gadget.ep_list); INIT_LIST_HEAD (&dum->gadget.ep_list);
for (i = 0; i < DUMMY_ENDPOINTS; i++) { for (i = 0; i < DUMMY_ENDPOINTS; i++) {
struct dummy_ep *ep = &dum->ep [i]; struct dummy_ep *ep = &dum->ep [i];
...@@ -1130,8 +1165,19 @@ static void dummy_timer (unsigned long _dum) ...@@ -1130,8 +1165,19 @@ static void dummy_timer (unsigned long _dum)
break; break;
case USB_REQ_SET_FEATURE: case USB_REQ_SET_FEATURE:
if (setup.bRequestType == Dev_Request) { if (setup.bRequestType == Dev_Request) {
// remote wakeup, and (hs) test mode value = 0;
value = -EOPNOTSUPP; switch (setup.wValue) {
case USB_DEVICE_REMOTE_WAKEUP:
break;
default:
value = -EOPNOTSUPP;
}
if (value == 0) {
dum->devstatus |=
(1 << setup.wValue);
maybe_set_status (urb, 0);
}
} else if (setup.bRequestType == Ep_Request) { } else if (setup.bRequestType == Ep_Request) {
// endpoint halt // endpoint halt
ep2 = find_endpoint (dum, ep2 = find_endpoint (dum,
...@@ -1147,9 +1193,17 @@ static void dummy_timer (unsigned long _dum) ...@@ -1147,9 +1193,17 @@ static void dummy_timer (unsigned long _dum)
break; break;
case USB_REQ_CLEAR_FEATURE: case USB_REQ_CLEAR_FEATURE:
if (setup.bRequestType == Dev_Request) { if (setup.bRequestType == Dev_Request) {
// remote wakeup switch (setup.wValue) {
value = 0; case USB_DEVICE_REMOTE_WAKEUP:
maybe_set_status (urb, 0); dum->devstatus &= ~(1 <<
USB_DEVICE_REMOTE_WAKEUP);
value = 0;
maybe_set_status (urb, 0);
break;
default:
value = -EOPNOTSUPP;
break;
}
} else if (setup.bRequestType == Ep_Request) { } else if (setup.bRequestType == Ep_Request) {
// endpoint halt // endpoint halt
ep2 = find_endpoint (dum, ep2 = find_endpoint (dum,
...@@ -1185,6 +1239,10 @@ static void dummy_timer (unsigned long _dum) ...@@ -1185,6 +1239,10 @@ static void dummy_timer (unsigned long _dum)
break; break;
} }
buf [0] = ep2->halted; buf [0] = ep2->halted;
} else if (setup.bRequestType ==
Dev_InRequest) {
buf [0] = (u8)
dum->devstatus;
} else } else
buf [0] = 0; buf [0] = 0;
} }
...@@ -1338,8 +1396,21 @@ static int dummy_hub_control ( ...@@ -1338,8 +1396,21 @@ static int dummy_hub_control (
case ClearHubFeature: case ClearHubFeature:
break; break;
case ClearPortFeature: case ClearPortFeature:
// FIXME won't some of these need special handling? switch (wValue) {
dum->port_status &= ~(1 << wValue); case USB_PORT_FEAT_SUSPEND:
/* 20msec resume signaling */
dum->resuming = 1;
dum->re_timeout = jiffies + ((HZ * 20)/1000);
break;
case USB_PORT_FEAT_POWER:
dum->port_status = 0;
dum->address = 0;
dum->hdev = 0;
dum->resuming = 0;
break;
default:
dum->port_status &= ~(1 << wValue);
}
break; break;
case GetHubDescriptor: case GetHubDescriptor:
hub_descriptor ((struct usb_hub_descriptor *) buf); hub_descriptor ((struct usb_hub_descriptor *) buf);
...@@ -1350,33 +1421,28 @@ static int dummy_hub_control ( ...@@ -1350,33 +1421,28 @@ static int dummy_hub_control (
case GetPortStatus: case GetPortStatus:
if (wIndex != 1) if (wIndex != 1)
retval = -EPIPE; retval = -EPIPE;
((u16 *) buf)[0] = cpu_to_le16 (dum->port_status);
((u16 *) buf)[1] = cpu_to_le16 (dum->port_status >> 16);
break;
case SetHubFeature:
retval = -EPIPE;
break;
case SetPortFeature:
if (wValue == USB_PORT_FEAT_RESET) {
/* if it's already running, disconnect first */
if (dum->port_status & USB_PORT_STAT_ENABLE) {
dum->port_status &= ~(USB_PORT_STAT_ENABLE
| USB_PORT_STAT_LOW_SPEED
| USB_PORT_STAT_HIGH_SPEED);
if (dum->driver) {
dev_dbg (hardware, "disconnect\n");
stop_activity (dum, dum->driver);
}
/* FIXME test that code path! */ /* whoever resets or resumes must GetPortStatus to
} else * complete it!!
dum->port_status |= */
(1 << USB_PORT_FEAT_C_ENABLE); if (dum->resuming && time_after (jiffies, dum->re_timeout)) {
dum->port_status |= (1 << USB_PORT_FEAT_C_SUSPEND);
dum->port_status |= USB_PORT_STAT_ENABLE | dum->port_status &= ~(1 << USB_PORT_FEAT_SUSPEND);
(1 << USB_PORT_FEAT_C_RESET); dum->resuming = 0;
dum->re_timeout = 0;
if (dum->driver->resume) {
spin_unlock (&dum->lock);
dum->driver->resume (&dum->gadget);
spin_lock (&dum->lock);
}
}
if ((dum->port_status & (1 << USB_PORT_FEAT_RESET)) != 0
&& time_after (jiffies, dum->re_timeout)) {
dum->port_status |= (1 << USB_PORT_FEAT_C_RESET);
dum->port_status &= ~(1 << USB_PORT_FEAT_RESET);
dum->re_timeout = 0;
if (dum->driver) { if (dum->driver) {
dum->port_status |= USB_PORT_STAT_ENABLE;
/* give it the best speed we agree on */ /* give it the best speed we agree on */
dum->gadget.speed = dum->driver->speed; dum->gadget.speed = dum->driver->speed;
dum->gadget.ep0->maxpacket = 64; dum->gadget.ep0->maxpacket = 64;
...@@ -1395,8 +1461,42 @@ static int dummy_hub_control ( ...@@ -1395,8 +1461,42 @@ static int dummy_hub_control (
break; break;
} }
} }
} else }
((u16 *) buf)[0] = cpu_to_le16 (dum->port_status);
((u16 *) buf)[1] = cpu_to_le16 (dum->port_status >> 16);
break;
case SetHubFeature:
retval = -EPIPE;
break;
case SetPortFeature:
switch (wValue) {
case USB_PORT_FEAT_SUSPEND:
dum->port_status |= (1 << USB_PORT_FEAT_SUSPEND);
if (dum->driver->suspend) {
spin_unlock (&dum->lock);
dum->driver->suspend (&dum->gadget);
spin_lock (&dum->lock);
}
break;
case USB_PORT_FEAT_RESET:
/* if it's already running, disconnect first */
if (dum->port_status & USB_PORT_STAT_ENABLE) {
dum->port_status &= ~(USB_PORT_STAT_ENABLE
| USB_PORT_STAT_LOW_SPEED
| USB_PORT_STAT_HIGH_SPEED);
if (dum->driver) {
dev_dbg (hardware, "disconnect\n");
stop_activity (dum, dum->driver);
}
/* FIXME test that code path! */
}
/* 50msec reset signaling */
dum->re_timeout = jiffies + ((HZ * 50)/1000);
/* FALLTHROUGH */
default:
dum->port_status |= (1 << wValue); dum->port_status |= (1 << wValue);
}
break; break;
default: default:
......
...@@ -2054,7 +2054,7 @@ static int do_request_sense(struct fsg_dev *fsg, struct fsg_buffhd *bh) ...@@ -2054,7 +2054,7 @@ static int do_request_sense(struct fsg_dev *fsg, struct fsg_buffhd *bh)
buf[0] = 0x80 | 0x70; // Valid, current error buf[0] = 0x80 | 0x70; // Valid, current error
buf[2] = SK(sd); buf[2] = SK(sd);
put_be32(&buf[3], sdinfo); // Sense information put_be32(&buf[3], sdinfo); // Sense information
buf[7] = 18 - 7; // Additional sense length buf[7] = 18 - 8; // Additional sense length
buf[12] = ASC(sd); buf[12] = ASC(sd);
buf[13] = ASCQ(sd); buf[13] = ASCQ(sd);
return 18; return 18;
......
...@@ -20,7 +20,7 @@ ...@@ -20,7 +20,7 @@
#define gadget_is_dummy(g) 0 #define gadget_is_dummy(g) 0
#endif #endif
#ifdef CONFIG_USB_GADGET_PXA #ifdef CONFIG_USB_GADGET_PXA2XX
#define gadget_is_pxa(g) !strcmp("pxa2xx_udc", (g)->name) #define gadget_is_pxa(g) !strcmp("pxa2xx_udc", (g)->name)
#else #else
#define gadget_is_pxa(g) 0 #define gadget_is_pxa(g) 0
......
...@@ -157,8 +157,12 @@ do { \ ...@@ -157,8 +157,12 @@ do { \
/* debug macro */ /* debug macro */
#if G_SERIAL_DEBUG #if G_SERIAL_DEBUG
static int debug = G_SERIAL_DEBUG; static int debug = G_SERIAL_DEBUG;
#else
static int debug = 0;
#endif
#if G_SERIAL_DEBUG
#define gs_debug(format, arg...) \ #define gs_debug(format, arg...) \
do { if (debug) printk(KERN_DEBUG format, ## arg); } while(0) do { if (debug) printk(KERN_DEBUG format, ## arg); } while(0)
......
...@@ -130,6 +130,9 @@ struct zero_dev { ...@@ -130,6 +130,9 @@ struct zero_dev {
*/ */
u8 config; u8 config;
struct usb_ep *in_ep, *out_ep; struct usb_ep *in_ep, *out_ep;
/* autoresume timer */
struct timer_list resume;
}; };
#define xprintk(d,level,fmt,args...) \ #define xprintk(d,level,fmt,args...) \
...@@ -167,6 +170,12 @@ module_param (buflen, uint, S_IRUGO|S_IWUSR); ...@@ -167,6 +170,12 @@ module_param (buflen, uint, S_IRUGO|S_IWUSR);
module_param (qlen, uint, S_IRUGO|S_IWUSR); module_param (qlen, uint, S_IRUGO|S_IWUSR);
module_param (pattern, uint, S_IRUGO|S_IWUSR); module_param (pattern, uint, S_IRUGO|S_IWUSR);
/*
* if it's nonzero, autoresume says how many seconds to wait
* before trying to wake up the host after suspend.
*/
static unsigned autoresume = 0;
module_param (autoresume, uint, 0);
/* /*
* Normally the "loopback" configuration is second (index 1) so * Normally the "loopback" configuration is second (index 1) so
...@@ -224,7 +233,7 @@ device_desc = { ...@@ -224,7 +233,7 @@ device_desc = {
.bNumConfigurations = 2, .bNumConfigurations = 2,
}; };
static const struct usb_config_descriptor static struct usb_config_descriptor
source_sink_config = { source_sink_config = {
.bLength = sizeof source_sink_config, .bLength = sizeof source_sink_config,
.bDescriptorType = USB_DT_CONFIG, .bDescriptorType = USB_DT_CONFIG,
...@@ -237,7 +246,7 @@ source_sink_config = { ...@@ -237,7 +246,7 @@ source_sink_config = {
.bMaxPower = 1, /* self-powered */ .bMaxPower = 1, /* self-powered */
}; };
static const struct usb_config_descriptor static struct usb_config_descriptor
loopback_config = { loopback_config = {
.bLength = sizeof loopback_config, .bLength = sizeof loopback_config,
.bDescriptorType = USB_DT_CONFIG, .bDescriptorType = USB_DT_CONFIG,
...@@ -1060,6 +1069,19 @@ zero_disconnect (struct usb_gadget *gadget) ...@@ -1060,6 +1069,19 @@ zero_disconnect (struct usb_gadget *gadget)
*/ */
} }
static void
zero_autoresume (unsigned long _dev)
{
struct zero_dev *dev = (struct zero_dev *) _dev;
int status;
/* normally the host would be woken up for something
* more significant than just a timer firing...
*/
status = usb_gadget_wakeup (dev->gadget);
DBG (dev, "wakeup --> %d\n", status);
}
/*-------------------------------------------------------------------------*/ /*-------------------------------------------------------------------------*/
static void static void
...@@ -1072,6 +1094,7 @@ zero_unbind (struct usb_gadget *gadget) ...@@ -1072,6 +1094,7 @@ zero_unbind (struct usb_gadget *gadget)
/* we've already been disconnected ... no i/o is active */ /* we've already been disconnected ... no i/o is active */
if (dev->req) if (dev->req)
free_ep_req (gadget->ep0, dev->req); free_ep_req (gadget->ep0, dev->req);
del_timer_sync (&dev->resume);
kfree (dev); kfree (dev);
set_gadget_data (gadget, 0); set_gadget_data (gadget, 0);
} }
...@@ -1176,6 +1199,14 @@ zero_bind (struct usb_gadget *gadget) ...@@ -1176,6 +1199,14 @@ zero_bind (struct usb_gadget *gadget)
usb_gadget_set_selfpowered (gadget); usb_gadget_set_selfpowered (gadget);
init_timer (&dev->resume);
dev->resume.function = zero_autoresume;
dev->resume.data = (unsigned long) dev;
if (autoresume) {
source_sink_config.bmAttributes |= USB_CONFIG_ATT_WAKEUP;
loopback_config.bmAttributes |= USB_CONFIG_ATT_WAKEUP;
}
gadget->ep0->driver_data = dev; gadget->ep0->driver_data = dev;
INFO (dev, "%s, version: " DRIVER_VERSION "\n", longname); INFO (dev, "%s, version: " DRIVER_VERSION "\n", longname);
...@@ -1193,6 +1224,33 @@ zero_bind (struct usb_gadget *gadget) ...@@ -1193,6 +1224,33 @@ zero_bind (struct usb_gadget *gadget)
return -ENOMEM; return -ENOMEM;
} }
/*-------------------------------------------------------------------------*/
static void
zero_suspend (struct usb_gadget *gadget)
{
struct zero_dev *dev = get_gadget_data (gadget);
if (gadget->speed == USB_SPEED_UNKNOWN)
return;
if (autoresume) {
mod_timer (&dev->resume, jiffies + (HZ * autoresume));
DBG (dev, "suspend, wakeup in %d seconds\n", autoresume);
} else
DBG (dev, "suspend\n");
}
static void
zero_resume (struct usb_gadget *gadget)
{
struct zero_dev *dev = get_gadget_data (gadget);
DBG (dev, "resume\n");
del_timer (&dev->resume);
}
/*-------------------------------------------------------------------------*/ /*-------------------------------------------------------------------------*/
static struct usb_gadget_driver zero_driver = { static struct usb_gadget_driver zero_driver = {
...@@ -1208,6 +1266,9 @@ static struct usb_gadget_driver zero_driver = { ...@@ -1208,6 +1266,9 @@ static struct usb_gadget_driver zero_driver = {
.setup = zero_setup, .setup = zero_setup,
.disconnect = zero_disconnect, .disconnect = zero_disconnect,
.suspend = zero_suspend,
.resume = zero_resume,
.driver = { .driver = {
.name = (char *) shortname, .name = (char *) shortname,
// .shutdown = ... // .shutdown = ...
......
...@@ -170,6 +170,20 @@ dbg_itd (const char *label, struct ehci_hcd *ehci, struct ehci_itd *itd) ...@@ -170,6 +170,20 @@ dbg_itd (const char *label, struct ehci_hcd *ehci, struct ehci_itd *itd)
itd->index[6], itd->index[7]); itd->index[6], itd->index[7]);
} }
static void __attribute__((__unused__))
dbg_sitd (const char *label, struct ehci_hcd *ehci, struct ehci_sitd *sitd)
{
ehci_dbg (ehci, "%s [%d] sitd %p, next %08x, urb %p\n",
label, sitd->frame, sitd, le32_to_cpu(sitd->hw_next), sitd->urb);
ehci_dbg (ehci,
" addr %08x sched %04x result %08x buf %08x %08x\n",
le32_to_cpu(sitd->hw_fullspeed_ep),
le32_to_cpu(sitd->hw_uframe),
le32_to_cpu(sitd->hw_results),
le32_to_cpu(sitd->hw_buf [0]),
le32_to_cpu(sitd->hw_buf [1]));
}
static int __attribute__((__unused__)) static int __attribute__((__unused__))
dbg_status_buf (char *buf, unsigned len, char *label, u32 status) dbg_status_buf (char *buf, unsigned len, char *label, u32 status)
{ {
...@@ -625,11 +639,20 @@ show_registers (struct class_device *class_dev, char *buf) ...@@ -625,11 +639,20 @@ show_registers (struct class_device *class_dev, char *buf)
spin_lock_irqsave (&ehci->lock, flags); spin_lock_irqsave (&ehci->lock, flags);
if (bus->controller->power.power_state) {
size = scnprintf (next, size,
"bus %s, device %s (driver " DRIVER_VERSION ")\n"
"SUSPENDED (no register access)\n",
hcd->self.controller->bus->name,
hcd->self.controller->bus_id);
goto done;
}
/* Capability Registers */ /* Capability Registers */
i = HC_VERSION(readl (&ehci->caps->hc_capbase)); i = HC_VERSION(readl (&ehci->caps->hc_capbase));
temp = scnprintf (next, size, temp = scnprintf (next, size,
"bus %s device %s\n" "bus %s, device %s (driver " DRIVER_VERSION ")\n"
"EHCI %x.%02x, hcd state %d (driver " DRIVER_VERSION ")\n", "EHCI %x.%02x, hcd state %d\n",
hcd->self.controller->bus->name, hcd->self.controller->bus->name,
hcd->self.controller->bus_id, hcd->self.controller->bus_id,
i >> 8, i & 0x0ff, ehci->hcd.state); i >> 8, i & 0x0ff, ehci->hcd.state);
...@@ -672,7 +695,7 @@ show_registers (struct class_device *class_dev, char *buf) ...@@ -672,7 +695,7 @@ show_registers (struct class_device *class_dev, char *buf)
next += temp; next += temp;
for (i = 0; i < HCS_N_PORTS (ehci->hcs_params); i++) { for (i = 0; i < HCS_N_PORTS (ehci->hcs_params); i++) {
temp = dbg_port_buf (scratch, sizeof scratch, label, i, temp = dbg_port_buf (scratch, sizeof scratch, label, i + 1,
readl (&ehci->regs->port_status [i])); readl (&ehci->regs->port_status [i]));
temp = scnprintf (next, size, fmt, temp, scratch); temp = scnprintf (next, size, fmt, temp, scratch);
size -= temp; size -= temp;
...@@ -701,6 +724,7 @@ show_registers (struct class_device *class_dev, char *buf) ...@@ -701,6 +724,7 @@ show_registers (struct class_device *class_dev, char *buf)
next += temp; next += temp;
#endif #endif
done:
spin_unlock_irqrestore (&ehci->lock, flags); spin_unlock_irqrestore (&ehci->lock, flags);
return PAGE_SIZE - size; return PAGE_SIZE - size;
......
/* /*
* Copyright (c) 2000-2002 by David Brownell * Copyright (c) 2000-2004 by David Brownell
* *
* This program is free software; you can redistribute it and/or modify it * This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the * under the terms of the GNU General Public License as published by the
...@@ -69,6 +69,7 @@ ...@@ -69,6 +69,7 @@
* *
* HISTORY: * HISTORY:
* *
* 2004-05-10 Root hub and PCI suspend/resume support; remote wakeup. (db)
* 2004-02-24 Replace pci_* with generic dma_* API calls (dsaxena@plexity.net) * 2004-02-24 Replace pci_* with generic dma_* API calls (dsaxena@plexity.net)
* 2003-12-29 Rewritten high speed iso transfer support (by Michal Sojka, * 2003-12-29 Rewritten high speed iso transfer support (by Michal Sojka,
* <sojkam@centrum.cz>, updates by DB). * <sojkam@centrum.cz>, updates by DB).
...@@ -96,7 +97,7 @@ ...@@ -96,7 +97,7 @@
* 2001-June Works with usb-storage and NEC EHCI on 2.4 * 2001-June Works with usb-storage and NEC EHCI on 2.4
*/ */
#define DRIVER_VERSION "2003-Dec-29" #define DRIVER_VERSION "2004-May-10"
#define DRIVER_AUTHOR "David Brownell" #define DRIVER_AUTHOR "David Brownell"
#define DRIVER_DESC "USB 2.0 'Enhanced' Host Controller (EHCI) Driver" #define DRIVER_DESC "USB 2.0 'Enhanced' Host Controller (EHCI) Driver"
...@@ -128,7 +129,7 @@ static int log2_irq_thresh = 0; // 0 to 6 ...@@ -128,7 +129,7 @@ static int log2_irq_thresh = 0; // 0 to 6
module_param (log2_irq_thresh, int, S_IRUGO); module_param (log2_irq_thresh, int, S_IRUGO);
MODULE_PARM_DESC (log2_irq_thresh, "log2 IRQ latency, 1-64 microframes"); MODULE_PARM_DESC (log2_irq_thresh, "log2 IRQ latency, 1-64 microframes");
#define INTR_MASK (STS_IAA | STS_FATAL | STS_ERR | STS_INT) #define INTR_MASK (STS_IAA | STS_FATAL | STS_PCD | STS_ERR | STS_INT)
/*-------------------------------------------------------------------------*/ /*-------------------------------------------------------------------------*/
...@@ -201,6 +202,7 @@ static int ehci_reset (struct ehci_hcd *ehci) ...@@ -201,6 +202,7 @@ static int ehci_reset (struct ehci_hcd *ehci)
dbg_cmd (ehci, "reset", command); dbg_cmd (ehci, "reset", command);
writel (command, &ehci->regs->command); writel (command, &ehci->regs->command);
ehci->hcd.state = USB_STATE_HALT; ehci->hcd.state = USB_STATE_HALT;
ehci->next_statechange = jiffies;
return handshake (&ehci->regs->command, CMD_RESET, 0, 250 * 1000); return handshake (&ehci->regs->command, CMD_RESET, 0, 250 * 1000);
} }
...@@ -241,6 +243,8 @@ static void ehci_ready (struct ehci_hcd *ehci) ...@@ -241,6 +243,8 @@ static void ehci_ready (struct ehci_hcd *ehci)
/*-------------------------------------------------------------------------*/ /*-------------------------------------------------------------------------*/
static void ehci_work(struct ehci_hcd *ehci, struct pt_regs *regs);
#include "ehci-hub.c" #include "ehci-hub.c"
#include "ehci-mem.c" #include "ehci-mem.c"
#include "ehci-q.c" #include "ehci-q.c"
...@@ -248,8 +252,6 @@ static void ehci_ready (struct ehci_hcd *ehci) ...@@ -248,8 +252,6 @@ static void ehci_ready (struct ehci_hcd *ehci)
/*-------------------------------------------------------------------------*/ /*-------------------------------------------------------------------------*/
static void ehci_work(struct ehci_hcd *ehci, struct pt_regs *regs);
static void ehci_watchdog (unsigned long param) static void ehci_watchdog (unsigned long param)
{ {
struct ehci_hcd *ehci = (struct ehci_hcd *) param; struct ehci_hcd *ehci = (struct ehci_hcd *) param;
...@@ -428,12 +430,17 @@ static int ehci_start (struct usb_hcd *hcd) ...@@ -428,12 +430,17 @@ static int ehci_start (struct usb_hcd *hcd)
#ifdef CONFIG_PCI #ifdef CONFIG_PCI
if (hcd->self.controller->bus == &pci_bus_type) { if (hcd->self.controller->bus == &pci_bus_type) {
struct pci_dev *pdev; struct pci_dev *pdev;
u16 port_wake;
pdev = to_pci_dev(hcd->self.controller); pdev = to_pci_dev(hcd->self.controller);
/* Serial Bus Release Number is at PCI 0x60 offset */ /* Serial Bus Release Number is at PCI 0x60 offset */
pci_read_config_byte(pdev, 0x60, &sbrn); pci_read_config_byte(pdev, 0x60, &sbrn);
/* port wake capability, reported by boot firmware */
pci_read_config_word(pdev, 0x62, &port_wake);
hcd->can_wakeup = (port_wake & 1) != 0;
/* help hc dma work well with cachelines */ /* help hc dma work well with cachelines */
pci_set_mwi (pdev); pci_set_mwi (pdev);
...@@ -615,41 +622,26 @@ static int ehci_get_frame (struct usb_hcd *hcd) ...@@ -615,41 +622,26 @@ static int ehci_get_frame (struct usb_hcd *hcd)
/* suspend/resume, section 4.3 */ /* suspend/resume, section 4.3 */
/* These routines rely on PCI to handle powerdown and wakeup, and
* transceivers that don't need any software attention to set up
* the right sort of wakeup.
*/
static int ehci_suspend (struct usb_hcd *hcd, u32 state) static int ehci_suspend (struct usb_hcd *hcd, u32 state)
{ {
struct ehci_hcd *ehci = hcd_to_ehci (hcd); struct ehci_hcd *ehci = hcd_to_ehci (hcd);
int ports;
int i;
ehci_dbg (ehci, "suspend to %d\n", state);
ports = HCS_N_PORTS (ehci->hcs_params);
// FIXME: This assumes what's probably a D3 level suspend... while (time_before (jiffies, ehci->next_statechange))
msec_delay (100);
// FIXME: usb wakeup events on this bus should resume the machine. #ifdef CONFIG_USB_SUSPEND
// pci config register PORTWAKECAP controls which ports can do it; (void) usb_suspend_device (hcd->self.root_hub);
// bios may have initted the register... #else
/* FIXME lock root hub */
/* suspend each port, then stop the hc */ (void) ehci_hub_suspend (hcd);
for (i = 0; i < ports; i++) { #endif
int temp = readl (&ehci->regs->port_status [i]);
if ((temp & PORT_PE) == 0
|| (temp & PORT_OWNER) != 0)
continue;
ehci_dbg (ehci, "suspend port %d", i);
temp |= PORT_SUSPEND;
writel (temp, &ehci->regs->port_status [i]);
}
if (hcd->state == USB_STATE_RUNNING)
ehci_ready (ehci);
writel (readl (&ehci->regs->command) & ~CMD_RUN, &ehci->regs->command);
// save pci FLADJ value
/* who tells PCI to reduce power consumption? */ // save (PCI) FLADJ in case of Vaux power loss
return 0; return 0;
} }
...@@ -657,40 +649,22 @@ static int ehci_suspend (struct usb_hcd *hcd, u32 state) ...@@ -657,40 +649,22 @@ static int ehci_suspend (struct usb_hcd *hcd, u32 state)
static int ehci_resume (struct usb_hcd *hcd) static int ehci_resume (struct usb_hcd *hcd)
{ {
struct ehci_hcd *ehci = hcd_to_ehci (hcd); struct ehci_hcd *ehci = hcd_to_ehci (hcd);
int ports; int retval;
int i;
// maybe restore (PCI) FLADJ
ehci_dbg (ehci, "resume\n");
while (time_before (jiffies, ehci->next_statechange))
ports = HCS_N_PORTS (ehci->hcs_params); msec_delay (100);
// FIXME: if controller didn't retain state, #ifdef CONFIG_USB_SUSPEND
// return and let generic code clean it up retval = usb_resume_device (hcd->self.root_hub);
// test configured_flag ? #else
/* FIXME lock root hub */
/* resume HC and each port */ retval = ehci_hub_resume (hcd);
// restore pci FLADJ value #endif
// khubd and drivers will set HC running, if needed; if (retval == 0)
hcd->state = USB_STATE_RUNNING; hcd->self.controller->power.power_state = 0;
// FIXME Philips/Intel/... etc don't really have a "READY" return retval;
// state ... turn on CMD_RUN too
for (i = 0; i < ports; i++) {
int temp = readl (&ehci->regs->port_status [i]);
if ((temp & PORT_PE) == 0
|| (temp & PORT_SUSPEND) != 0)
continue;
ehci_dbg (ehci, "resume port %d", i);
temp |= PORT_RESUME;
writel (temp, &ehci->regs->port_status [i]);
readl (&ehci->regs->command); /* unblock posted writes */
wait_ms (20);
temp &= ~PORT_RESUME;
writel (temp, &ehci->regs->port_status [i]);
}
readl (&ehci->regs->command); /* unblock posted writes */
return 0;
} }
#endif #endif
...@@ -752,7 +726,7 @@ static irqreturn_t ehci_irq (struct usb_hcd *hcd, struct pt_regs *regs) ...@@ -752,7 +726,7 @@ static irqreturn_t ehci_irq (struct usb_hcd *hcd, struct pt_regs *regs)
bh = 0; bh = 0;
#ifdef EHCI_VERBOSE_DEBUG #ifdef EHCI_VERBOSE_DEBUG
/* unrequested/ignored: Port Change Detect, Frame List Rollover */ /* unrequested/ignored: Frame List Rollover */
dbg_status (ehci, "irq", status); dbg_status (ehci, "irq", status);
#endif #endif
...@@ -774,6 +748,34 @@ static irqreturn_t ehci_irq (struct usb_hcd *hcd, struct pt_regs *regs) ...@@ -774,6 +748,34 @@ static irqreturn_t ehci_irq (struct usb_hcd *hcd, struct pt_regs *regs)
bh = 1; bh = 1;
} }
/* remote wakeup [4.3.1] */
if ((status & STS_PCD) && ehci->hcd.remote_wakeup) {
unsigned i = HCS_N_PORTS (ehci->hcs_params);
/* resume root hub? */
status = readl (&ehci->regs->command);
if (!(status & CMD_RUN))
writel (status | CMD_RUN, &ehci->regs->command);
while (i--) {
status = readl (&ehci->regs->port_status [i]);
if (status & PORT_OWNER)
continue;
if (!(status & PORT_RESUME)
|| ehci->reset_done [i] != 0)
continue;
/* start 20 msec resume signaling from this port,
* and make khubd collect PORT_STAT_C_SUSPEND to
* stop that signaling.
*/
ehci->reset_done [i] = jiffies + MSEC_TO_JIFFIES (20);
mod_timer (&ehci->hcd.rh_timer,
ehci->reset_done [i] + 1);
ehci_dbg (ehci, "port %d remote wakeup\n", i + 1);
}
}
/* PCI errors [4.15.2.4] */ /* PCI errors [4.15.2.4] */
if (unlikely ((status & STS_FATAL) != 0)) { if (unlikely ((status & STS_FATAL) != 0)) {
ehci_err (ehci, "fatal error\n"); ehci_err (ehci, "fatal error\n");
...@@ -814,7 +816,6 @@ static int ehci_urb_enqueue ( ...@@ -814,7 +816,6 @@ static int ehci_urb_enqueue (
struct ehci_hcd *ehci = hcd_to_ehci (hcd); struct ehci_hcd *ehci = hcd_to_ehci (hcd);
struct list_head qtd_list; struct list_head qtd_list;
urb->transfer_flags &= ~EHCI_STATE_UNLINK;
INIT_LIST_HEAD (&qtd_list); INIT_LIST_HEAD (&qtd_list);
switch (usb_pipetype (urb->pipe)) { switch (usb_pipetype (urb->pipe)) {
...@@ -914,7 +915,6 @@ static int ehci_urb_dequeue (struct usb_hcd *hcd, struct urb *urb) ...@@ -914,7 +915,6 @@ static int ehci_urb_dequeue (struct usb_hcd *hcd, struct urb *urb)
// wait till next completion, do it then. // wait till next completion, do it then.
// completion irqs can wait up to 1024 msec, // completion irqs can wait up to 1024 msec,
urb->transfer_flags |= EHCI_STATE_UNLINK;
break; break;
} }
spin_unlock_irqrestore (&ehci->lock, flags); spin_unlock_irqrestore (&ehci->lock, flags);
...@@ -965,7 +965,7 @@ ehci_endpoint_disable (struct usb_hcd *hcd, struct hcd_dev *dev, int ep) ...@@ -965,7 +965,7 @@ ehci_endpoint_disable (struct usb_hcd *hcd, struct hcd_dev *dev, int ep)
goto rescan; goto rescan;
case QH_STATE_IDLE: /* fully unlinked */ case QH_STATE_IDLE: /* fully unlinked */
if (list_empty (&qh->qtd_list)) { if (list_empty (&qh->qtd_list)) {
qh_put (ehci, qh); qh_put (qh);
break; break;
} }
/* else FALL THROUGH */ /* else FALL THROUGH */
......
...@@ -28,6 +28,131 @@ ...@@ -28,6 +28,131 @@
/*-------------------------------------------------------------------------*/ /*-------------------------------------------------------------------------*/
#ifdef CONFIG_PM
static int ehci_hub_suspend (struct usb_hcd *hcd)
{
struct ehci_hcd *ehci = hcd_to_ehci (hcd);
struct usb_device *root = hcd_to_bus (&ehci->hcd)->root_hub;
int port;
int status = 0;
if (root->dev.power.power_state != 0)
return 0;
if (time_before (jiffies, ehci->next_statechange))
return -EAGAIN;
port = HCS_N_PORTS (ehci->hcs_params);
spin_lock_irq (&ehci->lock);
/* suspend any active/unsuspended ports, maybe allow wakeup */
while (port--) {
u32 t1 = readl (&ehci->regs->port_status [port]);
u32 t2 = t1;
if ((t1 & PORT_PE) && !(t1 & PORT_OWNER))
t2 |= PORT_SUSPEND;
if (ehci->hcd.remote_wakeup)
t2 |= PORT_WKOC_E|PORT_WKDISC_E|PORT_WKCONN_E;
else
t2 &= ~(PORT_WKOC_E|PORT_WKDISC_E|PORT_WKCONN_E);
if (t1 != t2) {
ehci_vdbg (ehci, "port %d, %08x -> %08x\n",
port + 1, t1, t2);
writel (t2, &ehci->regs->port_status [port]);
}
}
/* stop schedules, then turn off HC and clean any completed work */
if (hcd->state == USB_STATE_RUNNING)
ehci_ready (ehci);
ehci->command = readl (&ehci->regs->command);
writel (ehci->command & ~CMD_RUN, &ehci->regs->command);
if (ehci->reclaim)
ehci->reclaim_ready = 1;
ehci_work (ehci, 0);
(void) handshake (&ehci->regs->status, STS_HALT, STS_HALT, 2000);
root->dev.power.power_state = 3;
ehci->next_statechange = jiffies + MSEC_TO_JIFFIES(10);
spin_unlock_irq (&ehci->lock);
return status;
}
/* caller owns root->serialize, and should reset/reinit on error */
static int ehci_hub_resume (struct usb_hcd *hcd)
{
struct ehci_hcd *ehci = hcd_to_ehci (hcd);
struct usb_device *root = hcd_to_bus (&ehci->hcd)->root_hub;
u32 temp;
int i;
if (!root->dev.power.power_state)
return 0;
if (time_before (jiffies, ehci->next_statechange))
return -EAGAIN;
/* re-init operational registers in case we lost power */
if (readl (&ehci->regs->intr_enable) == 0) {
writel (INTR_MASK, &ehci->regs->intr_enable);
writel (0, &ehci->regs->segment);
writel (ehci->periodic_dma, &ehci->regs->frame_list);
writel ((u32)ehci->async->qh_dma, &ehci->regs->async_next);
/* FIXME will this work even (pci) vAUX was lost? */
}
/* restore CMD_RUN, framelist size, and irq threshold */
writel (ehci->command, &ehci->regs->command);
/* take ports out of suspend */
i = HCS_N_PORTS (ehci->hcs_params);
while (i--) {
temp = readl (&ehci->regs->port_status [i]);
temp &= ~(PORT_WKOC_E|PORT_WKDISC_E|PORT_WKCONN_E);
if (temp & PORT_SUSPEND) {
ehci->reset_done [i] = jiffies + MSEC_TO_JIFFIES (20);
temp |= PORT_RESUME;
}
writel (temp, &ehci->regs->port_status [i]);
}
i = HCS_N_PORTS (ehci->hcs_params);
wait_ms (20);
while (i--) {
temp = readl (&ehci->regs->port_status [i]);
if ((temp & PORT_SUSPEND) == 0)
continue;
temp &= ~PORT_RESUME;
writel (temp, &ehci->regs->port_status [i]);
ehci_vdbg (ehci, "resumed port %d\n", i + 1);
}
(void) readl (&ehci->regs->command);
/* maybe re-activate the schedule(s) */
temp = 0;
if (ehci->async->qh_next.qh)
temp |= CMD_ASE;
if (ehci->periodic_sched)
temp |= CMD_PSE;
if (temp)
writel (ehci->command | temp, &ehci->regs->command);
root->dev.power.power_state = 0;
ehci->next_statechange = jiffies + MSEC_TO_JIFFIES(5);
ehci->hcd.state = USB_STATE_RUNNING;
return 0;
}
#else
#define ehci_hub_suspend 0
#define ehci_hub_resume 0
#endif /* CONFIG_PM */
/*-------------------------------------------------------------------------*/
static int check_reset_complete ( static int check_reset_complete (
struct ehci_hcd *ehci, struct ehci_hcd *ehci,
int index, int index,
...@@ -99,7 +224,11 @@ ehci_hub_status_data (struct usb_hcd *hcd, char *buf) ...@@ -99,7 +224,11 @@ ehci_hub_status_data (struct usb_hcd *hcd, char *buf)
} }
if (!(temp & PORT_CONNECT)) if (!(temp & PORT_CONNECT))
ehci->reset_done [i] = 0; ehci->reset_done [i] = 0;
if ((temp & (PORT_CSC | PORT_PEC | PORT_OCC)) != 0) { if ((temp & (PORT_CSC | PORT_PEC | PORT_OCC)) != 0
// PORT_STAT_C_SUSPEND?
|| ((temp & PORT_RESUME) != 0
&& time_after (jiffies,
ehci->reset_done [i]))) {
if (i < 7) if (i < 7)
buf [0] |= 1 << (i + 1); buf [0] |= 1 << (i + 1);
else else
...@@ -143,6 +272,8 @@ ehci_hub_descriptor ( ...@@ -143,6 +272,8 @@ ehci_hub_descriptor (
/*-------------------------------------------------------------------------*/ /*-------------------------------------------------------------------------*/
#define PORT_WAKE_BITS (PORT_WKOC_E|PORT_WKDISC_E|PORT_WKCONN_E)
static int ehci_hub_control ( static int ehci_hub_control (
struct usb_hcd *hcd, struct usb_hcd *hcd,
u16 typeReq, u16 typeReq,
...@@ -194,8 +325,20 @@ static int ehci_hub_control ( ...@@ -194,8 +325,20 @@ static int ehci_hub_control (
&ehci->regs->port_status [wIndex]); &ehci->regs->port_status [wIndex]);
break; break;
case USB_PORT_FEAT_SUSPEND: case USB_PORT_FEAT_SUSPEND:
if (temp & PORT_RESET)
goto error;
if (temp & PORT_SUSPEND) {
if ((temp & PORT_PE) == 0)
goto error;
/* resume signaling for 20 msec */
writel ((temp & ~PORT_WAKE_BITS) | PORT_RESUME,
&ehci->regs->port_status [wIndex]);
ehci->reset_done [wIndex] = jiffies
+ MSEC_TO_JIFFIES (20);
}
break;
case USB_PORT_FEAT_C_SUSPEND: case USB_PORT_FEAT_C_SUSPEND:
/* ? */ /* we auto-clear this feature */
break; break;
case USB_PORT_FEAT_POWER: case USB_PORT_FEAT_POWER:
if (HCS_PPC (ehci->hcs_params)) if (HCS_PPC (ehci->hcs_params))
...@@ -239,15 +382,37 @@ static int ehci_hub_control ( ...@@ -239,15 +382,37 @@ static int ehci_hub_control (
status |= 1 << USB_PORT_FEAT_C_CONNECTION; status |= 1 << USB_PORT_FEAT_C_CONNECTION;
if (temp & PORT_PEC) if (temp & PORT_PEC)
status |= 1 << USB_PORT_FEAT_C_ENABLE; status |= 1 << USB_PORT_FEAT_C_ENABLE;
// USB_PORT_FEAT_C_SUSPEND
if (temp & PORT_OCC) if (temp & PORT_OCC)
status |= 1 << USB_PORT_FEAT_C_OVER_CURRENT; status |= 1 << USB_PORT_FEAT_C_OVER_CURRENT;
/* whoever resumes must GetPortStatus to complete it!! */
if ((temp & PORT_RESUME)
&& time_after (jiffies,
ehci->reset_done [wIndex])) {
status |= 1 << USB_PORT_FEAT_C_SUSPEND;
ehci->reset_done [wIndex] = 0;
/* stop resume signaling */
temp = readl (&ehci->regs->port_status [wIndex]);
writel (temp & ~PORT_RESUME,
&ehci->regs->port_status [wIndex]);
retval = handshake (
&ehci->regs->port_status [wIndex],
PORT_RESUME, 0, 2000 /* 2msec */);
if (retval != 0) {
ehci_err (ehci, "port %d resume error %d\n",
wIndex + 1, retval);
goto error;
}
temp &= ~(PORT_SUSPEND|PORT_RESUME|(3<<10));
}
/* whoever resets must GetPortStatus to complete it!! */ /* whoever resets must GetPortStatus to complete it!! */
if ((temp & PORT_RESET) if ((temp & PORT_RESET)
&& time_after (jiffies, && time_after (jiffies,
ehci->reset_done [wIndex])) { ehci->reset_done [wIndex])) {
status |= 1 << USB_PORT_FEAT_C_RESET; status |= 1 << USB_PORT_FEAT_C_RESET;
ehci->reset_done [wIndex] = 0;
/* force reset to complete */ /* force reset to complete */
writel (temp & ~PORT_RESET, writel (temp & ~PORT_RESET,
...@@ -275,7 +440,7 @@ static int ehci_hub_control ( ...@@ -275,7 +440,7 @@ static int ehci_hub_control (
} }
if (temp & PORT_PE) if (temp & PORT_PE)
status |= 1 << USB_PORT_FEAT_ENABLE; status |= 1 << USB_PORT_FEAT_ENABLE;
if (temp & PORT_SUSPEND) if (temp & (PORT_SUSPEND|PORT_RESUME))
status |= 1 << USB_PORT_FEAT_SUSPEND; status |= 1 << USB_PORT_FEAT_SUSPEND;
if (temp & PORT_OC) if (temp & PORT_OC)
status |= 1 << USB_PORT_FEAT_OVER_CURRENT; status |= 1 << USB_PORT_FEAT_OVER_CURRENT;
...@@ -312,6 +477,11 @@ static int ehci_hub_control ( ...@@ -312,6 +477,11 @@ static int ehci_hub_control (
switch (wValue) { switch (wValue) {
case USB_PORT_FEAT_SUSPEND: case USB_PORT_FEAT_SUSPEND:
if ((temp & PORT_PE) == 0
|| (temp & PORT_RESET) != 0)
goto error;
if (ehci->hcd.remote_wakeup)
temp |= PORT_WAKE_BITS;
writel (temp | PORT_SUSPEND, writel (temp | PORT_SUSPEND,
&ehci->regs->port_status [wIndex]); &ehci->regs->port_status [wIndex]);
break; break;
...@@ -321,6 +491,8 @@ static int ehci_hub_control ( ...@@ -321,6 +491,8 @@ static int ehci_hub_control (
&ehci->regs->port_status [wIndex]); &ehci->regs->port_status [wIndex]);
break; break;
case USB_PORT_FEAT_RESET: case USB_PORT_FEAT_RESET:
if (temp & PORT_RESUME)
goto error;
/* line status bits may report this as low speed, /* line status bits may report this as low speed,
* which can be fine if this root hub has a * which can be fine if this root hub has a
* transaction translator built in. * transaction translator built in.
...@@ -342,7 +514,7 @@ static int ehci_hub_control ( ...@@ -342,7 +514,7 @@ static int ehci_hub_control (
* usb 2.0 spec says 50 ms resets on root * usb 2.0 spec says 50 ms resets on root
*/ */
ehci->reset_done [wIndex] = jiffies ehci->reset_done [wIndex] = jiffies
+ ((50 /* msec */ * HZ) / 1000); + MSEC_TO_JIFFIES (50);
} }
writel (temp, &ehci->regs->port_status [wIndex]); writel (temp, &ehci->regs->port_status [wIndex]);
break; break;
......
...@@ -87,6 +87,22 @@ static inline void ehci_qtd_free (struct ehci_hcd *ehci, struct ehci_qtd *qtd) ...@@ -87,6 +87,22 @@ static inline void ehci_qtd_free (struct ehci_hcd *ehci, struct ehci_qtd *qtd)
} }
static void qh_destroy (struct kref *kref)
{
struct ehci_qh *qh = container_of(kref, struct ehci_qh, kref);
struct ehci_hcd *ehci = qh->ehci;
/* clean qtds first, and know this is not linked */
if (!list_empty (&qh->qtd_list) || qh->qh_next.ptr) {
ehci_dbg (ehci, "unused qh not empty!\n");
BUG ();
}
if (qh->dummy)
ehci_qtd_free (ehci, qh->dummy);
usb_put_dev (qh->dev);
dma_pool_free (ehci->qh_pool, qh, qh->qh_dma);
}
static struct ehci_qh *ehci_qh_alloc (struct ehci_hcd *ehci, int flags) static struct ehci_qh *ehci_qh_alloc (struct ehci_hcd *ehci, int flags)
{ {
struct ehci_qh *qh; struct ehci_qh *qh;
...@@ -98,7 +114,8 @@ static struct ehci_qh *ehci_qh_alloc (struct ehci_hcd *ehci, int flags) ...@@ -98,7 +114,8 @@ static struct ehci_qh *ehci_qh_alloc (struct ehci_hcd *ehci, int flags)
return qh; return qh;
memset (qh, 0, sizeof *qh); memset (qh, 0, sizeof *qh);
atomic_set (&qh->refcount, 1); kref_init(&qh->kref, qh_destroy);
qh->ehci = ehci;
qh->qh_dma = dma; qh->qh_dma = dma;
// INIT_LIST_HEAD (&qh->qh_list); // INIT_LIST_HEAD (&qh->qh_list);
INIT_LIST_HEAD (&qh->qtd_list); INIT_LIST_HEAD (&qh->qtd_list);
...@@ -114,25 +131,15 @@ static struct ehci_qh *ehci_qh_alloc (struct ehci_hcd *ehci, int flags) ...@@ -114,25 +131,15 @@ static struct ehci_qh *ehci_qh_alloc (struct ehci_hcd *ehci, int flags)
} }
/* to share a qh (cpu threads, or hc) */ /* to share a qh (cpu threads, or hc) */
static inline struct ehci_qh *qh_get (/* ehci, */ struct ehci_qh *qh) static inline struct ehci_qh *qh_get (struct ehci_qh *qh)
{ {
atomic_inc (&qh->refcount); kref_get(&qh->kref);
return qh; return qh;
} }
static void qh_put (struct ehci_hcd *ehci, struct ehci_qh *qh) static inline void qh_put (struct ehci_qh *qh)
{ {
if (!atomic_dec_and_test (&qh->refcount)) kref_put(&qh->kref);
return;
/* clean qtds first, and know this is not linked */
if (!list_empty (&qh->qtd_list) || qh->qh_next.ptr) {
ehci_dbg (ehci, "unused qh not empty!\n");
BUG ();
}
if (qh->dummy)
ehci_qtd_free (ehci, qh->dummy);
usb_put_dev (qh->dev);
dma_pool_free (ehci->qh_pool, qh, qh->qh_dma);
} }
/*-------------------------------------------------------------------------*/ /*-------------------------------------------------------------------------*/
...@@ -145,7 +152,7 @@ static void qh_put (struct ehci_hcd *ehci, struct ehci_qh *qh) ...@@ -145,7 +152,7 @@ static void qh_put (struct ehci_hcd *ehci, struct ehci_qh *qh)
static void ehci_mem_cleanup (struct ehci_hcd *ehci) static void ehci_mem_cleanup (struct ehci_hcd *ehci)
{ {
if (ehci->async) if (ehci->async)
qh_put (ehci, ehci->async); qh_put (ehci->async);
ehci->async = 0; ehci->async = 0;
/* DMA consistent memory and pools */ /* DMA consistent memory and pools */
......
...@@ -193,7 +193,7 @@ ehci_urb_done (struct ehci_hcd *ehci, struct urb *urb, struct pt_regs *regs) ...@@ -193,7 +193,7 @@ ehci_urb_done (struct ehci_hcd *ehci, struct urb *urb, struct pt_regs *regs)
/* ... update hc-wide periodic stats (for usbfs) */ /* ... update hc-wide periodic stats (for usbfs) */
hcd_to_bus (&ehci->hcd)->bandwidth_int_reqs--; hcd_to_bus (&ehci->hcd)->bandwidth_int_reqs--;
} }
qh_put (ehci, qh); qh_put (qh);
} }
spin_lock (&urb->lock); spin_lock (&urb->lock);
...@@ -708,7 +708,7 @@ qh_make ( ...@@ -708,7 +708,7 @@ qh_make (
default: default:
dbg ("bogus dev %p speed %d", urb->dev, urb->dev->speed); dbg ("bogus dev %p speed %d", urb->dev, urb->dev->speed);
done: done:
qh_put (ehci, qh); qh_put (qh);
return 0; return 0;
} }
...@@ -951,7 +951,7 @@ static void end_unlink_async (struct ehci_hcd *ehci, struct pt_regs *regs) ...@@ -951,7 +951,7 @@ static void end_unlink_async (struct ehci_hcd *ehci, struct pt_regs *regs)
// qh->hw_next = cpu_to_le32 (qh->qh_dma); // qh->hw_next = cpu_to_le32 (qh->qh_dma);
qh->qh_state = QH_STATE_IDLE; qh->qh_state = QH_STATE_IDLE;
qh->qh_next.qh = 0; qh->qh_next.qh = 0;
qh_put (ehci, qh); // refcount from reclaim qh_put (qh); // refcount from reclaim
/* other unlink(s) may be pending (in QH_STATE_UNLINK_WAIT) */ /* other unlink(s) may be pending (in QH_STATE_UNLINK_WAIT) */
next = qh->reclaim; next = qh->reclaim;
...@@ -965,7 +965,7 @@ static void end_unlink_async (struct ehci_hcd *ehci, struct pt_regs *regs) ...@@ -965,7 +965,7 @@ static void end_unlink_async (struct ehci_hcd *ehci, struct pt_regs *regs)
&& HCD_IS_RUNNING (ehci->hcd.state)) && HCD_IS_RUNNING (ehci->hcd.state))
qh_link_async (ehci, qh); qh_link_async (ehci, qh);
else { else {
qh_put (ehci, qh); // refcount from async list qh_put (qh); // refcount from async list
/* it's not free to turn the async schedule on/off; leave it /* it's not free to turn the async schedule on/off; leave it
* active but idle for a while once it empties. * active but idle for a while once it empties.
...@@ -1067,7 +1067,7 @@ scan_async (struct ehci_hcd *ehci, struct pt_regs *regs) ...@@ -1067,7 +1067,7 @@ scan_async (struct ehci_hcd *ehci, struct pt_regs *regs)
qh = qh_get (qh); qh = qh_get (qh);
qh->stamp = ehci->stamp; qh->stamp = ehci->stamp;
temp = qh_completions (ehci, qh, regs); temp = qh_completions (ehci, qh, regs);
qh_put (ehci, qh); qh_put (qh);
if (temp != 0) { if (temp != 0) {
goto rescan; goto rescan;
} }
......
...@@ -312,7 +312,7 @@ static void intr_deschedule ( ...@@ -312,7 +312,7 @@ static void intr_deschedule (
do { do {
periodic_unlink (ehci, frame, qh); periodic_unlink (ehci, frame, qh);
qh_put (ehci, qh); qh_put (qh);
frame += qh->period; frame += qh->period;
} while (frame < ehci->periodic_size); } while (frame < ehci->periodic_size);
...@@ -355,7 +355,7 @@ static void intr_deschedule ( ...@@ -355,7 +355,7 @@ static void intr_deschedule (
dbg ("descheduled qh %p, period = %d frame = %d count = %d, urbs = %d", dbg ("descheduled qh %p, period = %d frame = %d count = %d, urbs = %d",
qh, qh->period, frame, qh, qh->period, frame,
atomic_read (&qh->refcount), ehci->periodic_sched); atomic_read (&qh->kref.refcount), ehci->periodic_sched);
} }
static int check_period ( static int check_period (
...@@ -1846,7 +1846,7 @@ scan_periodic (struct ehci_hcd *ehci, struct pt_regs *regs) ...@@ -1846,7 +1846,7 @@ scan_periodic (struct ehci_hcd *ehci, struct pt_regs *regs)
modified = qh_completions (ehci, temp.qh, regs); modified = qh_completions (ehci, temp.qh, regs);
if (unlikely (list_empty (&temp.qh->qtd_list))) if (unlikely (list_empty (&temp.qh->qtd_list)))
intr_deschedule (ehci, temp.qh, 0); intr_deschedule (ehci, temp.qh, 0);
qh_put (ehci, temp.qh); qh_put (temp.qh);
break; break;
case Q_TYPE_FSTN: case Q_TYPE_FSTN:
/* for "save place" FSTNs, look at QH entries /* for "save place" FSTNs, look at QH entries
......
...@@ -84,6 +84,8 @@ struct ehci_hcd { /* one per controller */ ...@@ -84,6 +84,8 @@ struct ehci_hcd { /* one per controller */
struct notifier_block reboot_notifier; struct notifier_block reboot_notifier;
unsigned long actions; unsigned long actions;
unsigned stamp; unsigned stamp;
unsigned long next_statechange;
u32 command;
unsigned is_arc_rh_tt:1; /* ARC roothub with TT */ unsigned is_arc_rh_tt:1; /* ARC roothub with TT */
...@@ -99,8 +101,6 @@ struct ehci_hcd { /* one per controller */ ...@@ -99,8 +101,6 @@ struct ehci_hcd { /* one per controller */
/* unwrap an HCD pointer to get an EHCI_HCD pointer */ /* unwrap an HCD pointer to get an EHCI_HCD pointer */
#define hcd_to_ehci(hcd_ptr) container_of(hcd_ptr, struct ehci_hcd, hcd) #define hcd_to_ehci(hcd_ptr) container_of(hcd_ptr, struct ehci_hcd, hcd)
/* NOTE: urb->transfer_flags expected to not use this bit !!! */
#define EHCI_STATE_UNLINK 0x8000 /* urb being unlinked */
enum ehci_timer_action { enum ehci_timer_action {
TIMER_IO_WATCHDOG, TIMER_IO_WATCHDOG,
...@@ -221,7 +221,7 @@ struct ehci_regs { ...@@ -221,7 +221,7 @@ struct ehci_regs {
u32 segment; /* address bits 63:32 if needed */ u32 segment; /* address bits 63:32 if needed */
/* PERIODICLISTBASE: offset 0x14 */ /* PERIODICLISTBASE: offset 0x14 */
u32 frame_list; /* points to periodic list */ u32 frame_list; /* points to periodic list */
/* ASYNCICLISTADDR: offset 0x18 */ /* ASYNCLISTADDR: offset 0x18 */
u32 async_next; /* address of next async queue head */ u32 async_next; /* address of next async queue head */
u32 reserved [9]; u32 reserved [9];
...@@ -237,7 +237,10 @@ struct ehci_regs { ...@@ -237,7 +237,10 @@ struct ehci_regs {
#define PORT_WKDISC_E (1<<21) /* wake on disconnect (enable) */ #define PORT_WKDISC_E (1<<21) /* wake on disconnect (enable) */
#define PORT_WKCONN_E (1<<20) /* wake on connect (enable) */ #define PORT_WKCONN_E (1<<20) /* wake on connect (enable) */
/* 19:16 for port testing */ /* 19:16 for port testing */
/* 15:14 for using port indicator leds (if HCS_INDICATOR allows) */ #define PORT_LED_OFF (0<<14)
#define PORT_LED_AMBER (1<<14)
#define PORT_LED_GREEN (2<<14)
#define PORT_LED_MASK (3<<14)
#define PORT_OWNER (1<<13) /* true: companion hc owns this port */ #define PORT_OWNER (1<<13) /* true: companion hc owns this port */
#define PORT_POWER (1<<12) /* true: has power (see PPC) */ #define PORT_POWER (1<<12) /* true: has power (see PPC) */
#define PORT_USB11(x) (((x)&(3<<10))==(1<<10)) /* USB 1.1 device */ #define PORT_USB11(x) (((x)&(3<<10))==(1<<10)) /* USB 1.1 device */
...@@ -366,7 +369,8 @@ struct ehci_qh { ...@@ -366,7 +369,8 @@ struct ehci_qh {
struct ehci_qtd *dummy; struct ehci_qtd *dummy;
struct ehci_qh *reclaim; /* next to reclaim */ struct ehci_qh *reclaim; /* next to reclaim */
atomic_t refcount; struct ehci_hcd *ehci;
struct kref kref;
unsigned stamp; unsigned stamp;
u8 qh_state; u8 qh_state;
...@@ -592,6 +596,14 @@ ehci_port_speed(struct ehci_hcd *ehci, unsigned int portsc) ...@@ -592,6 +596,14 @@ ehci_port_speed(struct ehci_hcd *ehci, unsigned int portsc)
/*-------------------------------------------------------------------------*/ /*-------------------------------------------------------------------------*/
#define MSEC_TO_JIFFIES(msec) ((HZ * (msec) + 999) / 1000)
static inline void msec_delay(int msec)
{
set_current_state(TASK_UNINTERRUPTIBLE);
schedule_timeout(MSEC_TO_JIFFIES(msec));
}
#ifndef DEBUG #ifndef DEBUG
#define STUB_DEBUG_FILES #define STUB_DEBUG_FILES
#endif /* DEBUG */ #endif /* DEBUG */
......
...@@ -617,7 +617,17 @@ show_registers (struct class_device *class_dev, char *buf) ...@@ -617,7 +617,17 @@ show_registers (struct class_device *class_dev, char *buf)
/* dump driver info, then registers in spec order */ /* dump driver info, then registers in spec order */
ohci_dbg_sw (ohci, &next, &size, ohci_dbg_sw (ohci, &next, &size,
"%s version " DRIVER_VERSION "\n", hcd_name); "bus %s, device %s\n"
"%s version " DRIVER_VERSION "\n",
hcd->self.controller->bus->name,
hcd->self.controller->bus_id,
hcd_name);
if (bus->controller->power.power_state) {
size -= scnprintf (next, size,
"SUSPENDED (no register access)\n");
goto done;
}
ohci_dump_status(ohci, &next, &size); ohci_dump_status(ohci, &next, &size);
...@@ -657,8 +667,8 @@ show_registers (struct class_device *class_dev, char *buf) ...@@ -657,8 +667,8 @@ show_registers (struct class_device *class_dev, char *buf)
/* roothub */ /* roothub */
ohci_dump_roothub (ohci, 1, &next, &size); ohci_dump_roothub (ohci, 1, &next, &size);
done:
spin_unlock_irqrestore (&ohci->lock, flags); spin_unlock_irqrestore (&ohci->lock, flags);
return PAGE_SIZE - size; return PAGE_SIZE - size;
} }
static CLASS_DEVICE_ATTR (registers, S_IRUGO, show_registers, NULL); static CLASS_DEVICE_ATTR (registers, S_IRUGO, show_registers, NULL);
......
...@@ -117,8 +117,8 @@ ...@@ -117,8 +117,8 @@
/* For initializing controller (mask in an HCFS mode too) */ /* For initializing controller (mask in an HCFS mode too) */
#define OHCI_CONTROL_INIT OHCI_CTRL_CBSR #define OHCI_CONTROL_INIT OHCI_CTRL_CBSR
#define OHCI_INTR_INIT \
#define OHCI_UNLINK_TIMEOUT (HZ / 10) (OHCI_INTR_MIE | OHCI_INTR_UE | OHCI_INTR_RD | OHCI_INTR_WDH)
/*-------------------------------------------------------------------------*/ /*-------------------------------------------------------------------------*/
...@@ -126,11 +126,6 @@ static const char hcd_name [] = "ohci_hcd"; ...@@ -126,11 +126,6 @@ static const char hcd_name [] = "ohci_hcd";
#include "ohci.h" #include "ohci.h"
static inline void disable (struct ohci_hcd *ohci)
{
ohci->hcd.state = USB_STATE_HALT;
}
#include "ohci-hub.c" #include "ohci-hub.c"
#include "ohci-dbg.c" #include "ohci-dbg.c"
#include "ohci-mem.c" #include "ohci-mem.c"
...@@ -206,8 +201,7 @@ static int ohci_urb_enqueue ( ...@@ -206,8 +201,7 @@ static int ohci_urb_enqueue (
if (!urb_priv) if (!urb_priv)
return -ENOMEM; return -ENOMEM;
memset (urb_priv, 0, sizeof (urb_priv_t) + size * sizeof (struct td *)); memset (urb_priv, 0, sizeof (urb_priv_t) + size * sizeof (struct td *));
INIT_LIST_HEAD (&urb_priv->pending);
/* fill the private part of the URB */
urb_priv->length = size; urb_priv->length = size;
urb_priv->ed = ed; urb_priv->ed = ed;
...@@ -397,6 +391,16 @@ static int hc_reset (struct ohci_hcd *ohci) ...@@ -397,6 +391,16 @@ static int hc_reset (struct ohci_hcd *ohci)
{ {
u32 temp; u32 temp;
/* boot firmware should have set this up (5.1.1.3.1) */
if (!ohci->fminterval) {
temp = readl (&ohci->regs->fminterval);
if (temp & 0x3fff0000)
ohci->fminterval = temp;
else
ohci->fminterval = DEFAULT_FMINTERVAL;
/* also: power/overcurrent flags in roothub.a */
}
/* SMM owns the HC? not for long! /* SMM owns the HC? not for long!
* On PA-RISC, PDC can leave IR set incorrectly; ignore it there. * On PA-RISC, PDC can leave IR set incorrectly; ignore it there.
*/ */
...@@ -413,7 +417,7 @@ static int hc_reset (struct ohci_hcd *ohci) ...@@ -413,7 +417,7 @@ static int hc_reset (struct ohci_hcd *ohci)
writel (OHCI_INTR_OC, &ohci->regs->intrenable); writel (OHCI_INTR_OC, &ohci->regs->intrenable);
writel (OHCI_OCR, &ohci->regs->cmdstatus); writel (OHCI_OCR, &ohci->regs->cmdstatus);
while (readl (&ohci->regs->control) & OHCI_CTRL_IR) { while (readl (&ohci->regs->control) & OHCI_CTRL_IR) {
wait_ms (10); msec_delay (10);
if (--temp == 0) { if (--temp == 0) {
ohci_err (ohci, "USB HC TakeOver failed!\n"); ohci_err (ohci, "USB HC TakeOver failed!\n");
return -1; return -1;
...@@ -430,9 +434,12 @@ static int hc_reset (struct ohci_hcd *ohci) ...@@ -430,9 +434,12 @@ static int hc_reset (struct ohci_hcd *ohci)
/* Reset USB (needed by some controllers); RemoteWakeupConnected /* Reset USB (needed by some controllers); RemoteWakeupConnected
* saved if boot firmware (BIOS/SMM/...) told us it's connected * saved if boot firmware (BIOS/SMM/...) told us it's connected
* (for OHCI integrated on mainboard, it normally is)
*/ */
ohci->hc_control = readl (&ohci->regs->control); ohci->hc_control = readl (&ohci->regs->control);
ohci->hc_control &= OHCI_CTRL_RWC; /* hcfs 0 = RESET */ ohci->hc_control &= OHCI_CTRL_RWC; /* hcfs 0 = RESET */
if (ohci->hc_control)
ohci->hcd.can_wakeup = 1;
writel (ohci->hc_control, &ohci->regs->control); writel (ohci->hc_control, &ohci->regs->control);
if (power_switching) { if (power_switching) {
unsigned ports = roothub_a (ohci) & RH_A_NDP; unsigned ports = roothub_a (ohci) & RH_A_NDP;
...@@ -444,7 +451,7 @@ static int hc_reset (struct ohci_hcd *ohci) ...@@ -444,7 +451,7 @@ static int hc_reset (struct ohci_hcd *ohci)
} }
// flush those pci writes // flush those pci writes
(void) readl (&ohci->regs->control); (void) readl (&ohci->regs->control);
wait_ms (50); msec_delay (50);
/* HC Reset requires max 10 us delay */ /* HC Reset requires max 10 us delay */
writel (OHCI_HCR, &ohci->regs->cmdstatus); writel (OHCI_HCR, &ohci->regs->cmdstatus);
...@@ -473,9 +480,6 @@ static int hc_reset (struct ohci_hcd *ohci) ...@@ -473,9 +480,6 @@ static int hc_reset (struct ohci_hcd *ohci)
/*-------------------------------------------------------------------------*/ /*-------------------------------------------------------------------------*/
#define FI 0x2edf /* 12000 bits per frame (-1) */
#define LSTHRESH 0x628 /* lowspeed bit threshold */
/* Start an OHCI controller, set the BUS operational /* Start an OHCI controller, set the BUS operational
* enable interrupts * enable interrupts
* connect the virtual root hub * connect the virtual root hub
...@@ -486,7 +490,6 @@ static int hc_start (struct ohci_hcd *ohci) ...@@ -486,7 +490,6 @@ static int hc_start (struct ohci_hcd *ohci)
struct usb_device *udev; struct usb_device *udev;
struct usb_bus *bus; struct usb_bus *bus;
spin_lock_init (&ohci->lock);
disable (ohci); disable (ohci);
/* Tell the controller where the control and bulk lists are /* Tell the controller where the control and bulk lists are
...@@ -497,12 +500,7 @@ static int hc_start (struct ohci_hcd *ohci) ...@@ -497,12 +500,7 @@ static int hc_start (struct ohci_hcd *ohci)
/* a reset clears this */ /* a reset clears this */
writel ((u32) ohci->hcca_dma, &ohci->regs->hcca); writel ((u32) ohci->hcca_dma, &ohci->regs->hcca);
/* force default fmInterval (we won't adjust it); init thresholds periodic_reinit (ohci);
* for last FS and LS packets, reserve 90% for periodic.
*/
writel ((((6 * (FI - 210)) / 7) << 16) | FI, &ohci->regs->fminterval);
writel (((9 * FI) / 10) & 0x3fff, &ohci->regs->periodicstart);
writel (LSTHRESH, &ohci->regs->lsthresh);
/* some OHCI implementations are finicky about how they init. /* some OHCI implementations are finicky about how they init.
* bogus values here mean not even enumeration could work. * bogus values here mean not even enumeration could work.
...@@ -519,8 +517,11 @@ static int hc_start (struct ohci_hcd *ohci) ...@@ -519,8 +517,11 @@ static int hc_start (struct ohci_hcd *ohci)
writel (ohci->hc_control, &ohci->regs->control); writel (ohci->hc_control, &ohci->regs->control);
ohci->hcd.state = USB_STATE_RUNNING; ohci->hcd.state = USB_STATE_RUNNING;
/* wake on ConnectStatusChange, matching external hubs */
writel (RH_HS_DRWE, &ohci->regs->roothub.status);
/* Choose the interrupts we care about now, others later on demand */ /* Choose the interrupts we care about now, others later on demand */
mask = OHCI_INTR_MIE | OHCI_INTR_UE | OHCI_INTR_WDH; mask = OHCI_INTR_INIT;
writel (mask, &ohci->regs->intrstatus); writel (mask, &ohci->regs->intrstatus);
writel (mask, &ohci->regs->intrenable); writel (mask, &ohci->regs->intrenable);
...@@ -551,9 +552,14 @@ static int hc_start (struct ohci_hcd *ohci) ...@@ -551,9 +552,14 @@ static int hc_start (struct ohci_hcd *ohci)
// POTPGT delay is bits 24-31, in 2 ms units. // POTPGT delay is bits 24-31, in 2 ms units.
mdelay ((roothub_a (ohci) >> 23) & 0x1fe); mdelay ((roothub_a (ohci) >> 23) & 0x1fe);
bus = hcd_to_bus (&ohci->hcd);
if (bus->root_hub) {
ohci->hcd.state = USB_STATE_RUNNING;
return 0;
}
/* connect the virtual root hub */ /* connect the virtual root hub */
bus = hcd_to_bus (&ohci->hcd);
bus->root_hub = udev = usb_alloc_dev (NULL, bus, 0); bus->root_hub = udev = usb_alloc_dev (NULL, bus, 0);
ohci->hcd.state = USB_STATE_RUNNING; ohci->hcd.state = USB_STATE_RUNNING;
if (!udev) { if (!udev) {
...@@ -610,11 +616,18 @@ static irqreturn_t ohci_irq (struct usb_hcd *hcd, struct pt_regs *ptregs) ...@@ -610,11 +616,18 @@ static irqreturn_t ohci_irq (struct usb_hcd *hcd, struct pt_regs *ptregs)
ohci_dump (ohci, 1); ohci_dump (ohci, 1);
hc_reset (ohci); hc_reset (ohci);
} }
if (ints & OHCI_INTR_RD) {
ohci_vdbg (ohci, "resume detect\n");
schedule_work(&ohci->rh_resume);
}
if (ints & OHCI_INTR_WDH) { if (ints & OHCI_INTR_WDH) {
if (HCD_IS_RUNNING(hcd->state)) if (HCD_IS_RUNNING(hcd->state))
writel (OHCI_INTR_WDH, &regs->intrdisable); writel (OHCI_INTR_WDH, &regs->intrdisable);
dl_done_list (ohci, dl_reverse_done_list (ohci), ptregs); spin_lock (&ohci->lock);
dl_done_list (ohci, ptregs);
spin_unlock (&ohci->lock);
if (HCD_IS_RUNNING(hcd->state)) if (HCD_IS_RUNNING(hcd->state))
writel (OHCI_INTR_WDH, &regs->intrenable); writel (OHCI_INTR_WDH, &regs->intrenable);
} }
...@@ -654,6 +667,7 @@ static void ohci_stop (struct usb_hcd *hcd) ...@@ -654,6 +667,7 @@ static void ohci_stop (struct usb_hcd *hcd)
ohci->hcd.state); ohci->hcd.state);
ohci_dump (ohci, 1); ohci_dump (ohci, 1);
flush_scheduled_work();
if (HCD_IS_RUNNING(ohci->hcd.state)) if (HCD_IS_RUNNING(ohci->hcd.state))
hc_reset (ohci); hc_reset (ohci);
...@@ -670,22 +684,68 @@ static void ohci_stop (struct usb_hcd *hcd) ...@@ -670,22 +684,68 @@ static void ohci_stop (struct usb_hcd *hcd)
/*-------------------------------------------------------------------------*/ /*-------------------------------------------------------------------------*/
// FIXME: this restart logic should be generic,
// and handle full hcd state cleanup
/* controller died; cleanup debris, then restart */
/* must not be called from interrupt context */ /* must not be called from interrupt context */
#ifdef CONFIG_PM #ifdef CONFIG_PM
static void mark_children_gone (struct usb_device *dev)
{
unsigned i;
for (i = 0; i < dev->maxchild; i++) {
if (dev->children [i] == 0)
continue;
dev->children [i]->state = USB_STATE_NOTATTACHED;
mark_children_gone (dev->children [i]);
}
}
static int hc_restart (struct ohci_hcd *ohci) static int hc_restart (struct ohci_hcd *ohci)
{ {
int temp; int temp;
int i; int i;
struct urb_priv *priv;
/* mark any devices gone, so they do nothing till khubd disconnects.
* recycle any "live" eds/tds (and urbs) right away.
* later, khubd disconnect processing will recycle the other state,
* (either as disconnect/reconnect, or maybe someday as a reset).
*/
spin_lock_irq(&ohci->lock);
disable (ohci); disable (ohci);
if (hcd_to_bus (&ohci->hcd)->root_hub) mark_children_gone (ohci->hcd.self.root_hub);
usb_disconnect (&hcd_to_bus (&ohci->hcd)->root_hub); if (!list_empty (&ohci->pending))
ohci_dbg(ohci, "abort schedule...\n");
list_for_each_entry (priv, &ohci->pending, pending) {
struct urb *urb = priv->td[0]->urb;
struct ed *ed = priv->ed;
switch (ed->state) {
case ED_OPER:
ed->state = ED_UNLINK;
ed->hwINFO |= ED_DEQUEUE;
ed_deschedule (ohci, ed);
ed->ed_next = ohci->ed_rm_list;
ed->ed_prev = 0;
ohci->ed_rm_list = ed;
/* FALLTHROUGH */
case ED_UNLINK:
break;
default:
ohci_dbg(ohci, "bogus ed %p state %d\n",
ed, ed->state);
}
spin_lock (&urb->lock);
urb->status = -ESHUTDOWN;
spin_unlock (&urb->lock);
}
finish_unlinks (ohci, 0, 0);
spin_unlock_irq(&ohci->lock);
/* paranoia, in case that didn't work: */
/* empty the interrupt branches */ /* empty the interrupt branches */
for (i = 0; i < NUM_INTS; i++) ohci->load [i] = 0; for (i = 0; i < NUM_INTS; i++) ohci->load [i] = 0;
for (i = 0; i < NUM_INTS; i++) ohci->hcca->int_table [i] = 0; for (i = 0; i < NUM_INTS; i++) ohci->hcca->int_table [i] = 0;
...@@ -700,8 +760,20 @@ static int hc_restart (struct ohci_hcd *ohci) ...@@ -700,8 +760,20 @@ static int hc_restart (struct ohci_hcd *ohci)
if ((temp = hc_reset (ohci)) < 0 || (temp = hc_start (ohci)) < 0) { if ((temp = hc_reset (ohci)) < 0 || (temp = hc_start (ohci)) < 0) {
ohci_err (ohci, "can't restart, %d\n", temp); ohci_err (ohci, "can't restart, %d\n", temp);
return temp; return temp;
} else } else {
/* here we "know" root ports should always stay powered,
* and that if we try to turn them back on the root hub
* will respond to CSC processing.
*/
i = roothub_a (ohci) & RH_A_NDP;
while (i--)
writel (RH_PS_PSS,
&ohci->regs->roothub.portstatus [temp]);
ohci->hcd.self.root_hub->dev.power.power_state = 0;
ohci->hcd.state = USB_STATE_RUNNING;
ohci_dbg (ohci, "restart complete\n"); ohci_dbg (ohci, "restart complete\n");
ohci_dump (ohci, 1);
}
return 0; return 0;
} }
#endif #endif
......
...@@ -60,6 +60,261 @@ static u32 roothub_portstatus (struct ohci_hcd *hc, int i) ...@@ -60,6 +60,261 @@ static u32 roothub_portstatus (struct ohci_hcd *hc, int i)
(temp & RH_PS_CCS) ? " CCS" : "" \ (temp & RH_PS_CCS) ? " CCS" : "" \
); );
/*-------------------------------------------------------------------------*/
#if defined(CONFIG_USB_SUSPEND) || defined(CONFIG_PM)
#define OHCI_SCHED_ENABLES \
(OHCI_CTRL_CLE|OHCI_CTRL_BLE|OHCI_CTRL_PLE|OHCI_CTRL_IE)
static void dl_done_list (struct ohci_hcd *, struct pt_regs *);
static void finish_unlinks (struct ohci_hcd *, u16 , struct pt_regs *);
static int ohci_hub_suspend (struct usb_hcd *hcd)
{
struct ohci_hcd *ohci = hcd_to_ohci (hcd);
struct usb_device *root = hcd_to_bus (&ohci->hcd)->root_hub;
int status = 0;
if (root->dev.power.power_state != 0)
return 0;
if (time_before (jiffies, ohci->next_statechange))
return -EAGAIN;
spin_lock_irq (&ohci->lock);
ohci->hc_control = readl (&ohci->regs->control);
switch (ohci->hc_control & OHCI_CTRL_HCFS) {
case OHCI_USB_RESUME:
ohci_dbg (ohci, "resume/suspend?\n");
ohci->hc_control &= ~OHCI_CTRL_HCFS;
ohci->hc_control |= OHCI_USB_RESET;
writel (ohci->hc_control, &ohci->regs->control);
(void) readl (&ohci->regs->control);
/* FALL THROUGH */
case OHCI_USB_RESET:
status = -EBUSY;
ohci_dbg (ohci, "needs reinit!\n");
goto done;
case OHCI_USB_SUSPEND:
ohci_dbg (ohci, "already suspended?\n");
goto succeed;
}
ohci_dbg (ohci, "suspend root hub\n");
/* First stop any processing */
ohci->hcd.state = USB_STATE_QUIESCING;
if (ohci->hc_control & OHCI_SCHED_ENABLES) {
int limit;
ohci->hc_control &= ~OHCI_SCHED_ENABLES;
writel (ohci->hc_control, &ohci->regs->control);
ohci->hc_control = readl (&ohci->regs->control);
writel (OHCI_INTR_SF, &ohci->regs->intrstatus);
/* sched disables take effect on the next frame,
* then the last WDH could take 6+ msec
*/
ohci_dbg (ohci, "stopping schedules ...\n");
limit = 2000;
while (limit > 0) {
udelay (250);
limit =- 250;
if (readl (&ohci->regs->intrstatus) & OHCI_INTR_SF)
break;
}
dl_done_list (ohci, 0);
mdelay (7);
}
dl_done_list (ohci, 0);
finish_unlinks (ohci, OHCI_FRAME_NO(ohci->hcca), 0);
writel (readl (&ohci->regs->intrstatus), &ohci->regs->intrstatus);
/* maybe resume can wake root hub */
if (ohci->hcd.remote_wakeup)
ohci->hc_control |= OHCI_CTRL_RWE;
else
ohci->hc_control &= ~OHCI_CTRL_RWE;
/* Suspend hub */
ohci->hc_control &= ~OHCI_CTRL_HCFS;
ohci->hc_control |= OHCI_USB_SUSPEND;
writel (ohci->hc_control, &ohci->regs->control);
(void) readl (&ohci->regs->control);
/* no resumes until devices finish suspending */
ohci->next_statechange = jiffies + MSEC_TO_JIFFIES (5);
succeed:
/* it's not USB_STATE_SUSPENDED unless access to this
* hub from the non-usb side (PCI, SOC, etc) stopped
*/
root->dev.power.power_state = 3;
done:
spin_unlock_irq (&ohci->lock);
return status;
}
static inline struct ed *find_head (struct ed *ed)
{
/* for bulk and control lists */
while (ed->ed_prev)
ed = ed->ed_prev;
return ed;
}
static int hc_restart (struct ohci_hcd *ohci);
/* caller owns root->serialize */
static int ohci_hub_resume (struct usb_hcd *hcd)
{
struct ohci_hcd *ohci = hcd_to_ohci (hcd);
struct usb_device *root = hcd_to_bus (&ohci->hcd)->root_hub;
u32 temp, enables;
int status = -EINPROGRESS;
if (!root->dev.power.power_state)
return 0;
if (time_before (jiffies, ohci->next_statechange))
return -EAGAIN;
spin_lock_irq (&ohci->lock);
ohci->hc_control = readl (&ohci->regs->control);
switch (ohci->hc_control & OHCI_CTRL_HCFS) {
case OHCI_USB_SUSPEND:
ohci->hc_control &= ~(OHCI_CTRL_HCFS|OHCI_SCHED_ENABLES);
ohci->hc_control |= OHCI_USB_RESUME;
writel (ohci->hc_control, &ohci->regs->control);
(void) readl (&ohci->regs->control);
ohci_dbg (ohci, "resume root hub\n");
break;
case OHCI_USB_RESUME:
/* HCFS changes sometime after INTR_RD */
ohci_info (ohci, "remote wakeup\n");
break;
case OHCI_USB_OPER:
ohci_dbg (ohci, "odd resume\n");
root->dev.power.power_state = 0;
status = 0;
break;
default: /* RESET, we lost power */
ohci_dbg (ohci, "root hub hardware reset\n");
status = -EBUSY;
}
spin_unlock_irq (&ohci->lock);
if (status == -EBUSY)
return hc_restart (ohci);
if (status != -EINPROGRESS)
return status;
temp = roothub_a (ohci) & RH_A_NDP;
enables = 0;
while (temp--) {
u32 stat = readl (&ohci->regs->roothub.portstatus [temp]);
/* force global, not selective, resume */
if (!(stat & RH_PS_PSS))
continue;
writel (RH_PS_POCI, &ohci->regs->roothub.portstatus [temp]);
}
/* Some controllers (lucent) need extra-long delays */
ohci->hcd.state = USB_STATE_RESUMING;
mdelay (20 /* usb 11.5.1.10 */ + 15);
temp = readl (&ohci->regs->control);
temp &= OHCI_CTRL_HCFS;
if (temp != OHCI_USB_RESUME) {
ohci_err (ohci, "controller won't resume\n");
return -EBUSY;
}
/* disable old schedule state, reinit from scratch */
writel (0, &ohci->regs->ed_controlhead);
writel (0, &ohci->regs->ed_controlcurrent);
writel (0, &ohci->regs->ed_bulkhead);
writel (0, &ohci->regs->ed_bulkcurrent);
writel (0, &ohci->regs->ed_periodcurrent);
writel ((u32) ohci->hcca_dma, &ohci->regs->hcca);
periodic_reinit (ohci);
/* interrupts might have been disabled */
writel (OHCI_INTR_INIT, &ohci->regs->intrenable);
if (ohci->ed_rm_list)
writel (OHCI_INTR_SF, &ohci->regs->intrenable);
writel (readl (&ohci->regs->intrstatus), &ohci->regs->intrstatus);
/* Then re-enable operations */
writel (OHCI_USB_OPER, &ohci->regs->control);
(void) readl (&ohci->regs->control);
msec_delay (3);
temp = OHCI_CONTROL_INIT | OHCI_USB_OPER;
if (ohci->hcd.can_wakeup)
temp |= OHCI_CTRL_RWC;
ohci->hc_control = temp;
writel (temp, &ohci->regs->control);
(void) readl (&ohci->regs->control);
/* TRSMRCY */
msec_delay (10);
root->dev.power.power_state = 0;
/* keep it alive for ~5x suspend + resume costs */
ohci->next_statechange = jiffies + MSEC_TO_JIFFIES (250);
/* maybe turn schedules back on */
enables = 0;
temp = 0;
if (!ohci->ed_rm_list) {
if (ohci->ed_controltail) {
writel (find_head (ohci->ed_controltail)->dma,
&ohci->regs->ed_controlhead);
enables |= OHCI_CTRL_CLE;
temp |= OHCI_CLF;
}
if (ohci->ed_bulktail) {
writel (find_head (ohci->ed_bulktail)->dma,
&ohci->regs->ed_bulkhead);
enables |= OHCI_CTRL_BLE;
temp |= OHCI_BLF;
}
}
if (hcd_to_bus (&ohci->hcd)->bandwidth_isoc_reqs
|| hcd_to_bus (&ohci->hcd)->bandwidth_int_reqs)
enables |= OHCI_CTRL_PLE|OHCI_CTRL_IE;
if (enables) {
ohci_dbg (ohci, "restarting schedules ... %08x\n", enables);
ohci->hc_control |= enables;
writel (ohci->hc_control, &ohci->regs->control);
if (temp)
writel (status, &ohci->regs->cmdstatus);
(void) readl (&ohci->regs->control);
}
ohci->hcd.state = USB_STATE_RUNNING;
return 0;
}
static void ohci_rh_resume (void *_hcd)
{
struct usb_hcd *hcd = _hcd;
down (&hcd->self.root_hub->serialize);
(void) ohci_hub_resume (hcd);
up (&hcd->self.root_hub->serialize);
}
#else
static void ohci_rh_resume (void *_hcd)
{
struct ohci_hcd *ohci = hcd_to_ohci (hcd);
ohci_dbg(ohci, "rh_resume ??\n");
}
#endif /* CONFIG_USB_SUSPEND || CONFIG_PM */
/*-------------------------------------------------------------------------*/ /*-------------------------------------------------------------------------*/
...@@ -70,6 +325,7 @@ ohci_hub_status_data (struct usb_hcd *hcd, char *buf) ...@@ -70,6 +325,7 @@ ohci_hub_status_data (struct usb_hcd *hcd, char *buf)
{ {
struct ohci_hcd *ohci = hcd_to_ohci (hcd); struct ohci_hcd *ohci = hcd_to_ohci (hcd);
int ports, i, changed = 0, length = 1; int ports, i, changed = 0, length = 1;
int can_suspend = 1;
ports = roothub_a (ohci) & RH_A_NDP; ports = roothub_a (ohci) & RH_A_NDP;
if (ports > MAX_ROOT_PORTS) { if (ports > MAX_ROOT_PORTS) {
...@@ -95,16 +351,44 @@ ohci_hub_status_data (struct usb_hcd *hcd, char *buf) ...@@ -95,16 +351,44 @@ ohci_hub_status_data (struct usb_hcd *hcd, char *buf)
for (i = 0; i < ports; i++) { for (i = 0; i < ports; i++) {
u32 status = roothub_portstatus (ohci, i); u32 status = roothub_portstatus (ohci, i);
status &= RH_PS_CSC | RH_PS_PESC | RH_PS_PSSC if (status & (RH_PS_CSC | RH_PS_PESC | RH_PS_PSSC
| RH_PS_OCIC | RH_PS_PRSC; | RH_PS_OCIC | RH_PS_PRSC)) {
if (status) {
changed = 1; changed = 1;
if (i < 7) if (i < 7)
buf [0] |= 1 << (i + 1); buf [0] |= 1 << (i + 1);
else else
buf [1] |= 1 << (i - 7); buf [1] |= 1 << (i - 7);
continue;
} }
/* can suspend if no ports are enabled; or if all all
* enabled ports are suspended AND remote wakeup is on.
*/
if (!(status & RH_PS_CCS))
continue;
if ((status & RH_PS_PSS) && ohci->hcd.remote_wakeup)
continue;
can_suspend = 0;
} }
#ifdef CONFIG_PM
/* save power by suspending idle root hubs;
* INTR_RD wakes us when there's work
*/
if (can_suspend
&& !changed
&& !ohci->ed_rm_list
&& ((OHCI_CTRL_HCFS | OHCI_SCHED_ENABLES)
& ohci->hc_control)
== OHCI_USB_OPER
&& down_trylock (&hcd->self.root_hub->serialize) == 0
) {
ohci_vdbg (ohci, "autosuspend\n");
(void) ohci_hub_suspend (&ohci->hcd);
up (&hcd->self.root_hub->serialize);
}
#endif
return changed ? length : 0; return changed ? length : 0;
} }
...@@ -188,6 +472,9 @@ static int ohci_hub_control ( ...@@ -188,6 +472,9 @@ static int ohci_hub_control (
break; break;
case USB_PORT_FEAT_SUSPEND: case USB_PORT_FEAT_SUSPEND:
temp = RH_PS_POCI; temp = RH_PS_POCI;
if ((ohci->hc_control & OHCI_CTRL_HCFS)
!= OHCI_USB_OPER)
schedule_work (&ohci->rh_resume);
break; break;
case USB_PORT_FEAT_C_SUSPEND: case USB_PORT_FEAT_C_SUSPEND:
temp = RH_PS_PSSC; temp = RH_PS_PSSC;
......
...@@ -31,6 +31,9 @@ static struct usb_hcd *ohci_hcd_alloc (void) ...@@ -31,6 +31,9 @@ static struct usb_hcd *ohci_hcd_alloc (void)
if (ohci != 0) { if (ohci != 0) {
memset (ohci, 0, sizeof (struct ohci_hcd)); memset (ohci, 0, sizeof (struct ohci_hcd));
ohci->hcd.product_desc = "OHCI Host Controller"; ohci->hcd.product_desc = "OHCI Host Controller";
spin_lock_init (&ohci->lock);
INIT_LIST_HEAD (&ohci->pending);
INIT_WORK (&ohci->rh_resume, ohci_rh_resume, &ohci->hcd);
return &ohci->hcd; return &ohci->hcd;
} }
return 0; return 0;
......
...@@ -36,6 +36,7 @@ ohci_pci_reset (struct usb_hcd *hcd) ...@@ -36,6 +36,7 @@ ohci_pci_reset (struct usb_hcd *hcd)
struct ohci_hcd *ohci = hcd_to_ohci (hcd); struct ohci_hcd *ohci = hcd_to_ohci (hcd);
ohci->regs = hcd->regs; ohci->regs = hcd->regs;
ohci->next_statechange = jiffies;
return hc_reset (ohci); return hc_reset (ohci);
} }
...@@ -118,74 +119,25 @@ ohci_pci_start (struct usb_hcd *hcd) ...@@ -118,74 +119,25 @@ ohci_pci_start (struct usb_hcd *hcd)
static int ohci_pci_suspend (struct usb_hcd *hcd, u32 state) static int ohci_pci_suspend (struct usb_hcd *hcd, u32 state)
{ {
struct ohci_hcd *ohci = hcd_to_ohci (hcd); struct ohci_hcd *ohci = hcd_to_ohci (hcd);
u16 cmd;
u32 tmp;
if ((ohci->hc_control & OHCI_CTRL_HCFS) != OHCI_USB_OPER) { /* suspend root hub, hoping it keeps power during suspend */
ohci_dbg (ohci, "can't suspend (state is %s)\n", while (time_before (jiffies, ohci->next_statechange))
hcfs2string (ohci->hc_control & OHCI_CTRL_HCFS)); msec_delay (100);
return -EIO;
} #ifdef CONFIG_USB_SUSPEND
(void) usb_suspend_device (hcd->self.root_hub);
#else
/* FIXME lock root hub */
(void) ohci_hub_suspend (hcd);
#endif
/* act as if usb suspend can always be used */ /* let things settle down a bit */
ohci_dbg (ohci, "suspend to %d\n", state); msec_delay (100);
/* First stop processing */
spin_lock_irq (&ohci->lock);
ohci->hc_control &=
~(OHCI_CTRL_PLE|OHCI_CTRL_CLE|OHCI_CTRL_BLE|OHCI_CTRL_IE);
writel (ohci->hc_control, &ohci->regs->control);
writel (OHCI_INTR_SF, &ohci->regs->intrstatus);
(void) readl (&ohci->regs->intrstatus);
spin_unlock_irq (&ohci->lock);
/* Wait a frame or two */
mdelay (1);
if (!readl (&ohci->regs->intrstatus) & OHCI_INTR_SF)
mdelay (1);
#ifdef CONFIG_PMAC_PBOOK #ifdef CONFIG_PMAC_PBOOK
if (_machine == _MACH_Pmac) if (_machine == _MACH_Pmac)
disable_irq ((to_pci_dev(hcd->self.controller))->irq); disable_irq ((to_pci_dev(hcd->self.controller))->irq);
/* else, 2.4 assumes shared irqs -- don't disable */
#endif
/* Enable remote wakeup */
writel (readl (&ohci->regs->intrenable) | OHCI_INTR_RD,
&ohci->regs->intrenable);
/* Suspend chip and let things settle down a bit */
spin_lock_irq (&ohci->lock);
ohci->hc_control = OHCI_USB_SUSPEND;
writel (ohci->hc_control, &ohci->regs->control);
(void) readl (&ohci->regs->control);
spin_unlock_irq (&ohci->lock);
set_current_state (TASK_UNINTERRUPTIBLE);
schedule_timeout (HZ/2);
tmp = readl (&ohci->regs->control) | OHCI_CTRL_HCFS;
switch (tmp) {
case OHCI_USB_RESET:
case OHCI_USB_RESUME:
case OHCI_USB_OPER:
ohci_err (ohci, "can't suspend; hcfs %d\n", tmp);
break;
case OHCI_USB_SUSPEND:
ohci_dbg (ohci, "suspended\n");
break;
}
/* In some rare situations, Apple's OHCI have happily trashed
* memory during sleep. We disable its bus master bit during
* suspend
*/
pci_read_config_word (to_pci_dev(hcd->self.controller), PCI_COMMAND,
&cmd);
cmd &= ~PCI_COMMAND_MASTER;
pci_write_config_word (to_pci_dev(hcd->self.controller), PCI_COMMAND,
cmd);
#ifdef CONFIG_PMAC_PBOOK
{ {
struct device_node *of_node; struct device_node *of_node;
...@@ -202,7 +154,6 @@ static int ohci_pci_suspend (struct usb_hcd *hcd, u32 state) ...@@ -202,7 +154,6 @@ static int ohci_pci_suspend (struct usb_hcd *hcd, u32 state)
static int ohci_pci_resume (struct usb_hcd *hcd) static int ohci_pci_resume (struct usb_hcd *hcd)
{ {
struct ohci_hcd *ohci = hcd_to_ohci (hcd); struct ohci_hcd *ohci = hcd_to_ohci (hcd);
int temp;
int retval = 0; int retval = 0;
#ifdef CONFIG_PMAC_PBOOK #ifdef CONFIG_PMAC_PBOOK
...@@ -215,90 +166,25 @@ static int ohci_pci_resume (struct usb_hcd *hcd) ...@@ -215,90 +166,25 @@ static int ohci_pci_resume (struct usb_hcd *hcd)
pmac_call_feature (PMAC_FTR_USB_ENABLE, of_node, 0, 1); pmac_call_feature (PMAC_FTR_USB_ENABLE, of_node, 0, 1);
} }
#endif #endif
/* did we suspend, or were we powered off? */
ohci->hc_control = readl (&ohci->regs->control);
temp = ohci->hc_control & OHCI_CTRL_HCFS;
#ifdef DEBUG /* resume root hub */
/* the registers may look crazy here */ while (time_before (jiffies, ohci->next_statechange))
ohci_dump_status (ohci, 0, 0); msec_delay (100);
#ifdef CONFIG_USB_SUSPEND
/* get extra cleanup even if remote wakeup isn't in use */
retval = usb_resume_device (hcd->self.root_hub);
#else
down (&hcd->self.root_hub->serialize);
retval = ohci_hub_resume (hcd);
up (&hcd->self.root_hub->serialize);
#endif #endif
/* Re-enable bus mastering */ if (retval == 0) {
pci_set_master (to_pci_dev(ohci->hcd.self.controller)); hcd->self.controller->power.power_state = 0;
switch (temp) {
case OHCI_USB_RESET: // lost power
restart:
ohci_info (ohci, "USB restart\n");
retval = hc_restart (ohci);
break;
case OHCI_USB_SUSPEND: // host wakeup
case OHCI_USB_RESUME: // remote wakeup
ohci_info (ohci, "USB continue from %s wakeup\n",
(temp == OHCI_USB_SUSPEND)
? "host" : "remote");
/* we "should" only need RESUME if we're SUSPENDed ... */
ohci->hc_control = OHCI_USB_RESUME;
writel (ohci->hc_control, &ohci->regs->control);
(void) readl (&ohci->regs->control);
/* Some controllers (lucent) need extra-long delays */
mdelay (35); /* no schedule here ! */
temp = readl (&ohci->regs->control);
temp = ohci->hc_control & OHCI_CTRL_HCFS;
if (temp != OHCI_USB_RESUME) {
ohci_err (ohci, "controller won't resume\n");
/* maybe we can reset */
goto restart;
}
/* Then re-enable operations */
writel (OHCI_USB_OPER, &ohci->regs->control);
(void) readl (&ohci->regs->control);
mdelay (3);
spin_lock_irq (&ohci->lock);
ohci->hc_control = OHCI_CONTROL_INIT | OHCI_USB_OPER;
if (!ohci->ed_rm_list) {
if (ohci->ed_controltail)
ohci->hc_control |= OHCI_CTRL_CLE;
if (ohci->ed_bulktail)
ohci->hc_control |= OHCI_CTRL_BLE;
}
if (hcd_to_bus (&ohci->hcd)->bandwidth_isoc_reqs
|| hcd_to_bus (&ohci->hcd)->bandwidth_int_reqs)
ohci->hc_control |= OHCI_CTRL_PLE|OHCI_CTRL_IE;
hcd->state = USB_STATE_RUNNING;
writel (ohci->hc_control, &ohci->regs->control);
/* trigger a start-frame interrupt (why?) */
writel (OHCI_INTR_SF, &ohci->regs->intrstatus);
writel (OHCI_INTR_SF, &ohci->regs->intrenable);
writel (OHCI_INTR_WDH, &ohci->regs->intrdisable);
(void) readl (&ohci->regs->intrdisable);
spin_unlock_irq (&ohci->lock);
#ifdef CONFIG_PMAC_PBOOK #ifdef CONFIG_PMAC_PBOOK
if (_machine == _MACH_Pmac) if (_machine == _MACH_Pmac)
enable_irq (to_pci_dev(hcd->self.controller)->irq); enable_irq (to_pci_dev(hcd->self.controller)->irq);
#endif #endif
/* Check for a pending done list */
if (ohci->hcca->done_head)
dl_done_list (ohci, dl_reverse_done_list (ohci), NULL);
writel (OHCI_INTR_WDH, &ohci->regs->intrenable);
/* assume there are TDs on the bulk and control lists */
writel (OHCI_BLF | OHCI_CLF, &ohci->regs->cmdstatus);
break;
default:
ohci_warn (ohci, "odd PCI resume\n");
} }
return retval; return retval;
} }
......
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
...@@ -80,7 +80,7 @@ ...@@ -80,7 +80,7 @@
#define CAN_SCHEDULE_FRAMES 1000 /* how far future frames can be scheduled */ #define CAN_SCHEDULE_FRAMES 1000 /* how far future frames can be scheduled */
struct uhci_frame_list { struct uhci_frame_list {
__u32 frame[UHCI_NUMFRAMES]; u32 frame[UHCI_NUMFRAMES];
void *frame_cpu[UHCI_NUMFRAMES]; void *frame_cpu[UHCI_NUMFRAMES];
...@@ -105,8 +105,8 @@ struct urb_priv; ...@@ -105,8 +105,8 @@ struct urb_priv;
*/ */
struct uhci_qh { struct uhci_qh {
/* Hardware fields */ /* Hardware fields */
__u32 link; /* Next queue */ u32 link; /* Next queue */
__u32 element; /* Queue element pointer */ u32 element; /* Queue element pointer */
/* Software fields */ /* Software fields */
dma_addr_t dma_handle; dma_addr_t dma_handle;
...@@ -185,10 +185,10 @@ struct uhci_qh { ...@@ -185,10 +185,10 @@ struct uhci_qh {
*/ */
struct uhci_td { struct uhci_td {
/* Hardware fields */ /* Hardware fields */
__u32 link; u32 link;
__u32 status; u32 status;
__u32 token; u32 token;
__u32 buffer; u32 buffer;
/* Software fields */ /* Software fields */
dma_addr_t dma_handle; dma_addr_t dma_handle;
...@@ -370,6 +370,8 @@ struct uhci_hcd { ...@@ -370,6 +370,8 @@ struct uhci_hcd {
int rh_numports; int rh_numports;
struct timer_list stall_timer; struct timer_list stall_timer;
wait_queue_head_t waitqh; /* endpoint_disable waiters */
}; };
struct urb_priv { struct urb_priv {
......
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
...@@ -15,3 +15,4 @@ obj-$(CONFIG_USB_SPEEDTOUCH) += speedtch.o ...@@ -15,3 +15,4 @@ obj-$(CONFIG_USB_SPEEDTOUCH) += speedtch.o
obj-$(CONFIG_USB_TEST) += usbtest.o obj-$(CONFIG_USB_TEST) += usbtest.o
obj-$(CONFIG_USB_TIGL) += tiglusb.o obj-$(CONFIG_USB_TIGL) += tiglusb.o
obj-$(CONFIG_USB_USS720) += uss720.o obj-$(CONFIG_USB_USS720) += uss720.o
obj-$(CONFIG_USB_PHIDGETSERVO) += phidgetservo.o
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment