From b71db443ea7998de4dfddce7adad640db70f221c Mon Sep 17 00:00:00 2001 From: David Brownell Date: Wed, 24 Mar 2004 21:43:15 -0800 Subject: [PATCH] USB: USB gadgets can autoconfigure endpoints This adds some code that gadget drivers can call from driver initialization, to simplify the "configure against this hardware" step. Add endpoint autoconfiguration for gadget drivers. Endpoint selection is currently being done with conditional compilation. That doesn't look nice, but more importantly it doesn't work well with the model that some distributions won't be custom-matched to hardware. Say, a PDA distro running on iPaq (pxa2xx_udc) or Axim (mq11xx_udc). This code just makes it easier for drivers to match to hardware at run-time. It's a convenience function for something they could have been doing already, but weren't. --- include/linux/usb_gadget.h | 9 +++++++++ 1 file changed, 9 insertions(+) (limited to 'include/linux') diff --git a/include/linux/usb_gadget.h b/include/linux/usb_gadget.h index 503abfe9a9aa..3ba4e10d0372 100644 --- a/include/linux/usb_gadget.h +++ b/include/linux/usb_gadget.h @@ -731,6 +731,15 @@ int usb_descriptor_fillbuf(void *, unsigned, int usb_gadget_config_buf(const struct usb_config_descriptor *config, void *buf, unsigned buflen, const struct usb_descriptor_header **desc); +/*-------------------------------------------------------------------------*/ + +/* utility wrapping a simple endpoint selection policy */ + +extern struct usb_ep *usb_ep_autoconfig (struct usb_gadget *, + struct usb_endpoint_descriptor *) __init; + +extern void usb_ep_autoconfig_reset (struct usb_gadget *) __init; + #endif /* __KERNEL__ */ #endif /* __LINUX_USB_GADGET_H */ -- cgit v1.2.3 From 69dc1c65dbf699a526337a48b8ca64834f2d2acf Mon Sep 17 00:00:00 2001 From: David Brownell Date: Thu, 25 Mar 2004 20:31:15 -0800 Subject: [PATCH] USB: define USB feature bit indices This patch provides standard symbols for the various USB device and endpoint feature bits, so that drivers can use symbolic names for them. It also changes the code relating to endpoint halts so it uses those symbols. --- drivers/usb/core/hcd.h | 4 ---- drivers/usb/core/message.c | 3 ++- drivers/usb/gadget/goku_udc.c | 3 +-- drivers/usb/gadget/net2280.c | 4 ++-- drivers/usb/misc/usbtest.c | 3 ++- drivers/usb/storage/transport.c | 5 +++-- include/linux/usb_ch9.h | 14 ++++++++++++++ 7 files changed, 24 insertions(+), 12 deletions(-) (limited to 'include/linux') diff --git a/drivers/usb/core/hcd.h b/drivers/usb/core/hcd.h index 798c72279362..57143644b3ac 100644 --- a/drivers/usb/core/hcd.h +++ b/drivers/usb/core/hcd.h @@ -275,10 +275,6 @@ extern int usb_set_address(struct usb_device *dev); #define EndpointOutRequest \ ((USB_DIR_OUT|USB_TYPE_STANDARD|USB_RECIP_INTERFACE)<<8) -/* table 9.6 standard features */ -#define DEVICE_REMOTE_WAKEUP 1 -#define ENDPOINT_HALT 0 - /* class requests from the USB 2.0 hub spec, table 11-15 */ /* GetBusState and SetHubDescriptor are optional, omitted */ #define ClearHubFeature (0x2000 | USB_REQ_CLEAR_FEATURE) diff --git a/drivers/usb/core/message.c b/drivers/usb/core/message.c index 3af6e2108e38..558c974ce5c9 100644 --- a/drivers/usb/core/message.c +++ b/drivers/usb/core/message.c @@ -722,7 +722,8 @@ int usb_clear_halt(struct usb_device *dev, int pipe) * this request for iso endpoints, which can't halt! */ result = usb_control_msg(dev, usb_sndctrlpipe(dev, 0), - USB_REQ_CLEAR_FEATURE, USB_RECIP_ENDPOINT, 0, endp, NULL, 0, + USB_REQ_CLEAR_FEATURE, USB_RECIP_ENDPOINT, + USB_ENDPOINT_HALT, endp, NULL, 0, HZ * USB_CTRL_SET_TIMEOUT); /* don't un-halt or force to DATA0 except on success */ diff --git a/drivers/usb/gadget/goku_udc.c b/drivers/usb/gadget/goku_udc.c index f39a67c1ba5e..61ce5b2eea41 100644 --- a/drivers/usb/gadget/goku_udc.c +++ b/drivers/usb/gadget/goku_udc.c @@ -1562,8 +1562,7 @@ static void ep0_setup(struct goku_udc *dev) if (dev->ep[tmp].is_in) goto stall; } - /* endpoint halt */ - if (ctrl.wValue != 0) + if (ctrl.wValue != USB_ENDPOINT_HALT) goto stall; if (tmp) goku_clear_halt(&dev->ep[tmp]); diff --git a/drivers/usb/gadget/net2280.c b/drivers/usb/gadget/net2280.c index 9a71c7b73281..2ae8309600bb 100644 --- a/drivers/usb/gadget/net2280.c +++ b/drivers/usb/gadget/net2280.c @@ -2401,7 +2401,7 @@ static void handle_stat0_irqs (struct net2280 *dev, u32 stat) /* hw handles device features */ if (u.r.bRequestType != USB_RECIP_ENDPOINT) goto delegate; - if (u.r.wValue != 0 /* HALT feature */ + if (u.r.wValue != USB_ENDPOINT_HALT || u.r.wLength != 0) goto do_stall; if ((e = get_ep_by_addr (dev, u.r.wIndex)) == 0) @@ -2418,7 +2418,7 @@ static void handle_stat0_irqs (struct net2280 *dev, u32 stat) /* hw handles device features */ if (u.r.bRequestType != USB_RECIP_ENDPOINT) goto delegate; - if (u.r.wValue != 0 /* HALT feature */ + if (u.r.wValue != USB_ENDPOINT_HALT || u.r.wLength != 0) goto do_stall; if ((e = get_ep_by_addr (dev, u.r.wIndex)) == 0) diff --git a/drivers/usb/misc/usbtest.c b/drivers/usb/misc/usbtest.c index 646a7884f496..f7be4dc9a06c 100644 --- a/drivers/usb/misc/usbtest.c +++ b/drivers/usb/misc/usbtest.c @@ -1169,7 +1169,8 @@ static int test_halt (int ep, struct urb *urb) /* set halt (protocol test only), verify it worked */ retval = usb_control_msg (urb->dev, usb_sndctrlpipe (urb->dev, 0), - USB_REQ_SET_FEATURE, USB_RECIP_ENDPOINT, 0, ep, + USB_REQ_SET_FEATURE, USB_RECIP_ENDPOINT, + USB_ENDPOINT_HALT, ep, NULL, 0, HZ * USB_CTRL_SET_TIMEOUT); if (retval < 0) { dbg ("ep %02x couldn't set halt, %d", ep, retval); diff --git a/drivers/usb/storage/transport.c b/drivers/usb/storage/transport.c index c0c05910fafb..bb738c8d99e5 100644 --- a/drivers/usb/storage/transport.c +++ b/drivers/usb/storage/transport.c @@ -256,8 +256,9 @@ int usb_stor_clear_halt(struct us_data *us, unsigned int pipe) endp |= USB_DIR_IN; result = usb_stor_control_msg(us, us->send_ctrl_pipe, - USB_REQ_CLEAR_FEATURE, USB_RECIP_ENDPOINT, 0, - endp, NULL, 0, 3*HZ); + USB_REQ_CLEAR_FEATURE, USB_RECIP_ENDPOINT, + USB_ENDPOINT_HALT, endp, + NULL, 0, 3*HZ); /* reset the toggles and endpoint flags */ usb_endpoint_running(us->pusb_dev, usb_pipeendpoint(pipe), diff --git a/include/linux/usb_ch9.h b/include/linux/usb_ch9.h index 682e95114870..2a9e19b10c64 100644 --- a/include/linux/usb_ch9.h +++ b/include/linux/usb_ch9.h @@ -68,6 +68,20 @@ #define USB_REQ_SET_INTERFACE 0x0B #define USB_REQ_SYNCH_FRAME 0x0C +/* + * USB feature flags are written using USB_REQ_{CLEAR,SET}_FEATURE, and + * are read as a bit array returned by USB_REQ_GET_STATUS. (So there + * are at most sixteen features of each type.) + */ +#define USB_DEVICE_SELF_POWERED 0 /* (read only) */ +#define USB_DEVICE_REMOTE_WAKEUP 1 /* dev may initiate wakeup */ +#define USB_DEVICE_TEST_MODE 2 /* (high speed only) */ +#define USB_DEVICE_B_HNP_ENABLE 3 /* dev may initiate HNP */ +#define USB_DEVICE_A_HNP_SUPPORT 4 /* RH port supports HNP */ +#define USB_DEVICE_A_ALT_HNP_SUPPORT 5 /* other RH port does */ + +#define USB_ENDPOINT_HALT 0 /* IN/OUT will STALL */ + /** * struct usb_ctrlrequest - SETUP data for a USB device control request -- cgit v1.2.3 From 10766ddeeaf441451237794ad6c0a7ae6dcbf1f1 Mon Sep 17 00:00:00 2001 From: David Brownell Date: Mon, 29 Mar 2004 17:25:17 -0800 Subject: [PATCH] USB: set_configuration locking cleanups I've posted all these before, the only notable change is treating that one gphoto2 case as warn-and-continue rather than return-with-failure. usb_set_configuration() cleanup * Remove it from the USB kernel driver API. No drivers need it now, and the sysadmin can change bConfigurationValue using sysfs (say, when hotplugging an otherwise problematic device). * Simpler/cleaner locking: caller must own dev->serialize. * Access from usbfs now uses usb_reset_configuration() sometimes, preventing sysfs thrash, and warns about some dangerous usage (which gphoto2 and other programs may be relying on). (This is from Alan Stern, but I morphed an error return into a warning.) * Prevent a couple potential "no configuration" oopses. (Alan's?) * Remove one broken call from usbcore, in the "device morphed" path of usb_reset_device(). This should be more polite now, hanging that one device rather than khubd. --- drivers/usb/core/devio.c | 47 ++++++++++++++++++++++++++++++++++++++++++++- drivers/usb/core/driverfs.c | 2 ++ drivers/usb/core/hub.c | 34 ++++++++------------------------ drivers/usb/core/message.c | 7 ++----- drivers/usb/core/usb.c | 4 ++++ drivers/usb/core/usb.h | 1 + include/linux/usb.h | 1 - 7 files changed, 63 insertions(+), 33 deletions(-) (limited to 'include/linux') diff --git a/drivers/usb/core/devio.c b/drivers/usb/core/devio.c index 1a2650ac5e3c..a3655d0e7ccb 100644 --- a/drivers/usb/core/devio.c +++ b/drivers/usb/core/devio.c @@ -430,6 +430,8 @@ static int findintfep(struct usb_device *dev, unsigned int ep) if (ep & ~(USB_DIR_IN|0xf)) return -EINVAL; + if (!dev->actconfig) + return -ESRCH; for (i = 0; i < dev->actconfig->desc.bNumInterfaces; i++) { iface = dev->actconfig->interface[i]; for (j = 0; j < iface->num_altsetting; j++) { @@ -450,6 +452,8 @@ static int findintfif(struct usb_device *dev, unsigned int ifn) if (ifn & ~0xff) return -EINVAL; + if (!dev->actconfig) + return -ESRCH; for (i = 0; i < dev->actconfig->desc.bNumInterfaces; i++) { if (dev->actconfig->interface[i]-> altsetting[0].desc.bInterfaceNumber == ifn) @@ -766,10 +770,51 @@ static int proc_setintf(struct dev_state *ps, void __user *arg) static int proc_setconfig(struct dev_state *ps, void __user *arg) { unsigned int u; + int status = 0; + struct usb_host_config *actconfig; if (get_user(u, (unsigned int __user *)arg)) return -EFAULT; - return usb_set_configuration(ps->dev, u); + + down(&ps->dev->serialize); + actconfig = ps->dev->actconfig; + + /* Don't touch the device if any interfaces are claimed. + * It could interfere with other drivers' operations, and if + * an interface is claimed by usbfs it could easily deadlock. + */ + if (actconfig) { + int i; + + for (i = 0; i < actconfig->desc.bNumInterfaces; ++i) { + if (usb_interface_claimed(actconfig->interface[i])) { + dev_warn (&ps->dev->dev, + "usbfs: interface %d claimed " + "while '%s' sets config #%d\n", + actconfig->interface[i] + ->cur_altsetting + ->desc.bInterfaceNumber, + current->comm, u); +#if 0 /* FIXME: enable in 2.6.10 or so */ + status = -EBUSY; + break; +#endif + } + } + } + + /* SET_CONFIGURATION is often abused as a "cheap" driver reset, + * so avoid usb_set_configuration()'s kick to sysfs + */ + if (status == 0) { + if (actconfig && actconfig->desc.bConfigurationValue == u) + status = usb_reset_configuration(ps->dev); + else + status = usb_set_configuration(ps->dev, u); + } + up(&ps->dev->serialize); + + return status; } static int proc_submiturb(struct dev_state *ps, void __user *arg) diff --git a/drivers/usb/core/driverfs.c b/drivers/usb/core/driverfs.c index ed14bb6987b9..2284803bdeed 100644 --- a/drivers/usb/core/driverfs.c +++ b/drivers/usb/core/driverfs.c @@ -55,7 +55,9 @@ set_bConfigurationValue (struct device *dev, const char *buf, size_t count) if (sscanf (buf, "%u", &config) != 1 || config > 255) return -EINVAL; + down(&udev->serialize); value = usb_set_configuration (udev, config); + up(&udev->serialize); return (value < 0) ? value : count; } diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c index e8abb6929099..86adfdd5d439 100644 --- a/drivers/usb/core/hub.c +++ b/drivers/usb/core/hub.c @@ -1299,33 +1299,15 @@ int usb_physical_reset_device(struct usb_device *dev) kfree(descriptor); usb_destroy_configuration(dev); - ret = usb_get_device_descriptor(dev, sizeof(dev->descriptor)); - if (ret != sizeof(dev->descriptor)) { - if (ret < 0) - err("unable to get device %s descriptor " - "(error=%d)", dev->devpath, ret); - else - err("USB device %s descriptor short read " - "(expected %Zi, got %i)", - dev->devpath, - sizeof(dev->descriptor), ret); - - clear_bit(dev->devnum, dev->bus->devmap.devicemap); - dev->devnum = -1; - return -EIO; - } + /* FIXME Linux doesn't yet handle these "device morphed" + * paths. DFU variants need this to work ... and they + * include the "config descriptors changed" case this + * doesn't yet detect! + */ + dev->state = USB_STATE_NOTATTACHED; + dev_err(&dev->dev, "device morphed (DFU?), nyet supported\n"); - ret = usb_get_configuration(dev); - if (ret < 0) { - err("unable to get configuration (error=%d)", ret); - usb_destroy_configuration(dev); - clear_bit(dev->devnum, dev->bus->devmap.devicemap); - dev->devnum = -1; - return 1; - } - - usb_set_configuration(dev, dev->config[0].desc.bConfigurationValue); - return 1; + return -ENODEV; } kfree(descriptor); diff --git a/drivers/usb/core/message.c b/drivers/usb/core/message.c index 558c974ce5c9..5d9e3a88a80e 100644 --- a/drivers/usb/core/message.c +++ b/drivers/usb/core/message.c @@ -1075,11 +1075,11 @@ int usb_reset_configuration(struct usb_device *dev) return 0; } -/** +/* * usb_set_configuration - Makes a particular device setting be current * @dev: the device whose configuration is being updated * @configuration: the configuration being chosen. - * Context: !in_interrupt () + * Context: !in_interrupt(), caller holds dev->serialize * * This is used to enable non-default device modes. Not all devices * use this kind of configurability; many devices only have one @@ -1115,7 +1115,6 @@ int usb_set_configuration(struct usb_device *dev, int configuration) struct usb_host_config *cp = NULL; /* dev->serialize guards all config changes */ - down(&dev->serialize); for (i=0; idescriptor.bNumConfigurations; i++) { if (dev->config[i].desc.bConfigurationValue == configuration) { @@ -1191,7 +1190,6 @@ int usb_set_configuration(struct usb_device *dev, int configuration) } out: - up(&dev->serialize); return ret; } @@ -1300,6 +1298,5 @@ EXPORT_SYMBOL(usb_string); // synchronous calls that also maintain usbcore state EXPORT_SYMBOL(usb_clear_halt); EXPORT_SYMBOL(usb_reset_configuration); -EXPORT_SYMBOL(usb_set_configuration); EXPORT_SYMBOL(usb_set_interface); diff --git a/drivers/usb/core/usb.c b/drivers/usb/core/usb.c index 05dd49c24a61..0018f343c6c0 100644 --- a/drivers/usb/core/usb.c +++ b/drivers/usb/core/usb.c @@ -1173,10 +1173,13 @@ int usb_new_device(struct usb_device *dev) usb_show_string(dev, "SerialNumber", dev->descriptor.iSerialNumber); #endif + down(&dev->serialize); + /* put device-specific files into sysfs */ err = device_add (&dev->dev); if (err) { dev_err(&dev->dev, "can't device_add, error %d\n", err); + up(&dev->serialize); goto fail; } usb_create_driverfs_dev_files (dev); @@ -1211,6 +1214,7 @@ int usb_new_device(struct usb_device *dev) dev->descriptor.bNumConfigurations); } err = usb_set_configuration(dev, config); + up(&dev->serialize); if (err) { dev_err(&dev->dev, "can't set config #%d, error %d\n", config, err); diff --git a/drivers/usb/core/usb.h b/drivers/usb/core/usb.h index 499d9ae5642e..5126d551ce39 100644 --- a/drivers/usb/core/usb.h +++ b/drivers/usb/core/usb.h @@ -17,6 +17,7 @@ extern void usb_enable_interface (struct usb_device *dev, extern int usb_get_device_descriptor(struct usb_device *dev, unsigned int size); +extern int usb_set_configuration(struct usb_device *dev, int configuration); /* for labeling diagnostics */ extern const char *usbcore_name; diff --git a/include/linux/usb.h b/include/linux/usb.h index e4b60511fc46..f6c4c170750d 100644 --- a/include/linux/usb.h +++ b/include/linux/usb.h @@ -904,7 +904,6 @@ extern int usb_string(struct usb_device *dev, int index, /* wrappers that also update important state inside usbcore */ extern int usb_clear_halt(struct usb_device *dev, int pipe); extern int usb_reset_configuration(struct usb_device *dev); -extern int usb_set_configuration(struct usb_device *dev, int configuration); extern int usb_set_interface(struct usb_device *dev, int ifnum, int alternate); /* -- cgit v1.2.3 From e0d47b758e7645993c24c9ebe967c4735b9f0741 Mon Sep 17 00:00:00 2001 From: David Brownell Date: Tue, 30 Mar 2004 21:35:04 -0800 Subject: [PATCH] USB: remove usb_interface.driver field Remove usb_interface.driver, and along with it the "half bound" state previously associated with drivers binding with claim() instead of probe(). This changes usb_driver_claim_interface() semantics slightly: drivers must now be prepared to accept disconnect() callbacks. Fixes more locking bugs, and a claim() oops that snuck in with a recent patch. --- drivers/usb/core/devices.c | 4 ++- drivers/usb/core/devio.c | 90 ++++++++++++++++++---------------------------- drivers/usb/core/message.c | 25 +++++++++++-- drivers/usb/core/usb.c | 87 +++++++++++++++----------------------------- include/linux/usb.h | 18 ++++++++-- 5 files changed, 103 insertions(+), 121 deletions(-) (limited to 'include/linux') diff --git a/drivers/usb/core/devices.c b/drivers/usb/core/devices.c index 704ee35c0075..aa6f7baa6048 100644 --- a/drivers/usb/core/devices.c +++ b/drivers/usb/core/devices.c @@ -247,7 +247,9 @@ static char *usb_dump_interface_descriptor(char *start, char *end, const struct class_decode(desc->bInterfaceClass), desc->bInterfaceSubClass, desc->bInterfaceProtocol, - iface->driver ? iface->driver->name : "(none)"); + iface->dev.driver + ? iface->dev.driver->name + : "(none)"); up_read(&usb_bus_type.subsys.rwsem); return start; } diff --git a/drivers/usb/core/devio.c b/drivers/usb/core/devio.c index a3655d0e7ccb..ece587efd575 100644 --- a/drivers/usb/core/devio.c +++ b/drivers/usb/core/devio.c @@ -704,9 +704,9 @@ static int proc_getdriver(struct dev_state *ps, void __user *arg) if ((ret = findintfif(ps->dev, gd.interface)) < 0) return ret; interface = ps->dev->actconfig->interface[ret]; - if (!interface->driver) + if (!interface->dev.driver) return -ENODATA; - strcpy(gd.driver, interface->driver->name); + strncpy(gd.driver, interface->dev.driver->name, sizeof(gd.driver)); if (copy_to_user(arg, &gd, sizeof(gd))) return -EFAULT; return 0; @@ -725,26 +725,11 @@ static int proc_connectinfo(struct dev_state *ps, void __user *arg) static int proc_resetdevice(struct dev_state *ps) { - int i, ret; - - ret = usb_reset_device(ps->dev); - if (ret < 0) - return ret; - - for (i = 0; i < ps->dev->actconfig->desc.bNumInterfaces; i++) { - struct usb_interface *intf = ps->dev->actconfig->interface[i]; - - /* Don't simulate interfaces we've claimed */ - if (test_bit(i, &ps->ifclaimed)) - continue; - - err ("%s - this function is broken", __FUNCTION__); - if (intf->driver && ps->dev) { - usb_probe_interface (&intf->dev); - } - } + /* FIXME when usb_reset_device() is fixed we'll need to grab + * ps->dev->serialize before calling it. + */ + return usb_reset_device(ps->dev); - return 0; } static int proc_setintf(struct dev_state *ps, void __user *arg) @@ -758,7 +743,7 @@ static int proc_setintf(struct dev_state *ps, void __user *arg) if ((ret = findintfif(ps->dev, setintf.interface)) < 0) return ret; interface = ps->dev->actconfig->interface[ret]; - if (interface->driver) { + if (interface->dev.driver) { if ((ret = checkintf(ps, ret))) return ret; } @@ -1141,58 +1126,51 @@ static int proc_ioctl (struct dev_state *ps, void __user *arg) } } - if (!ps->dev) - retval = -ENODEV; - else if (!(ifp = usb_ifnum_to_if (ps->dev, ctrl.ifno))) + if (!ps->dev) { + if (buf) + kfree(buf); + return -ENODEV; + } + + down(&ps->dev->serialize); + if (ps->dev->state != USB_STATE_CONFIGURED) + retval = -ENODEV; + else if (!(ifp = usb_ifnum_to_if (ps->dev, ctrl.ifno))) retval = -EINVAL; - else switch (ctrl.ioctl_code) { - - /* disconnect kernel driver from interface, leaving it unbound. */ - /* maybe unbound - you get no guarantee it stays unbound */ - case USBDEVFS_DISCONNECT: - /* this function is misdesigned - retained for compatibility */ - lock_kernel(); - driver = ifp->driver; - if (driver) { - dbg ("disconnect '%s' from dev %d interface %d", - driver->name, ps->dev->devnum, ctrl.ifno); - usb_unbind_interface(&ifp->dev); + else switch (ctrl.ioctl_code) { + + /* disconnect kernel driver from interface */ + case USBDEVFS_DISCONNECT: + down_write(&usb_bus_type.subsys.rwsem); + if (ifp->dev.driver) { + driver = to_usb_driver(ifp->dev.driver); + dev_dbg (&ifp->dev, "disconnect by usbfs\n"); + usb_driver_release_interface(driver, ifp); } else retval = -ENODATA; - unlock_kernel(); + up_write(&usb_bus_type.subsys.rwsem); break; /* let kernel drivers try to (re)bind to the interface */ case USBDEVFS_CONNECT: - lock_kernel(); - retval = usb_probe_interface (&ifp->dev); - unlock_kernel(); + bus_rescan_devices(ifp->dev.bus); break; /* talk directly to the interface's driver */ default: - /* BKL used here to protect against changing the binding - * of this driver to this device, as well as unloading its - * driver module. - */ - lock_kernel (); - driver = ifp->driver; + down_read(&usb_bus_type.subsys.rwsem); + if (ifp->dev.driver) + driver = to_usb_driver(ifp->dev.driver); if (driver == 0 || driver->ioctl == 0) { - unlock_kernel(); - retval = -ENOSYS; + retval = -ENOTTY; } else { - if (!try_module_get (driver->owner)) { - unlock_kernel(); - retval = -ENOSYS; - break; - } - unlock_kernel (); retval = driver->ioctl (ifp, ctrl.ioctl_code, buf); if (retval == -ENOIOCTLCMD) retval = -ENOTTY; - module_put (driver->owner); } + up_read(&usb_bus_type.subsys.rwsem); } + up(&ps->dev->serialize); /* cleanup and return */ if (retval >= 0 diff --git a/drivers/usb/core/message.c b/drivers/usb/core/message.c index 5d9e3a88a80e..4b1641d5f47f 100644 --- a/drivers/usb/core/message.c +++ b/drivers/usb/core/message.c @@ -1176,15 +1176,34 @@ int usb_set_configuration(struct usb_device *dev, int configuration) intf->dev.bus = &usb_bus_type; intf->dev.dma_mask = dev->dev.dma_mask; intf->dev.release = release_interface; + device_initialize (&intf->dev); sprintf (&intf->dev.bus_id[0], "%d-%s:%d.%d", dev->bus->busnum, dev->devpath, configuration, alt->desc.bInterfaceNumber); + } + + /* Now that all interfaces are setup, probe() calls + * may claim() any interface that's not yet bound. + * Many class drivers need that: CDC, audio, video, etc. + */ + for (i = 0; i < cp->desc.bNumInterfaces; ++i) { + struct usb_interface *intf = cp->interface[i]; + struct usb_interface_descriptor *desc; + + desc = &intf->altsetting [0].desc; dev_dbg (&dev->dev, - "registering %s (config #%d, interface %d)\n", + "adding %s (config #%d, interface %d)\n", intf->dev.bus_id, configuration, - alt->desc.bInterfaceNumber); - device_register (&intf->dev); + desc->bInterfaceNumber); + ret = device_add (&intf->dev); + if (ret != 0) { + dev_err(&dev->dev, + "device_add(%s) --> %d\n", + intf->dev.bus_id, + ret); + continue; + } usb_create_driverfs_intf_files (intf); } } diff --git a/drivers/usb/core/usb.c b/drivers/usb/core/usb.c index 0018f343c6c0..bb2afb5de2aa 100644 --- a/drivers/usb/core/usb.c +++ b/drivers/usb/core/usb.c @@ -7,8 +7,7 @@ * (C) Copyright Gregory P. Smith 1999 * (C) Copyright Deti Fliegl 1999 (new USB architecture) * (C) Copyright Randy Dunlap 2000 - * (C) Copyright David Brownell 2000-2001 (kernel hotplug, usb_device_id, - more docs, etc) + * (C) Copyright David Brownell 2000-2004 * (C) Copyright Yggdrasil Computing, Inc. 2000 * (usb_device_id matching changes by Adam J. Richter) * (C) Copyright Greg Kroah-Hartman 2002-2003 @@ -95,17 +94,11 @@ int usb_probe_interface(struct device *dev) if (!driver->probe) return error; - /* driver claim() doesn't yet affect dev->driver... */ - if (intf->driver) - return error; - id = usb_match_id (intf, driver->id_table); if (id) { dev_dbg (dev, "%s - got id\n", __FUNCTION__); error = driver->probe (intf, id); } - if (!error) - intf->driver = driver; return error; } @@ -114,7 +107,7 @@ int usb_probe_interface(struct device *dev) int usb_unbind_interface(struct device *dev) { struct usb_interface *intf = to_usb_interface(dev); - struct usb_driver *driver = intf->driver; + struct usb_driver *driver = to_usb_driver(intf->dev.driver); /* release all urbs for this interface */ usb_disable_interface(interface_to_usbdev(intf), intf); @@ -127,7 +120,6 @@ int usb_unbind_interface(struct device *dev) intf->altsetting[0].desc.bInterfaceNumber, 0); usb_set_intfdata(intf, NULL); - intf->driver = NULL; return 0; } @@ -290,7 +282,8 @@ usb_epnum_to_ep_desc(struct usb_device *dev, unsigned epnum) /** * usb_driver_claim_interface - bind a driver to an interface * @driver: the driver to be bound - * @iface: the interface to which it will be bound + * @iface: the interface to which it will be bound; must be in the + * usb device's active configuration * @priv: driver data associated with that interface * * This is used by usb device drivers that need to claim more than one @@ -308,75 +301,52 @@ usb_epnum_to_ep_desc(struct usb_device *dev, unsigned epnum) */ int usb_driver_claim_interface(struct usb_driver *driver, struct usb_interface *iface, void* priv) { - if (!iface || !driver) - return -EINVAL; + struct device *dev = &iface->dev; - if (iface->driver) + if (dev->driver) return -EBUSY; - /* FIXME should device_bind_driver() */ - iface->driver = driver; + dev->driver = &driver->driver; usb_set_intfdata(iface, priv); - return 0; -} -/** - * usb_interface_claimed - returns true iff an interface is claimed - * @iface: the interface being checked - * - * This should be used by drivers to check other interfaces to see if - * they are available or not. If another driver has claimed the interface, - * they may not claim it. Otherwise it's OK to claim it using - * usb_driver_claim_interface(). - * - * Returns true (nonzero) iff the interface is claimed, else false (zero). - */ -int usb_interface_claimed(struct usb_interface *iface) -{ - if (!iface) - return 0; + /* if interface was already added, bind now; else let + * the future device_add() bind it, bypassing probe() + */ + if (!list_empty (&dev->bus_list)) + device_bind_driver(dev); - return (iface->driver != NULL); -} /* usb_interface_claimed() */ + return 0; +} /** * usb_driver_release_interface - unbind a driver from an interface * @driver: the driver to be unbound * @iface: the interface from which it will be unbound * - * In addition to unbinding the driver, this re-initializes the interface - * by selecting altsetting 0, the default alternate setting. - * * This can be used by drivers to release an interface without waiting - * for their disconnect() methods to be called. - * - * When the USB subsystem disconnect()s a driver from some interface, - * it automatically invokes this method for that interface. That - * means that even drivers that used usb_driver_claim_interface() - * usually won't need to call this. + * for their disconnect() methods to be called. In typical cases this + * also causes the driver disconnect() method to be called. * * This call is synchronous, and may not be used in an interrupt context. - * Callers must own the driver model's usb bus writelock. So driver - * disconnect() entries don't need extra locking, but other call contexts - * may need to explicitly claim that lock. + * Callers must own the usb_device serialize semaphore and the driver model's + * usb bus writelock. So driver disconnect() entries don't need extra locking, + * but other call contexts may need to explicitly claim those locks. */ -void usb_driver_release_interface(struct usb_driver *driver, struct usb_interface *iface) +void usb_driver_release_interface(struct usb_driver *driver, + struct usb_interface *iface) { + struct device *dev = &iface->dev; + /* this should never happen, don't release something that's not ours */ - if (!iface || !iface->driver || iface->driver != driver) + if (!dev->driver || dev->driver != &driver->driver) return; - if (iface->dev.driver) { - /* FIXME should be the ONLY case here */ - device_release_driver(&iface->dev); - return; - } + /* don't disconnect from disconnect(), or before dev_add() */ + if (!list_empty (&dev->driver_list) && !list_empty (&dev->bus_list)) + device_release_driver(dev); - usb_set_interface(interface_to_usbdev(iface), - iface->altsetting[0].desc.bInterfaceNumber, - 0); + dev->driver = NULL; usb_set_intfdata(iface, NULL); - iface->driver = NULL; } /** @@ -1633,7 +1603,6 @@ EXPORT_SYMBOL(usb_get_dev); EXPORT_SYMBOL(usb_hub_tt_clear_buffer); EXPORT_SYMBOL(usb_driver_claim_interface); -EXPORT_SYMBOL(usb_interface_claimed); EXPORT_SYMBOL(usb_driver_release_interface); EXPORT_SYMBOL(usb_match_id); EXPORT_SYMBOL(usb_find_interface); diff --git a/include/linux/usb.h b/include/linux/usb.h index f6c4c170750d..739db8598207 100644 --- a/include/linux/usb.h +++ b/include/linux/usb.h @@ -31,6 +31,7 @@ static __inline__ void wait_ms(unsigned int ms) } struct usb_device; +struct usb_driver; /*-------------------------------------------------------------------------*/ @@ -123,7 +124,6 @@ struct usb_interface { * active alternate setting */ unsigned num_altsetting; /* number of alternate settings */ - struct usb_driver *driver; /* driver */ int minor; /* minor number this interface is bound to */ struct device dev; /* interface specific device info */ struct class_device *class_dev; @@ -318,7 +318,21 @@ extern int usb_get_current_frame_number (struct usb_device *usb_dev); /* used these for multi-interface device registration */ extern int usb_driver_claim_interface(struct usb_driver *driver, struct usb_interface *iface, void* priv); -extern int usb_interface_claimed(struct usb_interface *iface); + +/** + * usb_interface_claimed - returns true iff an interface is claimed + * @iface: the interface being checked + * + * Returns true (nonzero) iff the interface is claimed, else false (zero). + * Callers must own the driver model's usb bus readlock. So driver + * probe() entries don't need extra locking, but other call contexts + * may need to explicitly claim that lock. + * + */ +static int inline usb_interface_claimed(struct usb_interface *iface) { + return (iface->dev.driver != NULL); +} + extern void usb_driver_release_interface(struct usb_driver *driver, struct usb_interface *iface); const struct usb_device_id *usb_match_id(struct usb_interface *interface, -- cgit v1.2.3 From 3ccbb87c83a53639cde9d14854b4976b0fe9e021 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Tue, 30 Mar 2004 21:46:06 -0800 Subject: USB: remove "released" field from struct usb_interface as it is not needed. --- include/linux/usb.h | 3 --- 1 file changed, 3 deletions(-) (limited to 'include/linux') diff --git a/include/linux/usb.h b/include/linux/usb.h index 739db8598207..47a4e5c82af4 100644 --- a/include/linux/usb.h +++ b/include/linux/usb.h @@ -87,8 +87,6 @@ struct usb_host_interface { * number from the USB core by calling usb_register_dev(). * @dev: driver model's view of this device * @class_dev: driver model's class view of this device. - * @released: wait for the interface to be released when changing - * configurations. * * USB device drivers attach to interfaces on a physical device. Each * interface encapsulates a single high level function, such as feeding @@ -127,7 +125,6 @@ struct usb_interface { int minor; /* minor number this interface is bound to */ struct device dev; /* interface specific device info */ struct class_device *class_dev; - struct completion *released; /* wait for release */ }; #define to_usb_interface(d) container_of(d, struct usb_interface, dev) #define interface_to_usbdev(intf) \ -- cgit v1.2.3 From 622d3e6b0db6e4c6fc97a7e02627148176674d76 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Tue, 30 Mar 2004 22:17:38 -0800 Subject: USB: add usb_get_intf() and usb_put_intf() functions as they will be needed. --- drivers/usb/core/usb.c | 36 +++++++++++++++++++++++++++++++++++- include/linux/usb.h | 3 +++ 2 files changed, 38 insertions(+), 1 deletion(-) (limited to 'include/linux') diff --git a/drivers/usb/core/usb.c b/drivers/usb/core/usb.c index 11d5c1b94fce..0da70f7af71d 100644 --- a/drivers/usb/core/usb.c +++ b/drivers/usb/core/usb.c @@ -768,7 +768,7 @@ usb_alloc_dev(struct usb_device *parent, struct usb_bus *bus, unsigned port) * * A pointer to the device with the incremented reference counter is returned. */ -struct usb_device *usb_get_dev (struct usb_device *dev) +struct usb_device *usb_get_dev(struct usb_device *dev) { if (dev) get_device(&dev->dev); @@ -788,6 +788,40 @@ void usb_put_dev(struct usb_device *dev) put_device(&dev->dev); } +/** + * usb_get_intf - increments the reference count of the usb interface structure + * @intf: the interface being referenced + * + * Each live reference to a interface must be refcounted. + * + * Drivers for USB interfaces should normally record such references in + * their probe() methods, when they bind to an interface, and release + * them by calling usb_put_intf(), in their disconnect() methods. + * + * A pointer to the interface with the incremented reference counter is + * returned. + */ +struct usb_interface *usb_get_intf(struct usb_interface *intf) +{ + if (intf) + get_device(&intf->dev); + return intf; +} + +/** + * usb_put_intf - release a use of the usb interface structure + * @intf: interface that's been decremented + * + * Must be called when a user of an interface is finished with it. When the + * last user of the interface calls this function, the memory of the interface + * is freed. + */ +void usb_put_intf(struct usb_interface *intf) +{ + if (intf) + put_device(&intf->dev); +} + static struct usb_device *match_device(struct usb_device *dev, u16 vendor_id, u16 product_id) { diff --git a/include/linux/usb.h b/include/linux/usb.h index 47a4e5c82af4..608e3a297340 100644 --- a/include/linux/usb.h +++ b/include/linux/usb.h @@ -140,6 +140,9 @@ static inline void usb_set_intfdata (struct usb_interface *intf, void *data) dev_set_drvdata(&intf->dev, data); } +struct usb_interface *usb_get_intf(struct usb_interface *intf); +void usb_put_intf(struct usb_interface *intf); + /* this maximum is arbitrary */ #define USB_MAXINTERFACES 32 -- cgit v1.2.3 From f1a40c850b54a213bd746f91bd7c92786fc744ed Mon Sep 17 00:00:00 2001 From: David Brownell Date: Thu, 1 Apr 2004 17:56:28 -0800 Subject: [PATCH] USB: ehci updates: CONFIG_PCI, integrated TT Generalize the driver a bit: - PCI-specific handling is restricted to a small chunk of init code. Non-PCI implementations are in the pipeline. - Merge support from ARC International (Craig Nadler) for their integrated root hub transaction translators (on PCI). Other implementations should be similar. --- drivers/usb/host/Kconfig | 11 ++++++++++ drivers/usb/host/ehci-dbg.c | 6 ++++-- drivers/usb/host/ehci-hcd.c | 50 +++++++++++++++++++++++++++++++++++++-------- drivers/usb/host/ehci-hub.c | 18 ++++++++++++++-- drivers/usb/host/ehci-q.c | 15 +++++++++++--- drivers/usb/host/ehci.h | 40 ++++++++++++++++++++++++++++++++++++ include/linux/pci_ids.h | 3 +++ 7 files changed, 127 insertions(+), 16 deletions(-) (limited to 'include/linux') diff --git a/drivers/usb/host/Kconfig b/drivers/usb/host/Kconfig index 55ec90a82ed6..946898ac46da 100644 --- a/drivers/usb/host/Kconfig +++ b/drivers/usb/host/Kconfig @@ -38,6 +38,17 @@ config USB_EHCI_SPLIT_ISO EHCI or USB 2.0 transaction translator implementations. It should work for ISO-OUT transfers, like audio. +config USB_EHCI_ROOT_HUB_TT + bool "Root Hub Transaction Translators (EXPERIMENTAL)" + depends on USB_EHCI_HCD && EXPERIMENTAL + ---help--- + Some EHCI chips have vendor-specific extensions to integrate + transaction translators, so that no OHCI or UHCI companion + controller is needed. It's safe to say "y" even if your + controller doesn't support this feature. + + This supports the EHCI implementation from ARC International. + config USB_OHCI_HCD tristate "OHCI HCD support" depends on USB diff --git a/drivers/usb/host/ehci-dbg.c b/drivers/usb/host/ehci-dbg.c index 989a37804491..530cfd9f84ca 100644 --- a/drivers/usb/host/ehci-dbg.c +++ b/drivers/usb/host/ehci-dbg.c @@ -628,8 +628,10 @@ show_registers (struct class_device *class_dev, char *buf) /* Capability Registers */ i = HC_VERSION(readl (&ehci->caps->hc_capbase)); temp = scnprintf (next, size, - "PCI device %s\nEHCI %x.%02x, hcd state %d (driver " DRIVER_VERSION ")\n", - pci_name(to_pci_dev(hcd->self.controller)), + "bus %s device %s\n" + "EHCI %x.%02x, hcd state %d (driver " DRIVER_VERSION ")\n", + hcd->self.controller->bus->name, + hcd->self.controller->bus_id, i >> 8, i & 0x0ff, ehci->hcd.state); size -= temp; next += temp; diff --git a/drivers/usb/host/ehci-hcd.c b/drivers/usb/host/ehci-hcd.c index 75d6db2b0540..1e61876ca0a7 100644 --- a/drivers/usb/host/ehci-hcd.c +++ b/drivers/usb/host/ehci-hcd.c @@ -279,6 +279,8 @@ static void ehci_watchdog (unsigned long param) spin_unlock_irqrestore (&ehci->lock, flags); } +#ifdef CONFIG_PCI + /* EHCI 0.96 (and later) section 5.1 says how to kick BIOS/SMM/... * off the controller (maybe it can boot from highspeed USB disks). */ @@ -307,6 +309,8 @@ static int bios_handoff (struct ehci_hcd *ehci, int where, u32 cap) return 0; } +#endif + static int ehci_reboot (struct notifier_block *self, unsigned long code, void *null) { @@ -335,8 +339,12 @@ static int ehci_hc_reset (struct usb_hcd *hcd) dbg_hcs_params (ehci, "reset"); dbg_hcc_params (ehci, "reset"); +#ifdef CONFIG_PCI /* EHCI 0.96 and later may have "extended capabilities" */ - temp = HCC_EXT_CAPS (readl (&ehci->caps->hcc_params)); + if (hcd->self.controller->bus == &pci_bus_type) + temp = HCC_EXT_CAPS (readl (&ehci->caps->hcc_params)); + else + temp = 0; while (temp) { u32 cap; @@ -356,6 +364,7 @@ static int ehci_hc_reset (struct usb_hcd *hcd) } temp = (cap >> 8) & 0xff; } +#endif /* cache this readonly data; minimize PCI reads */ ehci->hcs_params = readl (&ehci->caps->hcs_params); @@ -372,7 +381,7 @@ static int ehci_start (struct usb_hcd *hcd) struct usb_bus *bus; int retval; u32 hcc_params; - u8 tempbyte; + u8 sbrn = 0; init_timer (&ehci->watchdog); ehci->watchdog.function = ehci_watchdog; @@ -406,6 +415,29 @@ static int ehci_start (struct usb_hcd *hcd) writel (INTR_MASK, &ehci->regs->intr_enable); writel (ehci->periodic_dma, &ehci->regs->frame_list); +#ifdef CONFIG_PCI + if (hcd->self.controller->bus == &pci_bus_type) { + struct pci_dev *pdev; + + pdev = to_pci_dev(hcd->self.controller); + + /* Serial Bus Release Number is at PCI 0x60 offset */ + pci_read_config_byte(pdev, 0x60, &sbrn); + + /* help hc dma work well with cachelines */ + pci_set_mwi (pdev); + + /* chip-specific init */ + switch (pdev->vendor) { + case PCI_VENDOR_ID_ARC: + if (pdev->device == PCI_DEVICE_ID_ARC_EHCI) + ehci->is_arc_rh_tt = 1; + break; + } + + } +#endif + /* * dedicate a qh for the async ring head, since we couldn't unlink * a 'real' qh without stopping the async schedule [4.8]. use it @@ -443,9 +475,6 @@ static int ehci_start (struct usb_hcd *hcd) #endif } - /* help hc dma work well with cachelines */ - pci_set_mwi (to_pci_dev(ehci->hcd.self.controller)); - /* clear interrupt enables, set irq latency */ temp = readl (&ehci->regs->command) & 0x0fff; if (log2_irq_thresh < 0 || log2_irq_thresh > 6) @@ -493,12 +522,10 @@ done2: writel (FLAG_CF, &ehci->regs->configured_flag); readl (&ehci->regs->command); /* unblock posted write */ - /* PCI Serial Bus Release Number is at 0x60 offset */ - pci_read_config_byte(to_pci_dev(hcd->self.controller), 0x60, &tempbyte); temp = HC_VERSION(readl (&ehci->caps->hc_capbase)); ehci_info (ehci, "USB %x.%x enabled, EHCI %x.%02x, driver %s\n", - ((tempbyte & 0xf0)>>4), (tempbyte & 0x0f), + ((sbrn & 0xf0)>>4), (sbrn & 0x0f), temp >> 8, temp & 0xff, DRIVER_VERSION); /* @@ -995,7 +1022,9 @@ static const struct hc_driver ehci_driver = { /*-------------------------------------------------------------------------*/ -/* EHCI spec says PCI is required. */ +/* EHCI 1.0 doesn't require PCI */ + +#ifdef CONFIG_PCI /* PCI driver selection metadata; PCI hotplugging uses this */ static const struct pci_device_id pci_ids [] = { { @@ -1021,6 +1050,9 @@ static struct pci_driver ehci_pci_driver = { #endif }; +#endif /* PCI */ + + #define DRIVER_INFO DRIVER_VERSION " " DRIVER_DESC MODULE_DESCRIPTION (DRIVER_INFO); diff --git a/drivers/usb/host/ehci-hub.c b/drivers/usb/host/ehci-hub.c index 2c5cf36c90ba..5c3a0615211c 100644 --- a/drivers/usb/host/ehci-hub.c +++ b/drivers/usb/host/ehci-hub.c @@ -40,6 +40,15 @@ static int check_reset_complete ( /* if reset finished and it's still not enabled -- handoff */ if (!(port_status & PORT_PE)) { + + /* with integrated TT, there's nobody to hand it to! */ + if (ehci_is_ARC(ehci)) { + ehci_dbg (ehci, + "Failed to enable port %d on root hub TT\n", + index+1); + return port_status; + } + ehci_dbg (ehci, "port %d full speed --> companion\n", index + 1); @@ -257,7 +266,8 @@ static int ehci_hub_control ( if (!(temp & PORT_OWNER)) { if (temp & PORT_CONNECT) { status |= 1 << USB_PORT_FEAT_CONNECTION; - status |= 1 << USB_PORT_FEAT_HIGHSPEED; + // status may be from integrated TT + status |= ehci_port_speed(ehci, temp); } if (temp & PORT_PE) status |= 1 << USB_PORT_FEAT_ENABLE; @@ -307,8 +317,12 @@ static int ehci_hub_control ( &ehci->regs->port_status [wIndex]); break; case USB_PORT_FEAT_RESET: - /* 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 + * transaction translator built in. + */ if ((temp & (PORT_PE|PORT_CONNECT)) == PORT_CONNECT + && !ehci_is_ARC(ehci) && PORT_USB11 (temp)) { ehci_dbg (ehci, "port %d low speed --> companion\n", diff --git a/drivers/usb/host/ehci-q.c b/drivers/usb/host/ehci-q.c index 69ca6429af12..fd8634b31231 100644 --- a/drivers/usb/host/ehci-q.c +++ b/drivers/usb/host/ehci-q.c @@ -165,7 +165,10 @@ static void qtd_copy_status ( /* if async CSPLIT failed, try cleaning out the TT buffer */ } else if (urb->dev->tt && !usb_pipeint (urb->pipe) && ((token & QTD_STS_MMF) != 0 - || QTD_CERR(token) == 0)) { + || QTD_CERR(token) == 0) + && (!ehci_is_ARC(ehci) + || urb->dev->tt->hub != + ehci->hcd.self.root_hub)) { #ifdef DEBUG struct usb_device *tt = urb->dev->tt->hub; dev_dbg (&tt->dev, @@ -576,7 +579,7 @@ clear_toggle (struct usb_device *udev, int ep, int is_out, struct ehci_qh *qh) // when each interface/altsetting is established. Unlink // any previous qh and cancel its urbs first; endpoints are // implicitly reset then (data toggle too). -// That'd mean updating how usbcore talks to HCDs. (2.5?) +// That'd mean updating how usbcore talks to HCDs. (2.7?) /* @@ -674,7 +677,13 @@ qh_make ( info2 |= (EHCI_TUNE_MULT_TT << 30); info2 |= urb->dev->ttport << 23; - info2 |= urb->dev->tt->hub->devnum << 16; + + /* set the address of the TT; for ARC's integrated + * root hub tt, leave it zeroed. + */ + if (!ehci_is_ARC(ehci) + || urb->dev->tt->hub != ehci->hcd.self.root_hub) + info2 |= urb->dev->tt->hub->devnum << 16; /* NOTE: if (PIPE_INTERRUPT) { scheduler sets c-mask } */ diff --git a/drivers/usb/host/ehci.h b/drivers/usb/host/ehci.h index 5c6eef9674d6..8cd5b7b2c97a 100644 --- a/drivers/usb/host/ehci.h +++ b/drivers/usb/host/ehci.h @@ -85,6 +85,8 @@ struct ehci_hcd { /* one per controller */ unsigned long actions; unsigned stamp; + unsigned is_arc_rh_tt:1; /* ARC roothub with TT */ + /* irq statistics */ #ifdef EHCI_STATS struct ehci_stats stats; @@ -552,6 +554,44 @@ struct ehci_fstn { /*-------------------------------------------------------------------------*/ +#ifdef CONFIG_USB_EHCI_ROOT_HUB_TT + +/* + * Some EHCI controllers have a Transaction Translator built into the + * root hub. This is a non-standard feature. Each controller will need + * to add code to the following inline functions, and call them as + * needed (mostly in root hub code). + */ + +#define ehci_is_ARC(e) ((e)->is_arc_rh_tt) + +/* Returns the speed of a device attached to a port on the root hub. */ +static inline unsigned int +ehci_port_speed(struct ehci_hcd *ehci, unsigned int portsc) +{ + if (ehci_is_ARC(ehci)) { + switch ((portsc>>26)&3) { + case 0: + return 0; + case 1: + return (1<