From 01ddaf6230a1ac2b1c50f5ebbce052c043687e01 Mon Sep 17 00:00:00 2001 From: David Brownell Date: Thu, 26 Feb 2004 20:12:28 -0800 Subject: [PATCH] USB Gadget: gadget config buffer utilities Adds two new gadget-side utility functions, to support a declarative style of managing usb configuration descriptors. The functions fill buffers from null-terminated vectors of usb descriptors, which are simple to build or update. The "ethernet" gadget driver currently has the most interesting config descriptors. This uses those functions to replace some complex code with simpler static declarations; result, it's cleaner. (And it'll be easier to add RNDIS configurations later, too.) Memory savings (or cost, depending on config) was less than 50 bytes; nothing worth worrying about. --- include/linux/usb_gadget.h | 11 +++++++++++ 1 file changed, 11 insertions(+) (limited to 'include/linux') diff --git a/include/linux/usb_gadget.h b/include/linux/usb_gadget.h index 1cfb33ebf348..78c8382b6398 100644 --- a/include/linux/usb_gadget.h +++ b/include/linux/usb_gadget.h @@ -716,6 +716,17 @@ struct usb_gadget_strings { /* put descriptor for string with that id into buf (buflen >= 256) */ int usb_gadget_get_string (struct usb_gadget_strings *table, int id, u8 *buf); +/*-------------------------------------------------------------------------*/ + +/* utility to simplify managing config descriptors */ + +/* write vector of descriptors into buffer */ +int usb_descriptor_fillbuf(void *, unsigned, + const struct usb_descriptor_header **); + +/* build config descriptor from single descriptor vector */ +int usb_gadget_config_buf(const struct usb_config_descriptor *config, + void *buf, unsigned buflen, const struct usb_descriptor_header **desc); #endif /* __KERNEL__ */ -- cgit v1.2.3 From f285ff9b83f4b75bfe6b6c9920913a0d9c9c95ba Mon Sep 17 00:00:00 2001 From: David Brownell Date: Tue, 2 Mar 2004 18:00:13 -0800 Subject: [PATCH] USB Gadget: make usb gadget strings talk utf-8 Teach gadget/usbstring to expect UTF-8 strings, not ISO-8859/1 ones. This just gets rid of an API issue: no hacks needed for non-Western languages, and multi-language support will be lots easier. Current drivers won't notice the API change, they use US-ASCII (which is a strict superset of both encodings). Future drivers may want to teach utf8_to_utf16le() about the four-byte encodings, so they can emit surrogate pairs for those Unicode characters. --- drivers/usb/gadget/usbstring.c | 92 +++++++++++++++++++++++++++++++++++------- include/linux/usb_gadget.h | 2 +- 2 files changed, 79 insertions(+), 15 deletions(-) (limited to 'include/linux') diff --git a/drivers/usb/gadget/usbstring.c b/drivers/usb/gadget/usbstring.c index 8ccb62ef1cf8..f5fcd1edaf07 100644 --- a/drivers/usb/gadget/usbstring.c +++ b/drivers/usb/gadget/usbstring.c @@ -16,24 +16,89 @@ #include #include +#include + + +static int utf8_to_utf16le(const char *s, u16 *cp, unsigned len) +{ + int count = 0; + u8 c; + u16 uchar; + + /* this insists on correct encodings, though not minimal ones. + * BUT it currently rejects legit 4-byte UTF-8 code points, + * which need surrogate pairs. (Unicode 3.1 can use them.) + */ + while (len != 0 && (c = (u8) *s++) != 0) { + if (unlikely(c & 0x80)) { + // 2-byte sequence: + // 00000yyyyyxxxxxx = 110yyyyy 10xxxxxx + if ((c & 0xe0) == 0xc0) { + uchar = (c & 0x1f) << 6; + + c = (u8) *s++; + if ((c & 0xc0) != 0xc0) + goto fail; + c &= 0x3f; + uchar |= c; + + // 3-byte sequence (most CJKV characters): + // zzzzyyyyyyxxxxxx = 1110zzzz 10yyyyyy 10xxxxxx + } else if ((c & 0xf0) == 0xe0) { + uchar = (c & 0x0f) << 12; + + c = (u8) *s++; + if ((c & 0xc0) != 0xc0) + goto fail; + c &= 0x3f; + uchar |= c << 6; + + c = (u8) *s++; + if ((c & 0xc0) != 0xc0) + goto fail; + c &= 0x3f; + uchar |= c; + + /* no bogus surrogates */ + if (0xd800 <= uchar && uchar <= 0xdfff) + goto fail; + + // 4-byte sequence (surrogate pairs, currently rare): + // 11101110wwwwzzzzyy + 110111yyyyxxxxxx + // = 11110uuu 10uuzzzz 10yyyyyy 10xxxxxx + // (uuuuu = wwww + 1) + // FIXME accept the surrogate code points (only) + + } else + goto fail; + } else + uchar = c; + put_unaligned (cpu_to_le16 (uchar), cp++); + count++; + len--; + } + return count; +fail: + return -1; +} + /** * usb_gadget_get_string - fill out a string descriptor - * @table: of c strings using iso latin/1 characters + * @table: of c strings encoded using UTF-8 * @id: string id, from low byte of wValue in get string descriptor * @buf: at least 256 bytes * - * Finds the iso latin/1 string matching the ID, and converts it into a + * Finds the UTF-8 string matching the ID, and converts it into a * string descriptor in utf16-le. * Returns length of descriptor (always even) or negative errno * - * If your driver needs stings in multiple languages, you'll need to - * to use some alternate solution for languages where the ISO 8859/1 - * (latin/1) character set can't be used. For example, they can't be - * used with Chinese (Big5, GB2312, etc), Japanese, Korean, or many other - * languages. You'd likely "switch (wIndex) { ... }" in your ep0 - * string descriptor logic, using this routine in cases where "western - * european" characters suffice for the strings being returned. + * If your driver needs stings in multiple languages, you'll probably + * "switch (wIndex) { ... }" in your ep0 string descriptor logic, + * using this routine after choosing which set of UTF-8 strings to use. + * Note that US-ASCII is a strict subset of UTF-8; any string bytes with + * the eighth bit set will be multibyte UTF-8 characters, not ISO-8859/1 + * characters (which are also widely used in C strings). */ int usb_gadget_get_string (struct usb_gadget_strings *table, int id, u8 *buf) @@ -59,13 +124,12 @@ usb_gadget_get_string (struct usb_gadget_strings *table, int id, u8 *buf) /* string descriptors have length, tag, then UTF16-LE text */ len = min ((size_t) 126, strlen (s->s)); + memset (buf + 2, 0, 2 * len); /* zero all the bytes */ + len = utf8_to_utf16le(s->s, (u16 *)&buf[2], len); + if (len < 0) + return -EINVAL; buf [0] = (len + 1) * 2; buf [1] = USB_DT_STRING; - memset (buf + 2, 0, 2 * len); /* zero all the high bytes */ - while (len) { - buf [2 * len] = s->s [len - 1]; - len--; - } return buf [0]; } diff --git a/include/linux/usb_gadget.h b/include/linux/usb_gadget.h index 78c8382b6398..041e2540a48e 100644 --- a/include/linux/usb_gadget.h +++ b/include/linux/usb_gadget.h @@ -690,7 +690,7 @@ int usb_gadget_unregister_driver (struct usb_gadget_driver *driver); /** * struct usb_string - wraps a C string and its USB id * @id:the (nonzero) ID for this string - * @s:the string, in ISO-8859/1 characters + * @s:the string, in UTF-8 encoding * * If you're using usb_gadget_get_string(), use this to wrap a string * together with its ID. -- cgit v1.2.3 From aa36bfd289a4e18f6c369e433af013f6598b39ef Mon Sep 17 00:00:00 2001 From: Alan Stern Date: Tue, 2 Mar 2004 19:16:57 -0800 Subject: [PATCH] USB: Don't add/del interfaces, register/unregister them On Fri, 27 Feb 2004, Greg KH wrote: > On Wed, Feb 25, 2004 at 10:05:37AM -0500, Alan Stern wrote: > > > Why would anyone want to do this, you ask? Well the USB subsystem does it > > already. Each USB device can have several configurations, only one of > > which is active at any time. Corresponding to each configuration is a set > > of struct devices, and they (together with their embedded kobjects) are > > allocated and initialized when the USB device is first detected. The > > struct devices are add()'ed and del()'ed as configurations are activated > > and deactivated, leading to just the sort of call sequence shown above. > > Then we need to fix this. The driver model does not support repeated device_add(), device_del(), device_add(), device_del(), ... calls for the same device. But that's what happens to an interface's embedded struct device when we change configurations. Accordingly, this patch changes the device_add()/device_del() calls for interfaces to device_register()/device_unregister(). When the interface is unregistered the new code waits for the release method to run, so that it will be safe to re-register the interface should the former configuration be reinstated. Greg, please check this out to make sure I haven't made some dumb mistake. It works on my system and it fixes a memory leak in the USB system. --- drivers/usb/core/config.c | 9 ++------- drivers/usb/core/message.c | 16 ++++++++++++++-- include/linux/usb.h | 3 +++ 3 files changed, 19 insertions(+), 9 deletions(-) (limited to 'include/linux') diff --git a/drivers/usb/core/config.c b/drivers/usb/core/config.c index 2332526cd803..bab2ef11a7d8 100644 --- a/drivers/usb/core/config.c +++ b/drivers/usb/core/config.c @@ -72,13 +72,10 @@ static int usb_parse_endpoint(struct usb_host_endpoint *endpoint, unsigned char return buffer - buffer0; } -static void usb_release_intf(struct device *dev) +static void usb_free_intf(struct usb_interface *intf) { - struct usb_interface *intf; int j; - intf = to_usb_interface(dev); - if (intf->altsetting) { for (j = 0; j < intf->num_altsetting; j++) { struct usb_host_interface *as = &intf->altsetting[j]; @@ -235,8 +232,6 @@ int usb_parse_configuration(struct usb_host_config *config, char *buffer, int si return -ENOMEM; } memset(interface, 0, sizeof(struct usb_interface)); - interface->dev.release = usb_release_intf; - device_initialize(&interface->dev); } /* Go through the descriptors, checking their length and counting the @@ -374,7 +369,7 @@ void usb_destroy_configuration(struct usb_device *dev) struct usb_interface *ifp = cf->interface[i]; if (ifp) - put_device(&ifp->dev); + usb_free_intf(ifp); } } kfree(dev->config); diff --git a/drivers/usb/core/message.c b/drivers/usb/core/message.c index 08a1e38e72e9..87924db94039 100644 --- a/drivers/usb/core/message.c +++ b/drivers/usb/core/message.c @@ -793,6 +793,13 @@ void usb_disable_interface(struct usb_device *dev, struct usb_interface *intf) } } +static void release_interface(struct device *dev) +{ + struct usb_interface *interface = to_usb_interface(dev); + + complete(interface->released); +} + /* * usb_disable_device - Disable all the endpoints for a USB device * @dev: the device whose endpoints are being disabled @@ -822,12 +829,16 @@ void usb_disable_device(struct usb_device *dev, int skip_ep0) if (dev->actconfig) { for (i = 0; i < dev->actconfig->desc.bNumInterfaces; i++) { struct usb_interface *interface; + struct completion intf_completion; /* remove this interface */ interface = dev->actconfig->interface[i]; dev_dbg (&dev->dev, "unregistering interface %s\n", interface->dev.bus_id); - device_del(&interface->dev); + init_completion (&intf_completion); + interface->released = &intf_completion; + device_unregister (&interface->dev); + wait_for_completion (&intf_completion); } dev->actconfig = 0; if (dev->state == USB_STATE_CONFIGURED) @@ -1145,6 +1156,7 @@ int usb_set_configuration(struct usb_device *dev, int configuration) intf->dev.driver = NULL; intf->dev.bus = &usb_bus_type; intf->dev.dma_mask = dev->dev.dma_mask; + intf->dev.release = release_interface; sprintf (&intf->dev.bus_id[0], "%d-%s:%d.%d", dev->bus->busnum, dev->devpath, configuration, @@ -1153,7 +1165,7 @@ int usb_set_configuration(struct usb_device *dev, int configuration) "registering %s (config #%d, interface %d)\n", intf->dev.bus_id, configuration, desc->bInterfaceNumber); - device_add (&intf->dev); + device_register (&intf->dev); usb_create_driverfs_intf_files (intf); } } diff --git a/include/linux/usb.h b/include/linux/usb.h index 45caca344ca3..6a2e0b7f18d8 100644 --- a/include/linux/usb.h +++ b/include/linux/usb.h @@ -89,6 +89,8 @@ 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 @@ -122,6 +124,7 @@ 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 e8a19690d7b04fde093d7d1d62b2e1f1b1b4db98 Mon Sep 17 00:00:00 2001 From: Alan Stern Date: Tue, 2 Mar 2004 20:49:44 -0800 Subject: [PATCH] USB: Improve handling of altsettings On Sat, 21 Feb 2004, Greg KH wrote: > > One thing that would be good, whether this change gets made or not, is to > > remove all assumptions from drivers about the order in which interfaces > > are stored (use usb_ifnum_to_if()) and the order in which altsettings are > > stored (replace intf.act_altsetting with a pointer and create > > usb_altnum_to_alt() analogous to usb_ifnum_to_if()). There are plenty of > > drivers that will need to be fixed up. > > I'd be glad to take patches to fix up any drivers that still have this > problem right now. Here's a start. This patch begins the conversion process by adding usbcore support for cur_altsetting and deprecating act_altsetting. So long as we assumed that altsetting numbers range from 0 to num_altsetting-1 and that the number matches its index in the altsetting array, there was no harm in using act_altsetting. But without that assumption act_altsetting is merely an invitation to errors. Although the kerneldoc says that act_altsetting is the _index_ of the active altsetting, it's all too easy to confuse it with the _number_ of the active altsetting. Using cur_altsetting instead (a pointer rather than a number) will prevent that confusion. Until all the drivers have been converted to use cur_altsetting, the core will have to maintain act_altsetting in parallel with it. Eventually we will be able to remove act_altsetting, but fixing all the drivers will take a while. Included in this patch: Add cur_altsetting to struct usb_interface and deprecate act_altsetting. Add comments and kerneldoc explaining the changes. Also remove the comments in front of struct usb_host_config (they seem to have been left behind when usb_ch9.h was split out) and add kerneldoc for that structure. Add usb_altnum_to_altsetting() to help look up altsettings based on their number. Convert the usb_set_interface(), usb_set_configuration(), and usb_reset_configuration() routines to support cur_altsetting and act_altsetting in parallel. Convert a few others to use cur_altsetting rather than act_altsetting. Rename a few local variables to make their meaning a little clearer. It would be nice to change struct usb_host_interface to something like usb_host_altsetting, but that's a patch for another time. --- drivers/usb/core/message.c | 68 ++++++++++++++++++++++++++--------------- drivers/usb/core/usb.c | 42 ++++++++++++++++++++----- include/linux/usb.h | 76 ++++++++++++++++++++++++++++++++++------------ 3 files changed, 135 insertions(+), 51 deletions(-) (limited to 'include/linux') diff --git a/drivers/usb/core/message.c b/drivers/usb/core/message.c index 87924db94039..76226e37bb36 100644 --- a/drivers/usb/core/message.c +++ b/drivers/usb/core/message.c @@ -783,13 +783,12 @@ void usb_disable_endpoint(struct usb_device *dev, unsigned int epaddr) */ void usb_disable_interface(struct usb_device *dev, struct usb_interface *intf) { - struct usb_host_interface *hintf = - &intf->altsetting[intf->act_altsetting]; + struct usb_host_interface *alt = intf->cur_altsetting; int i; - for (i = 0; i < hintf->desc.bNumEndpoints; ++i) { + for (i = 0; i < alt->desc.bNumEndpoints; ++i) { usb_disable_endpoint(dev, - hintf->endpoint[i].desc.bEndpointAddress); + alt->endpoint[i].desc.bEndpointAddress); } } @@ -887,12 +886,11 @@ void usb_enable_endpoint(struct usb_device *dev, void usb_enable_interface(struct usb_device *dev, struct usb_interface *intf) { - struct usb_host_interface *hintf = - &intf->altsetting[intf->act_altsetting]; + struct usb_host_interface *alt = intf->cur_altsetting; int i; - for (i = 0; i < hintf->desc.bNumEndpoints; ++i) - usb_enable_endpoint(dev, &hintf->endpoint[i].desc); + for (i = 0; i < alt->desc.bNumEndpoints; ++i) + usb_enable_endpoint(dev, &alt->endpoint[i].desc); } /** @@ -931,6 +929,7 @@ void usb_enable_interface(struct usb_device *dev, int usb_set_interface(struct usb_device *dev, int interface, int alternate) { struct usb_interface *iface; + struct usb_host_interface *alt; int ret; int manual = 0; @@ -940,14 +939,15 @@ int usb_set_interface(struct usb_device *dev, int interface, int alternate) return -EINVAL; } - if (alternate < 0 || alternate >= iface->num_altsetting) + alt = usb_altnum_to_altsetting(iface, alternate); + if (!alt) { + warn("selecting invalid altsetting %d", alternate); return -EINVAL; + } ret = usb_control_msg(dev, usb_sndctrlpipe(dev, 0), USB_REQ_SET_INTERFACE, USB_RECIP_INTERFACE, - iface->altsetting[alternate] - .desc.bAlternateSetting, - interface, NULL, 0, HZ * 5); + alternate, interface, NULL, 0, HZ * 5); /* 9.4.10 says devices don't need this and are free to STALL the * request if the interface only has one alternate setting. @@ -968,7 +968,8 @@ int usb_set_interface(struct usb_device *dev, int interface, int alternate) /* prevent submissions using previous endpoint settings */ usb_disable_interface(dev, iface); - iface->act_altsetting = alternate; + iface->cur_altsetting = alt; + iface->act_altsetting = alt - iface->altsetting; /* If the interface only has one altsetting and the device didn't * accept the request, we attempt to carry out the equivalent action @@ -976,13 +977,11 @@ int usb_set_interface(struct usb_device *dev, int interface, int alternate) * new altsetting. */ if (manual) { - struct usb_host_interface *iface_as = - &iface->altsetting[alternate]; int i; - for (i = 0; i < iface_as->desc.bNumEndpoints; i++) { + for (i = 0; i < alt->desc.bNumEndpoints; i++) { unsigned int epaddr = - iface_as->endpoint[i].desc.bEndpointAddress; + alt->endpoint[i].desc.bEndpointAddress; unsigned int pipe = __create_pipe(dev, USB_ENDPOINT_NUMBER_MASK & epaddr) | (usb_endpoint_out(epaddr) ? USB_DIR_OUT : USB_DIR_IN); @@ -1056,8 +1055,20 @@ int usb_reset_configuration(struct usb_device *dev) /* re-init hc/hcd interface/endpoint state */ for (i = 0; i < config->desc.bNumInterfaces; i++) { struct usb_interface *intf = config->interface[i]; + struct usb_host_interface *alt; + + alt = usb_altnum_to_altsetting(intf, 0); + + /* No altsetting 0? We'll assume the first altsetting. + * We could use a GetInterface call, but if a device is + * so non-compliant that it doesn't have altsetting 0 + * then I wouldn't trust its reply anyway. + */ + if (!alt) + alt = &intf->altsetting[0]; - intf->act_altsetting = 0; + intf->cur_altsetting = alt; + intf->act_altsetting = alt - intf->altsetting; usb_enable_interface(dev, intf); } return 0; @@ -1146,12 +1157,21 @@ int usb_set_configuration(struct usb_device *dev, int configuration) */ for (i = 0; i < cp->desc.bNumInterfaces; ++i) { struct usb_interface *intf = cp->interface[i]; - struct usb_interface_descriptor *desc; + struct usb_host_interface *alt; - intf->act_altsetting = 0; - desc = &intf->altsetting [0].desc; - usb_enable_interface(dev, intf); + alt = usb_altnum_to_altsetting(intf, 0); + + /* No altsetting 0? We'll assume the first altsetting. + * We could use a GetInterface call, but if a device is + * so non-compliant that it doesn't have altsetting 0 + * then I wouldn't trust its reply anyway. + */ + if (!alt) + alt = &intf->altsetting[0]; + intf->cur_altsetting = alt; + intf->act_altsetting = alt - intf->altsetting; + usb_enable_interface(dev, intf); intf->dev.parent = &dev->dev; intf->dev.driver = NULL; intf->dev.bus = &usb_bus_type; @@ -1160,11 +1180,11 @@ int usb_set_configuration(struct usb_device *dev, int configuration) sprintf (&intf->dev.bus_id[0], "%d-%s:%d.%d", dev->bus->busnum, dev->devpath, configuration, - desc->bInterfaceNumber); + alt->desc.bInterfaceNumber); dev_dbg (&dev->dev, "registering %s (config #%d, interface %d)\n", intf->dev.bus_id, configuration, - desc->bInterfaceNumber); + alt->desc.bInterfaceNumber); device_register (&intf->dev); usb_create_driverfs_intf_files (intf); } diff --git a/drivers/usb/core/usb.c b/drivers/usb/core/usb.c index f2ecd4308e0c..2ccb746c1f7a 100644 --- a/drivers/usb/core/usb.c +++ b/drivers/usb/core/usb.c @@ -189,7 +189,7 @@ void usb_deregister(struct usb_driver *driver) } /** - * usb_ifnum_to_if - get the interface object with a given interface number (usbcore-internal) + * usb_ifnum_to_if - get the interface object with a given interface number * @dev: the device whose current configuration is considered * @ifnum: the desired interface * @@ -219,6 +219,33 @@ struct usb_interface *usb_ifnum_to_if(struct usb_device *dev, unsigned ifnum) return NULL; } +/** + * usb_altnum_to_altsetting - get the altsetting structure with a given + * alternate setting number. + * @intf: the interface containing the altsetting in question + * @altnum: the desired alternate setting number + * + * This searches the altsetting array of the specified interface for + * an entry with the correct bAlternateSetting value and returns a pointer + * to that entry, or null. + * + * Note that altsettings need not be stored sequentially by number, so + * it would be incorrect to assume that the first altsetting entry in + * the array corresponds to altsetting zero. This routine helps device + * drivers avoid such mistakes. + */ +struct usb_host_interface *usb_altnum_to_altsetting(struct usb_interface *intf, + unsigned int altnum) +{ + int i; + + for (i = 0; i < intf->num_altsetting; i++) { + if (intf->altsetting[i].desc.bAlternateSetting == altnum) + return &intf->altsetting[i]; + } + return NULL; +} + /** * usb_epnum_to_ep_desc - get the endpoint object with a given endpoint number * @dev: the device whose current configuration+altsettings is considered @@ -247,7 +274,7 @@ usb_epnum_to_ep_desc(struct usb_device *dev, unsigned epnum) /* only endpoints in current altsetting are active */ intf = config->interface[i]; - alt = intf->altsetting + intf->act_altsetting; + alt = intf->cur_altsetting; for (k = 0; k < alt->desc.bNumEndpoints; k++) if (epnum == alt->endpoint[k].desc.bEndpointAddress) @@ -421,7 +448,7 @@ usb_match_id(struct usb_interface *interface, const struct usb_device_id *id) if (id == NULL) return NULL; - intf = &interface->altsetting [interface->act_altsetting]; + intf = interface->cur_altsetting; dev = interface_to_usbdev(interface); /* It is important to check that id->driver_info is nonzero, @@ -624,7 +651,7 @@ static int usb_hotplug (struct device *dev, char **envp, int num_envp, scratch += length; if (usb_dev->descriptor.bDeviceClass == 0) { - int alt = intf->act_altsetting; + struct usb_host_interface *alt = intf->cur_altsetting; /* 2.4 only exposed interface zero. in 2.5, hotplug * agents are called for all interfaces, and can use @@ -633,9 +660,9 @@ static int usb_hotplug (struct device *dev, char **envp, int num_envp, envp [i++] = scratch; length += snprintf (scratch, buffer_size - length, "INTERFACE=%d/%d/%d", - intf->altsetting[alt].desc.bInterfaceClass, - intf->altsetting[alt].desc.bInterfaceSubClass, - intf->altsetting[alt].desc.bInterfaceProtocol); + alt->desc.bInterfaceClass, + alt->desc.bInterfaceSubClass, + alt->desc.bInterfaceProtocol); if ((buffer_size - length <= 0) || (i >= num_envp)) return -ENOMEM; ++length; @@ -1582,6 +1609,7 @@ EXPORT_SYMBOL(usb_driver_release_interface); EXPORT_SYMBOL(usb_match_id); EXPORT_SYMBOL(usb_find_interface); EXPORT_SYMBOL(usb_ifnum_to_if); +EXPORT_SYMBOL(usb_altnum_to_altsetting); EXPORT_SYMBOL(usb_reset_device); EXPORT_SYMBOL(usb_disconnect); diff --git a/include/linux/usb.h b/include/linux/usb.h index 6a2e0b7f18d8..e7e102afcb20 100644 --- a/include/linux/usb.h +++ b/include/linux/usb.h @@ -72,14 +72,15 @@ struct usb_host_interface { /** * struct usb_interface - what usb device drivers talk to - * @altsetting: array of interface descriptors, one for each alternate + * @altsetting: array of interface structures, one for each alternate * setting that may be selected. Each one includes a set of - * endpoint configurations and will be in numberic order, - * 0..num_altsetting. + * endpoint configurations. They will be in no particular order. * @num_altsetting: number of altsettings defined. - * @act_altsetting: index of current altsetting. this number is always - * less than num_altsetting. after the device is configured, each + * @cur_altsetting: the current altsetting. + * @act_altsetting: index of current altsetting. This number is always + * less than num_altsetting. After the device is configured, each * interface uses its default setting of zero. + * NOTE: act_altsetting is deprecated. Use cur_altsetting instead. * @driver: the USB driver that is bound to this interface. * @minor: the minor number assigned to this interface, if this * interface is bound to a driver that uses the USB major number. @@ -104,20 +105,27 @@ struct usb_host_interface { * calls such as dev_get_drvdata() on the dev member of this structure. * * Each interface may have alternate settings. The initial configuration - * of a device sets the first of these, but the device driver can change + * of a device sets altsetting 0, but the device driver can change * that setting using usb_set_interface(). Alternate settings are often * used to control the the use of periodic endpoints, such as by having * different endpoints use different amounts of reserved USB bandwidth. * All standards-conformant USB devices that use isochronous endpoints * will use them in non-default settings. + * + * The USB specification says that alternate setting numbers must run from + * 0 to one less than the total number of alternate settings. But some + * devices manage to mess this up, and the structures aren't necessarily + * stored in numerical order anyhow. Use usb_altnum_to_altsetting() to + * look up an alternate setting in the altsetting array based on its number. */ struct usb_interface { - /* array of alternate settings for this interface. - * these will be in numeric order, 0..num_altsettting - */ + /* array of alternate settings for this interface, + * stored in no particular order */ struct usb_host_interface *altsetting; - unsigned act_altsetting; /* active alternate setting */ + struct usb_host_interface *cur_altsetting; /* the currently + * active alternate setting */ + unsigned act_altsetting; /* index of active alternate setting: DEPRECATED */ unsigned num_altsetting; /* number of alternate settings */ struct usb_driver *driver; /* driver */ @@ -143,19 +151,43 @@ static inline void usb_set_intfdata (struct usb_interface *intf, void *data) /* this maximum is arbitrary */ #define USB_MAXINTERFACES 32 -/* USB_DT_CONFIG: Configuration descriptor information. +/** + * struct usb_host_config - representation of a device's configuration + * @desc: the device's configuration descriptor. + * @interface: array of usb_interface structures, one for each interface + * in the configuration. The number of interfaces is stored in + * desc.bNumInterfaces. + * @extra: pointer to buffer containing all extra descriptors associated + * with this configuration (those preceding the first interface + * descriptor). + * @extralen: length of the extra descriptors buffer. * - * USB_DT_OTHER_SPEED_CONFIG is the same descriptor, except that the - * descriptor type is different. Highspeed-capable devices can look - * different depending on what speed they're currently running. Only - * devices with a USB_DT_DEVICE_QUALIFIER have an OTHER_SPEED_CONFIG. + * USB devices may have multiple configurations, but only one can be active + * at any time. Each encapsulates a different operational environment; + * for example, a dual-speed device would have separate configurations for + * full-speed and high-speed operation. The number of configurations + * available is stored in the device descriptor as bNumConfigurations. + * + * A configuration can contain multiple interfaces. Each corresponds to + * a different function of the USB device, and all are available whenever + * the configuration is active. The USB standard says that interfaces + * are supposed to be numbered from 0 to desc.bNumInterfaces-1, but a lot + * of devices get this wrong. In addition, the interface array is not + * guaranteed to be sorted in numerical order. Use usb_ifnum_to_if() to + * look up an interface entry based on its number. + * + * Device drivers should not attempt to activate configurations. The choice + * of which configuration to install is a policy decision based on such + * considerations as available power, functionality provided, and the user's + * desires (expressed through hotplug scripts). However, drivers can call + * usb_reset_configuration() to reinitialize the current configuration and + * all its interfaces. */ struct usb_host_config { struct usb_config_descriptor desc; - /* the interfaces associated with this configuration - * these will be in numeric order, 0..desc.bNumInterfaces - */ + /* the interfaces associated with this configuration, + * stored in no particular order */ struct usb_interface *interface[USB_MAXINTERFACES]; unsigned char *extra; /* Extra descriptors */ @@ -297,8 +329,12 @@ extern void usb_driver_release_interface(struct usb_driver *driver, const struct usb_device_id *usb_match_id(struct usb_interface *interface, const struct usb_device_id *id); -extern struct usb_interface *usb_find_interface(struct usb_driver *drv, int minor); -extern struct usb_interface *usb_ifnum_to_if(struct usb_device *dev, unsigned ifnum); +extern struct usb_interface *usb_find_interface(struct usb_driver *drv, + int minor); +extern struct usb_interface *usb_ifnum_to_if(struct usb_device *dev, + unsigned ifnum); +extern struct usb_host_interface *usb_altnum_to_altsetting( + struct usb_interface *intf, unsigned int altnum); /** -- cgit v1.2.3 From 07fd2b87fca9fb39a7d2626283291ce3861e1b5e Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Tue, 2 Mar 2004 20:54:26 -0800 Subject: [PATCH] USB: remove intf->act_altsetting altogether from the USB core and usb.h --- drivers/usb/core/message.c | 3 --- include/linux/usb.h | 5 ----- 2 files changed, 8 deletions(-) (limited to 'include/linux') diff --git a/drivers/usb/core/message.c b/drivers/usb/core/message.c index 76226e37bb36..5bd4d8ab05ad 100644 --- a/drivers/usb/core/message.c +++ b/drivers/usb/core/message.c @@ -969,7 +969,6 @@ int usb_set_interface(struct usb_device *dev, int interface, int alternate) usb_disable_interface(dev, iface); iface->cur_altsetting = alt; - iface->act_altsetting = alt - iface->altsetting; /* If the interface only has one altsetting and the device didn't * accept the request, we attempt to carry out the equivalent action @@ -1068,7 +1067,6 @@ int usb_reset_configuration(struct usb_device *dev) alt = &intf->altsetting[0]; intf->cur_altsetting = alt; - intf->act_altsetting = alt - intf->altsetting; usb_enable_interface(dev, intf); } return 0; @@ -1170,7 +1168,6 @@ int usb_set_configuration(struct usb_device *dev, int configuration) alt = &intf->altsetting[0]; intf->cur_altsetting = alt; - intf->act_altsetting = alt - intf->altsetting; usb_enable_interface(dev, intf); intf->dev.parent = &dev->dev; intf->dev.driver = NULL; diff --git a/include/linux/usb.h b/include/linux/usb.h index e7e102afcb20..eb34f2ee39ea 100644 --- a/include/linux/usb.h +++ b/include/linux/usb.h @@ -77,10 +77,6 @@ struct usb_host_interface { * endpoint configurations. They will be in no particular order. * @num_altsetting: number of altsettings defined. * @cur_altsetting: the current altsetting. - * @act_altsetting: index of current altsetting. This number is always - * less than num_altsetting. After the device is configured, each - * interface uses its default setting of zero. - * NOTE: act_altsetting is deprecated. Use cur_altsetting instead. * @driver: the USB driver that is bound to this interface. * @minor: the minor number assigned to this interface, if this * interface is bound to a driver that uses the USB major number. @@ -125,7 +121,6 @@ struct usb_interface { struct usb_host_interface *cur_altsetting; /* the currently * active alternate setting */ - unsigned act_altsetting; /* index of active alternate setting: DEPRECATED */ unsigned num_altsetting; /* number of alternate settings */ struct usb_driver *driver; /* driver */ -- cgit v1.2.3 From 25a1f19aa12e6a92315d64559380fce7b4ffe2e0 Mon Sep 17 00:00:00 2001 From: Stéphane Doyon Date: Mon, 8 Mar 2004 22:12:08 -0800 Subject: [PATCH] USB brlvger: Driver obsoleted by rewrite using usbfs We have rewritten the brlvger (Tieman Voyager USB Braille display) driver so that it works from user-space through usbfs. It appears to work just as well as the in-kernel driver. The brlvger driver in the 2.6.x kernel is now obsolete and should be removed. The attached patch against 2.6.3 does this. Please apply. NB: The following files are completely deleted: Documentation/usb/brlvger.txt drivers/usb/misc/brlvger.c include/linux/brlvger.h The new Voyager driver is available (stil under GPL) as part of BRLTTY, starting with version 3.5pre1 (http://mielke.cc/brltty). Thanks to Dave Mielke who implemented BRLTTY's usbfs functionality, among lots of other stuff. --- CREDITS | 10 - Documentation/usb/brlvger.txt | 36 -- MAINTAINERS | 7 - drivers/usb/Makefile | 1 - drivers/usb/misc/Kconfig | 11 - drivers/usb/misc/Makefile | 1 - drivers/usb/misc/brlvger.c | 1016 ----------------------------------------- include/linux/brlvger.h | 54 --- 8 files changed, 1136 deletions(-) delete mode 100644 Documentation/usb/brlvger.txt delete mode 100644 drivers/usb/misc/brlvger.c delete mode 100644 include/linux/brlvger.h (limited to 'include/linux') diff --git a/CREDITS b/CREDITS index 13eba329ee16..6a9a80876d86 100644 --- a/CREDITS +++ b/CREDITS @@ -673,11 +673,6 @@ S: Northampton S: NN1 3QT S: United Kingdom -N: Stephane Dalton -E: sdalton@videotron.ca -D: Tieman Voyager USB Braille display driver. -S: Québec, Canada - N: Uwe Dannowski E: Uwe.Dannowski@ira.uka.de W: http://i30www.ira.uka.de/~dannowsk/ @@ -797,11 +792,6 @@ E: cort@fsmlabs.com W: http://www.fsmlabs.com/linuxppcbk.html D: PowerPC -N: Stéphane Doyon -E: s.doyon@videotron.ca -D: Tieman Voyager USB Braille display driver. -S: Québec, Canada - N: Oleg Drokin E: green@ccssu.crimea.ua W: http://www.ccssu.crimea.ua/~green diff --git a/Documentation/usb/brlvger.txt b/Documentation/usb/brlvger.txt deleted file mode 100644 index e1441ec2558a..000000000000 --- a/Documentation/usb/brlvger.txt +++ /dev/null @@ -1,36 +0,0 @@ -Kernel Driver for the Tieman Voyager Braille Display (USB) - -Authors: -Stéphane Dalton -Stéphane Doyon - -Version 0.8, April 17, 2002 - -The brlvger driver supports a Braille display (aka Braille terminal) -model Voyager from Tieman. - -The driver has been in heavy use for about six months now (as of April -17th 2002) by a very few users (about 3-4), who say it has worked very -well for them. - -We have tested it with a Voyager 44, but it should also support -the Voyager 70. - -This driver implements a character device which allows userspace programs -access to the braille displays raw functions. You still need a userspace -program to perform the screen-review functions and control the -display. Get BRLTTY from http://mielke.cc/brltty/ (version 2.99.8 or -later). It has a Voyager driver which interfaces with this kernel driver. - -The interface is through a character device, major 180, minor 128, called -"brlvger" under devfs. - -Many thanks to the Tieman people: Corand van Strien, Ivar Illing, Daphne -Vogelaar and Ingrid Vogel. They provided us with a Braille display (as -well as programming information) so that we could write this driver. They -replaced the display when it broke and they answered our technical -questions. It is very motivating when companies take an interest in such -projects and are so supportive. - -Thanks to Andor Demarteau who got this whole -project started and beta-tested all our early buggy attempts. diff --git a/MAINTAINERS b/MAINTAINERS index b6ce0c77a2bd..b6640c1b494d 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1966,13 +1966,6 @@ P: Romain Lievin M: roms@lpg.ticalc.org S: Maintained -TIEMAN VOYAGER USB BRAILLE DISPLAY DRIVER -P: Stephane Dalton -M: sdalton@videotron.ca -P: Stéphane Doyon -M: s.doyon@videotron.ca -S: Maintained - TLAN NETWORK DRIVER P: Samuel Chessman M: chessman@tux.org diff --git a/drivers/usb/Makefile b/drivers/usb/Makefile index 6e3a98f12605..0baa243f4019 100644 --- a/drivers/usb/Makefile +++ b/drivers/usb/Makefile @@ -55,7 +55,6 @@ obj-$(CONFIG_USB_MICROTEK) += image/ obj-$(CONFIG_USB_SERIAL) += serial/ obj-$(CONFIG_USB_AUERSWALD) += misc/ -obj-$(CONFIG_USB_BRLVGER) += misc/ obj-$(CONFIG_USB_EMI26) += misc/ obj-$(CONFIG_USB_LCD) += misc/ obj-$(CONFIG_USB_LEGOTOWER) += misc/ diff --git a/drivers/usb/misc/Kconfig b/drivers/usb/misc/Kconfig index 958574645158..9ab0a65441da 100644 --- a/drivers/usb/misc/Kconfig +++ b/drivers/usb/misc/Kconfig @@ -86,17 +86,6 @@ config USB_LEGOTOWER a module, say M here and read . -config USB_BRLVGER - tristate "Tieman Voyager USB Braille display support (EXPERIMENTAL)" - depends on USB && EXPERIMENTAL - help - Say Y here if you want to use the Voyager USB Braille display from - Tieman. See for more - information. - - To compile this driver as a module, choose M here: the - module will be called brlvger. - config USB_LCD tristate "USB LCD driver support" depends on USB diff --git a/drivers/usb/misc/Makefile b/drivers/usb/misc/Makefile index 9f0f638af248..2ee829568c04 100644 --- a/drivers/usb/misc/Makefile +++ b/drivers/usb/misc/Makefile @@ -4,7 +4,6 @@ # obj-$(CONFIG_USB_AUERSWALD) += auerswald.o -obj-$(CONFIG_USB_BRLVGER) += brlvger.o obj-$(CONFIG_USB_EMI62) += emi62.o obj-$(CONFIG_USB_EMI26) += emi26.o obj-$(CONFIG_USB_LCD) += usblcd.o diff --git a/drivers/usb/misc/brlvger.c b/drivers/usb/misc/brlvger.c deleted file mode 100644 index 067d81730b2e..000000000000 --- a/drivers/usb/misc/brlvger.c +++ /dev/null @@ -1,1016 +0,0 @@ -/* - * Tieman Voyager braille display USB driver. - * - * Copyright 2001-2002 Stephane Dalton - * and Stéphane Doyon - * Maintained by Stéphane Doyon . - */ -/* - * 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 Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ -/* History: - * 0.8 April 2002: Integration into the kernel tree. - * 0.7 October 2001: First public release as a module, distributed with - * the BRLTTY package (beta versions around 2.99y). - */ - -#define DRIVER_VERSION "v0.8" -#define DATE "April 2002" -#define DRIVER_AUTHOR \ - "Stephane Dalton " \ - "and Stéphane Doyon " -#define DRIVER_DESC "Tieman Voyager braille display USB driver for Linux 2.4" -#define DRIVER_SHORTDESC "Voyager" - -#define BANNER \ - KERN_INFO DRIVER_SHORTDESC " " DRIVER_VERSION " (" DATE ")\n" \ - KERN_INFO " by " DRIVER_AUTHOR "\n" - -static const char longbanner[] = { - DRIVER_DESC ", " DRIVER_VERSION " (" DATE "), by " DRIVER_AUTHOR -}; - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -MODULE_AUTHOR( DRIVER_AUTHOR ); -MODULE_DESCRIPTION( DRIVER_DESC ); -MODULE_LICENSE("GPL"); - -/* Module parameters */ - -static int debug = 1; -MODULE_PARM(debug, "i"); -MODULE_PARM_DESC(debug, "Debug level, 0-3"); - -static int write_repeats = 2; -MODULE_PARM(write_repeats, "i"); -MODULE_PARM_DESC(write_repeats, "Hack: repetitions for command to " - "display braille pattern"); - /* to get rid of weird extra dots (perhaps only on - early hardware versions?) */ - -static int stall_tries = 3; -MODULE_PARM(stall_tries, "i"); -MODULE_PARM_DESC(stall_tries, "Hack: retransmits of stalled USB " - "control messages"); - /* broken early hardware versions? */ - -#define BRLVGER_RAW_VOLTAGE 89 -/* from 0->300V to 255->200V, we are told 265V is normal operating voltage, - but we don't know the scale. Assuming it is linear. */ -static int raw_voltage = BRLVGER_RAW_VOLTAGE; -MODULE_PARM(raw_voltage, "i"); -MODULE_PARM_DESC(raw_voltage, "Parameter for the call to SET_DISPLAY_VOLTAGE"); - - -/* protocol and display type defines */ -#define MAX_BRLVGER_CELLS 72 -#define MAX_INTERRUPT_DATA 8 -/* control message request types */ -#define BRLVGER_READ_REQ 0xC2 -#define BRLVGER_WRITE_REQ 0x42 -/* control message request codes */ -#define BRLVGER_SET_DISPLAY_ON 0 -#define BRLVGER_SET_DISPLAY_VOLTAGE 1 -#define BRLVGER_GET_SERIAL 3 -#define BRLVGER_GET_HWVERSION 4 -#define BRLVGER_GET_FWVERSION 5 -#define BRLVGER_GET_LENGTH 6 -#define BRLVGER_SEND_BRAILLE 7 -#define BRLVGER_BEEP 9 -#if 0 /* not used and not sure they're working */ -#define BRLVGER_GET_DISPLAY_VOLTAGE 2 -#define BRLVGER_GET_CURRENT 8 -#endif - -/* Prototypes */ -static int brlvger_probe (struct usb_interface *intf, - const struct usb_device_id *id); -static void brlvger_disconnect(struct usb_interface *intf); -static int brlvger_open(struct inode *inode, struct file *file); -static int brlvger_release(struct inode *inode, struct file *file); -static ssize_t brlvger_write(struct file *file, const char __user *buffer, - size_t count, loff_t *pos); -static ssize_t brlvger_read(struct file *file, char __user *buffer, - size_t count, loff_t *unused_pos); -static int brlvger_ioctl(struct inode *inode, struct file *file, - unsigned cmd, unsigned long arg); -static unsigned brlvger_poll(struct file *file, poll_table *wait); -static loff_t brlvger_llseek(struct file * file, loff_t offset, int orig); -static void intr_callback(struct urb *urb, struct pt_regs *regs); -struct brlvger_priv; -static int brlvger_get_hw_version(struct brlvger_priv *priv, - unsigned char *verbuf); -static int brlvger_get_fw_version(struct brlvger_priv *priv, - unsigned char *buf); -static int brlvger_get_serial(struct brlvger_priv *priv, - unsigned char *buf); -static int brlvger_get_display_length(struct brlvger_priv *priv); -static int brlvger_set_display_on_off(struct brlvger_priv *priv, __u16 on); -static int brlvger_beep(struct brlvger_priv *priv, __u16 duration); -static int brlvger_set_display_voltage(struct brlvger_priv *priv, - __u16 voltage); -static int mycontrolmsg(const char *funcname, - struct brlvger_priv *priv, unsigned pipe_dir, - __u8 request, __u8 requesttype, __u16 value, - __u16 index, void *data, __u16 size); - -#define controlmsg(priv,pipe_dir,a,b,c,d,e,f) \ - mycontrolmsg(__FUNCTION__, priv, pipe_dir, \ - a,b,c,d,e,f) -#define sndcontrolmsg(priv,a,b,c,d,e,f) \ - controlmsg(priv, 0, a,b,c,d,e,f) -#define rcvcontrolmsg(priv,a,b,c,d,e,f) \ - controlmsg(priv, USB_DIR_IN, a,b,c,d,e,f) - -/* ----------------------------------------------------------------------- */ - -/* Data */ - -/* key event queue size */ -#define MAX_INTERRUPT_BUFFER 10 - -/* private state */ -struct brlvger_priv { - struct usb_device *dev; /* USB device handle */ - struct usb_endpoint_descriptor *in_interrupt; - struct urb *intr_urb; - - int subminor; /* which minor dev #? */ - - unsigned char hwver[BRLVGER_HWVER_SIZE]; /* hardware version */ - unsigned char fwver[BRLVGER_FWVER_SIZE]; /* firmware version */ - unsigned char serialnum[BRLVGER_SERIAL_SIZE]; - - int llength; /* logical length */ - int plength; /* physical length */ - - __u8 obuf[MAX_BRLVGER_CELLS]; - __u8 intr_buff[MAX_INTERRUPT_DATA]; - __u8 event_queue[MAX_INTERRUPT_BUFFER][MAX_INTERRUPT_DATA]; - atomic_t intr_idx, read_idx; - spinlock_t intr_idx_lock; /* protects intr_idx */ - wait_queue_head_t read_wait; - - int opened; - struct semaphore open_sem; /* protects ->opened */ - struct semaphore dev_sem; /* protects ->dev */ -}; - -/* Globals */ - -/* For blocking open */ -static DECLARE_WAIT_QUEUE_HEAD(open_wait); - -/* Some print macros */ -#ifdef dbg -#undef dbg -#endif -#ifdef info -#undef info -#endif -#ifdef err -#undef err -#endif -#define info(args...) \ - ({ printk(KERN_INFO "Voyager: " args); \ - printk("\n"); }) -#define err(args...) \ - ({ printk(KERN_ERR "Voyager: " args); \ - printk("\n"); }) -#define dbgprint(fmt, args...) \ - ({ printk(KERN_DEBUG "Voyager: %s: " fmt, __FUNCTION__ , ##args); \ - printk("\n"); }) -#define dbg(args...) \ - ({ if(debug >= 1) dbgprint(args); }) -#define dbg2(args...) \ - ({ if(debug >= 2) dbgprint(args); }) -#define dbg3(args...) \ - ({ if(debug >= 3) dbgprint(args); }) - -/* ----------------------------------------------------------------------- */ - -/* Driver registration */ - -static struct usb_device_id brlvger_ids [] = { - { USB_DEVICE(0x0798, 0x0001) }, - { } /* Terminating entry */ -}; -MODULE_DEVICE_TABLE (usb, brlvger_ids); - -static struct file_operations brlvger_fops = -{ - .owner = THIS_MODULE, - .llseek = brlvger_llseek, - .read = brlvger_read, - .write = brlvger_write, - .ioctl = brlvger_ioctl, - .open = brlvger_open, - .release = brlvger_release, - .poll = brlvger_poll, -}; - -static struct usb_class_driver brlvger_class = { - .name = "usb/brlvger%d", - .fops = &brlvger_fops, - .mode = S_IFCHR | S_IRUSR |S_IWUSR | S_IRGRP | S_IWGRP, - .minor_base = BRLVGER_MINOR, -}; - -static struct usb_driver brlvger_driver = -{ - .owner = THIS_MODULE, - .name = "brlvger", - .probe = brlvger_probe, - .disconnect = brlvger_disconnect, - .id_table = brlvger_ids, -}; - -static int -__init brlvger_init (void) -{ - int retval; - printk(BANNER); - - if(stall_tries < 1 || write_repeats < 1) - return -EINVAL; - - retval = usb_register(&brlvger_driver); - if (retval) { - err("USB registration failed"); - goto out; - } - -out: - return retval; -} - -static void -__exit brlvger_cleanup (void) -{ - usb_deregister (&brlvger_driver); - dbg("Driver unregistered"); -} - -module_init (brlvger_init); -module_exit (brlvger_cleanup); - -/* ----------------------------------------------------------------------- */ - -/* Probe and disconnect functions */ - -static int -brlvger_probe (struct usb_interface *intf, - const struct usb_device_id *id) -{ - struct usb_device *dev = interface_to_usbdev(intf); - struct brlvger_priv *priv = NULL; - int retval; - struct usb_endpoint_descriptor *endpoint; - struct usb_host_interface *actifsettings; - /* protects against reentrance: once we've found a free slot - we reserve it.*/ - static DECLARE_MUTEX(reserve_sem); - - actifsettings = dev->actconfig->interface[0]->altsetting; - - if( dev->descriptor.bNumConfigurations != 1 - || dev->config->desc.bNumInterfaces != 1 - || actifsettings->desc.bNumEndpoints != 1 ) { - err ("Bogus braille display config info"); - return -ENODEV; - } - - endpoint = &actifsettings->endpoint [0].desc; - if (!(endpoint->bEndpointAddress & 0x80) || - ((endpoint->bmAttributes & 3) != 0x03)) { - err ("Bogus braille display config info, wrong endpoints"); - return -ENODEV; - } - - down(&reserve_sem); - - retval = usb_register_dev(intf, &brlvger_class); - if (retval) { - err("Not able to get a minor for this device."); - goto error; - } - - if( !(priv = kmalloc (sizeof *priv, GFP_KERNEL)) ){ - err("No more memory"); - goto error; - } - - memset(priv, 0, sizeof(*priv)); - atomic_set(&priv->intr_idx, 0); - atomic_set(&priv->read_idx, MAX_INTERRUPT_BUFFER-1); - spin_lock_init(&priv->intr_idx_lock); - init_waitqueue_head(&priv->read_wait); - /* opened is memset'ed to 0 */ - init_MUTEX(&priv->open_sem); - init_MUTEX(&priv->dev_sem); - - priv->subminor = intf->minor; - - /* we found a interrupt in endpoint */ - priv->in_interrupt = endpoint; - - priv->dev = dev; - - if(brlvger_get_hw_version(priv, priv->hwver) <0) { - err("Unable to get hardware version"); - goto error; - } - dbg("Hw ver %d.%d", priv->hwver[0], priv->hwver[1]); - if(brlvger_get_fw_version(priv, priv->fwver) <0) { - err("Unable to get firmware version"); - goto error; - } - dbg("Fw ver: %s", priv->fwver); - - if(brlvger_get_serial(priv, priv->serialnum) <0) { - err("Unable to get serial number"); - goto error; - } - dbg("Serial number: %s", priv->serialnum); - - if( (priv->llength = brlvger_get_display_length(priv)) <0 ){ - err("Unable to get display length"); - goto error; - } - switch(priv->llength) { - case 48: - priv->plength = 44; - break; - case 72: - priv->plength = 70; - break; - default: - err("Unsupported display length: %d", priv->llength); - goto error; - }; - dbg("Display length: %d", priv->plength); - - usb_set_intfdata (intf, priv); - info( "Braille display %d is device major %d minor %d", - intf->minor, USB_MAJOR, BRLVGER_MINOR + intf->minor); - - /* Tell anyone waiting on a blocking open */ - wake_up_interruptible(&open_wait); - - goto out; - - error: - if(priv) { - kfree( priv ); - priv = NULL; - } - - out: - up(&reserve_sem); - if (priv) { - usb_set_intfdata (intf, priv); - return 0; - } - return -EIO; -} - -static void -brlvger_disconnect(struct usb_interface *intf) -{ - struct brlvger_priv *priv = usb_get_intfdata (intf); - int r; - - usb_set_intfdata (intf, NULL); - if(priv){ - info("Display %d disconnecting", priv->subminor); - - usb_deregister_dev(intf, &brlvger_class); - - down(&priv->open_sem); - down(&priv->dev_sem); - if(priv->opened) { - /* Disable interrupts */ - if((r = usb_unlink_urb(priv->intr_urb)) <0) - err("usb_unlink_urb returns %d", r); - usb_free_urb(priv->intr_urb); - /* mark device as dead and prevent control - messages to it */ - priv->dev = NULL; - /* Tell anyone hung up on a read that it - won't be coming */ - wake_up_interruptible(&priv->read_wait); - up(&priv->dev_sem); - up(&priv->open_sem); - }else - /* no corresponding up()s */ - kfree(priv); - } -} - -/* ----------------------------------------------------------------------- */ - -/* fops implementation */ - -static int -brlvger_open(struct inode *inode, struct file *file) -{ - int devnum = iminor(inode); - struct usb_interface *intf = NULL; - struct brlvger_priv *priv = NULL; - int n, ret; - - if (devnum < 0) - return -ENXIO; - - n = devnum - BRLVGER_MINOR; - - do { - intf = usb_find_interface(&brlvger_driver, devnum); - if (!intf) { - if (file->f_flags & O_NONBLOCK) { - dbg3("Failing non-blocking open: " - "device %d not connected", n); - return -EAGAIN; - } - /* Blocking open. One global wait queue will - suffice. We wait until a device for the selected - minor is connected. */ - dbg2("Waiting for device %d to be connected", n); - ret = wait_event_interruptible(open_wait, - (intf = usb_find_interface(&brlvger_driver, devnum))); - if (ret) { - dbg2("Interrupted wait for device %d", n); - return ret; - } - } - } while(!intf); - priv = usb_get_intfdata(intf); - - /* We grabbed an existing device. */ - if(down_interruptible(&priv->open_sem)) - return -ERESTARTSYS; - - /* Only one process can open each device, no sharing. */ - ret = -EBUSY; - if(priv->opened) - goto out; - - dbg("Opening display %d", priv->subminor); - - /* Setup interrupt handler for receiving key input */ - priv->intr_urb = usb_alloc_urb(0, GFP_KERNEL); - if(!priv->intr_urb) { - err("Unable to allocate URB"); - goto out; - } - usb_fill_int_urb( priv->intr_urb, priv->dev, - usb_rcvintpipe(priv->dev, - priv->in_interrupt->bEndpointAddress), - priv->intr_buff, sizeof(priv->intr_buff), - intr_callback, priv, priv->in_interrupt->bInterval); - if((ret = usb_submit_urb(priv->intr_urb, GFP_KERNEL)) <0){ - err("Error %d while submitting URB", ret); - goto out; - } - - /* Set voltage */ - if(brlvger_set_display_voltage(priv, raw_voltage) <0) { - err("Unable to set voltage"); - goto out; - } - - /* Turn display on */ - if((ret = brlvger_set_display_on_off(priv, 1)) <0) { - err("Error %d while turning display on", ret); - goto out; - } - - /* Mark as opened, so disconnect cannot free priv. */ - priv->opened = 1; - - file->private_data = priv; - - ret = 0; - goto out; - - out: - up(&priv->open_sem); - return ret; -} - -static int -brlvger_release(struct inode *inode, struct file *file) -{ - struct brlvger_priv *priv = file->private_data; - int r; - - /* Turn display off. Safe even if disconnected. */ - brlvger_set_display_on_off(priv, 0); - - /* mutex with disconnect and with open */ - down(&priv->open_sem); - - if(!priv->dev) { - dbg("Releasing disconnected device %d", priv->subminor); - /* no up(&priv->open_sem) */ - kfree(priv); - }else{ - dbg("Closing display %d", priv->subminor); - /* Disable interrupts */ - if((r = usb_unlink_urb(priv->intr_urb)) <0) - err("usb_unlink_urb returns %d", r); - usb_free_urb(priv->intr_urb); - priv->opened = 0; - up(&priv->open_sem); - } - - return 0; -} - -static ssize_t -brlvger_write(struct file *file, const char __user *buffer, - size_t count, loff_t *pos) -{ - struct brlvger_priv *priv = file->private_data; - char buf[MAX_BRLVGER_CELLS]; - int ret; - size_t rs; - loff_t off; - __u16 written; - - if(!priv->dev) - return -ENOLINK; - - off = *pos; - - if(off > priv->plength) - return -ESPIPE;; - - rs = priv->plength - off; - - if(count > rs) - count = rs; - written = count; - - if (copy_from_user (buf, buffer, count ) ) - return -EFAULT; - - memset(priv->obuf, 0xaa, sizeof(priv->obuf)); - - /* Firmware supports multiples of 8cells, so some cells are absent - and for some reason there actually are holes! euurkkk! */ - - if( priv->plength == 44 ) { - /* Two ghost cells at the beginning of the display, plus - two more after the sixth physical cell. */ - if(off > 5) { - off +=4; - memcpy(priv->obuf, buf, count); - }else{ - int firstpart = 6 - off; - -#ifdef WRITE_DEBUG - dbg3("off: %lld, rs: %d, count: %d, firstpart: %d", - off, rs, count, firstpart); -#endif - - firstpart = (firstpart < count) ? firstpart : count; - -#ifdef WRITE_DEBUG - dbg3("off: %lld", off); - dbg3("firstpart: %d", firstpart); -#endif - - memcpy(priv->obuf, buf, firstpart); - - if(firstpart != count) { - int secondpart = count - firstpart; -#ifdef WRITE_DEBUG - dbg3("secondpart: %d", secondpart); -#endif - - memcpy(priv->obuf+(firstpart+2), - buf+firstpart, secondpart); - written +=2; - } - - off +=2; - -#ifdef WRITE_DEBUG - dbg3("off: %lld, rs: %d, count: %d, firstpart: %d, " - "written: %d", off, rs, count, firstpart, written); -#endif - } - }else{ - /* Two ghost cells at the beginningg of the display. */ - memcpy(priv->obuf, buf, count); - off += 2; - } - - { - int repeat = write_repeats; - /* Dirty hack: sometimes some of the dots are wrong and somehow - right themselves if the command is repeated. */ - while(repeat--) { - ret = sndcontrolmsg(priv, - BRLVGER_SEND_BRAILLE, BRLVGER_WRITE_REQ, 0, - off, priv->obuf, written); - if(ret <0) - return ret; - } - } - - return count; -} - -static int -read_index(struct brlvger_priv *priv) -{ - int intr_idx, read_idx; - - read_idx = atomic_read(&priv->read_idx); - read_idx = ++read_idx == MAX_INTERRUPT_BUFFER ? 0 : read_idx; - - intr_idx = atomic_read(&priv->intr_idx); - - return(read_idx == intr_idx ? -1 : read_idx); -} - -static ssize_t -brlvger_read(struct file *file, char __user *buffer, - size_t count, loff_t *unused_pos) -{ - struct brlvger_priv *priv = file->private_data; - int read_idx; - - if(count != MAX_INTERRUPT_DATA) - return -EINVAL; - - if(!priv->dev) - return -ENOLINK; - - if((read_idx = read_index(priv)) == -1) { - /* queue empty */ - if (file->f_flags & O_NONBLOCK) - return -EAGAIN; - else{ - int r = wait_event_interruptible(priv->read_wait, - (!priv->dev || (read_idx = read_index(priv)) != -1)); - if(!priv->dev) - return -ENOLINK; - if(r) - return r; - if(read_idx == -1) - /* should not happen */ - return 0; - } - } - - if (copy_to_user (buffer, priv->event_queue[read_idx], count) ) - return( -EFAULT); - - atomic_set(&priv->read_idx, read_idx); - /* Multiple opens are not allowed. Yet on SMP, two processes could - read at the same time (on a shared file descriptor); then it is not - deterministic whether or not they will get duplicates of a key - event. */ - return MAX_INTERRUPT_DATA; -} - -static int -brlvger_ioctl(struct inode *inode, struct file *file, - unsigned cmd, unsigned long arg) -{ - struct brlvger_priv *priv = file->private_data; - - if(!priv->dev) - return -ENOLINK; - - switch(cmd) { - case BRLVGER_GET_INFO: { - struct brlvger_info vi; - - memset(&vi, 0, sizeof(vi)); - strlcpy(vi.driver_version, DRIVER_VERSION, - sizeof(vi.driver_version)); - strlcpy(vi.driver_banner, longbanner, - sizeof(vi.driver_banner)); - - vi.display_length = priv->plength; - - memcpy(&vi.hwver, priv->hwver, BRLVGER_HWVER_SIZE); - memcpy(&vi.fwver, priv->fwver, BRLVGER_FWVER_SIZE); - memcpy(&vi.serialnum, priv->serialnum, BRLVGER_SERIAL_SIZE); - - if(copy_to_user((void __user *)arg, &vi, sizeof(vi))) - return -EFAULT; - return 0; - } - case BRLVGER_DISPLAY_ON: - return brlvger_set_display_on_off(priv, 1); - case BRLVGER_DISPLAY_OFF: - return brlvger_set_display_on_off(priv, 0); - case BRLVGER_BUZZ: { - __u16 duration; - if(get_user(duration, (__u16 *)arg)) - return -EFAULT; - return brlvger_beep(priv, duration); - } - -#if 0 /* Underlying commands don't seem to work for some reason; not clear if - we'd want to export these anyway. */ - case BRLVGER_SET_VOLTAGE: { - __u16 voltage; - if(get_user(voltage, (__u16 *)arg)) - return -EFAULT; - return brlvger_set_display_voltage(priv, voltage); - } - case BRLVGER_GET_VOLTAGE: { - __u8 voltage; - int r = brlvger_get_display_voltage(priv); - if(r <0) - return r; - voltage = r; - if(put_user(voltage, (__u8 *)arg)) - return -EFAULT; - return 0; - } -#endif - default: - return -EINVAL; - }; -} - -static loff_t -brlvger_llseek(struct file *file, loff_t offset, int orig) -{ - struct brlvger_priv *priv = file->private_data; - - if(!priv->dev) - return -ENOLINK; - - switch (orig) { - case 0: - /* nothing to do */ - break; - case 1: - offset +=file->f_pos; - break; - case 2: - offset += priv->plength; - default: - return -EINVAL; - } - - if((offset >= priv->plength) || (offset < 0)) - return -EINVAL; - - return (file->f_pos = offset); -} - -static unsigned -brlvger_poll(struct file *file, poll_table *wait) -{ - struct brlvger_priv *priv = file->private_data; - - if(!priv->dev) - return POLLERR | POLLHUP; - - poll_wait(file, &priv->read_wait, wait); - - if(!priv->dev) - return POLLERR | POLLHUP; - if(read_index(priv) != -1) - return POLLIN | POLLRDNORM; - - return 0; -} - -static void -intr_callback(struct urb *urb, struct pt_regs *regs) -{ - struct brlvger_priv *priv = urb->context; - int intr_idx, read_idx; - int status; - - switch (urb->status) { - case 0: - /* success */ - break; - case -ECONNRESET: - case -ENOENT: - case -ESHUTDOWN: - /* this urb is terminated, clean up */ - dbg("%s - urb shutting down with status: %d", __FUNCTION__, urb->status); - return; - default: - dbg("%s - nonzero urb status received: %d", __FUNCTION__, urb->status); - goto exit; - } - - read_idx = atomic_read(&priv->read_idx); - spin_lock(&priv->intr_idx_lock); - intr_idx = atomic_read(&priv->intr_idx); - if(read_idx == intr_idx) { - dbg2("Queue full, dropping braille display input"); - spin_unlock(&priv->intr_idx_lock); - goto exit; /* queue full */ - } - - memcpy(priv->event_queue[intr_idx], urb->transfer_buffer, - MAX_INTERRUPT_DATA); - - intr_idx = (++intr_idx == MAX_INTERRUPT_BUFFER)? 0 : intr_idx; - atomic_set(&priv->intr_idx, intr_idx); - spin_unlock(&priv->intr_idx_lock); - - wake_up_interruptible(&priv->read_wait); - -exit: - status = usb_submit_urb (urb, GFP_ATOMIC); - if (status) - err ("%s - usb_submit_urb failed with result %d", - __FUNCTION__, status); -} - -/* ----------------------------------------------------------------------- */ - -/* Hardware access functions */ - -static int -mycontrolmsg(const char *funcname, - struct brlvger_priv *priv, unsigned pipe_dir, - __u8 request, __u8 requesttype, __u16 value, - __u16 index, void *data, __u16 size) -{ - int ret=0, tries = stall_tries; - - /* Make sure the device was not disconnected */ - if(down_interruptible(&priv->dev_sem)) - return -ERESTARTSYS; - if(!priv->dev) { - up(&priv->dev_sem); - return -ENOLINK; - } - - /* Dirty hack for retransmission: stalls and fails all the time - without this on the hardware we tested. */ - while(tries--) { - ret = usb_control_msg(priv->dev, - usb_sndctrlpipe(priv->dev,0) |pipe_dir, - request, requesttype, value, - index, data, size, - HZ); - if(ret != -EPIPE) - break; - dbg2("Stalled, remaining %d tries", tries); - } - up(&priv->dev_sem); - if(ret <0) { - err("%s: usb_control_msg returns %d", - funcname, ret); - return -EIO; - } - return 0; -} - -static int -brlvger_get_hw_version(struct brlvger_priv *priv, unsigned char *verbuf) -{ - return rcvcontrolmsg(priv, - BRLVGER_GET_HWVERSION, BRLVGER_READ_REQ, 0, - 0, verbuf, BRLVGER_HWVER_SIZE); - /* verbuf should be 2 bytes */ -} - -static int -brlvger_get_fw_version(struct brlvger_priv *priv, unsigned char *buf) -{ - unsigned char rawbuf[(BRLVGER_FWVER_SIZE-1)*2+2]; - int i, len; - int r = rcvcontrolmsg(priv, - BRLVGER_GET_FWVERSION, BRLVGER_READ_REQ, 0, - 0, rawbuf, sizeof(rawbuf)); - if(r<0) - return r; - - /* If I guess correctly: succession of 16bit words, the string is - formed of the first byte of each of these words. First byte in - buffer indicates total length of data; not sure what second byte is - for. */ - len = rawbuf[0]-2; - if(len<0) - len = 0; - else if(len+1 > BRLVGER_FWVER_SIZE) - len = BRLVGER_FWVER_SIZE-1; - for(i=0; i9) ? (n)+'A' : (n)+'0') - buf[2*i] = NUM_TO_HEX(rawserial[i] >>4); - buf[2*i+1] = NUM_TO_HEX(rawserial[i] &0xf); - } - buf[2*i] = 0; - return 0; -} - -static int -brlvger_get_display_length(struct brlvger_priv *priv) -{ - unsigned char data[2]; - int ret = rcvcontrolmsg(priv, - BRLVGER_GET_LENGTH, BRLVGER_READ_REQ, 0, - 0, data, 2); - if(ret<0) - return ret; - return data[1]; -} - -static int -brlvger_beep(struct brlvger_priv *priv, __u16 duration) -{ - return sndcontrolmsg(priv, - BRLVGER_BEEP, BRLVGER_WRITE_REQ, duration, - 0, NULL, 0); -} - -static int -brlvger_set_display_on_off(struct brlvger_priv *priv, __u16 on) -{ - dbg2("Turning display %s", ((on) ? "on" : "off")); - return sndcontrolmsg(priv, - BRLVGER_SET_DISPLAY_ON, BRLVGER_WRITE_REQ, on, - 0, NULL, 0); -} - -static int -brlvger_set_display_voltage(struct brlvger_priv *priv, __u16 voltage) -{ - dbg("SET_DISPLAY_VOLTAGE to %u", voltage); - return sndcontrolmsg(priv, - BRLVGER_SET_DISPLAY_VOLTAGE, BRLVGER_WRITE_REQ, voltage, - 0, NULL, 0); -} - -#if 0 /* Had problems testing these commands. Not particularly useful anyway.*/ - -static int -brlvger_get_display_voltage(struct brlvger_priv *priv) -{ - __u8 voltage = 0; - int ret = rcvcontrolmsg(priv, - BRLVGER_GET_DISPLAY_VOLTAGE, BRLVGER_READ_REQ, 0, - 0, &voltage, 1); - if(ret<0) - return ret; - return voltage; -} - -static int -brlvger_get_current(struct brlvger_priv *priv) -{ - unsigned char data; - int ret = rcvcontrolmsg(priv, - BRLVGER_GET_CURRENT, BRLVGER_READ_REQ, 0, - 0, &data, 1); - if(ret<0) - return ret; - return data; -} -#endif diff --git a/include/linux/brlvger.h b/include/linux/brlvger.h deleted file mode 100644 index 388f3fdb6cc9..000000000000 --- a/include/linux/brlvger.h +++ /dev/null @@ -1,54 +0,0 @@ -/* - * Tieman Voyager braille display USB driver. - * - * Copyright 2001-2002 Stephane Dalton - * and Stéphane Doyon - * Maintained by Stéphane Doyon . - */ -/* - * 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 Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#ifndef _LINUX_BRLVGER_H -#define _LINUX_BRLVGER_H - -/* Ioctl request codes */ -#define BRLVGER_GET_INFO 0 -#define BRLVGER_DISPLAY_ON 2 -#define BRLVGER_DISPLAY_OFF 3 -#define BRLVGER_BUZZ 4 - -/* Base minor for the char devices */ -#define BRLVGER_MINOR 128 - -/* Size of some fields */ -#define BRLVGER_HWVER_SIZE 2 -#define BRLVGER_FWVER_SIZE 200 /* arbitrary, a long string */ -#define BRLVGER_SERIAL_BIN_SIZE 8 -#define BRLVGER_SERIAL_SIZE ((2*BRLVGER_SERIAL_BIN_SIZE)+1) - -struct brlvger_info { - __u8 driver_version[12]; - __u8 driver_banner[200]; - - __u32 display_length; - /* All other char[] fields are strings except this one. - Hardware version: first byte is major, second byte is minor. */ - __u8 hwver[BRLVGER_HWVER_SIZE]; - __u8 fwver[BRLVGER_FWVER_SIZE]; - __u8 serialnum[BRLVGER_SERIAL_SIZE]; -}; - -#endif -- cgit v1.2.3 From e2d38bf31f0a5a0b66a8ec60c557435498de68ba Mon Sep 17 00:00:00 2001 From: David Brownell Date: Tue, 9 Mar 2004 20:42:44 -0800 Subject: [PATCH] USB: usbcore doc update Some doc updates, mostly from Alan Stern, clarifying quetions folk have asked recently about unlinking and about iso transfers. --- drivers/usb/core/urb.c | 65 +++++++++++++++++++++++++++++++++++++++++--------- include/linux/usb.h | 27 +++++++++++---------- 2 files changed, 68 insertions(+), 24 deletions(-) (limited to 'include/linux') diff --git a/drivers/usb/core/urb.c b/drivers/usb/core/urb.c index 39d6c359bcf6..d5a75b1aca52 100644 --- a/drivers/usb/core/urb.c +++ b/drivers/usb/core/urb.c @@ -116,7 +116,8 @@ struct urb * usb_get_urb(struct urb *urb) * describing that request to the USB subsystem. Request completion will * be indicated later, asynchronously, by calling the completion handler. * The three types of completion are success, error, and unlink - * (also called "request cancellation"). + * (a software-induced fault, also called "request cancelation"). + * * URBs may be submitted in interrupt context. * * The caller must have correctly initialized the URB before submitting @@ -127,12 +128,23 @@ struct urb * usb_get_urb(struct urb *urb) * * Successful submissions return 0; otherwise this routine returns a * negative error number. If the submission is successful, the complete() - * callback from the urb will be called exactly once, when the USB core and - * host controller driver are finished with the urb. When the completion + * callback from the URB will be called exactly once, when the USB core and + * Host Controller Driver (HCD) are finished with the URB. When the completion * function is called, control of the URB is returned to the device * driver which issued the request. The completion handler may then * immediately free or reuse that URB. * + * With few exceptions, USB device drivers should never access URB fields + * provided by usbcore or the HCD until its complete() is called. + * The exceptions relate to periodic transfer scheduling. For both + * interrupt and isochronous urbs, as part of successful URB submission + * urb->interval is modified to reflect the actual transfer period used + * (normally some power of two units). And for isochronous urbs, + * urb->start_frame is modified to reflect when the URB's transfers were + * scheduled to start. Not all isochronous transfer scheduling policies + * will work, but most host controller drivers should easily handle ISO + * queues going from now until 10-200 msec into the future. + * * For control endpoints, the synchronous usb_control_msg() call is * often used (in non-interrupt context) instead of this call. * That is often used through convenience wrappers, for the requests @@ -143,15 +155,17 @@ struct urb * usb_get_urb(struct urb *urb) * * URBs may be submitted to endpoints before previous ones complete, to * minimize the impact of interrupt latencies and system overhead on data - * throughput. This is required for continuous isochronous data streams, + * throughput. With that queuing policy, an endpoint's queue would never + * be empty. This is required for continuous isochronous data streams, * and may also be required for some kinds of interrupt transfers. Such - * queueing also maximizes bandwidth utilization by letting USB controllers + * queuing also maximizes bandwidth utilization by letting USB controllers * start work on later requests before driver software has finished the - * completion processing for earlier requests. + * completion processing for earlier (successful) requests. * - * Bulk and Isochronous URBs may always be queued. At this writing, all - * mainstream host controller drivers support queueing for control and - * interrupt transfer requests. + * As of Linux 2.6, all USB endpoint transfer queues support depths greater + * than one. This was previously a HCD-specific behavior, except for ISO + * transfers. Non-isochronous endpoint queues are inactive during cleanup + * after faults (transfer errors or cancelation). * * Reserved Bandwidth Transfers: * @@ -389,7 +403,7 @@ int usb_submit_urb(struct urb *urb, int mem_flags) * When the URB_ASYNC_UNLINK transfer flag for the URB is clear, this * request is synchronous. Success is indicated by returning zero, * at which time the urb will have been unlinked and its completion - * handler will have been called with urb->status -ENOENT. Failure is + * handler will have been called with urb->status == -ENOENT. Failure is * indicated by any other return value. * * The synchronous cancelation mode may not be used @@ -400,8 +414,37 @@ int usb_submit_urb(struct urb *urb, int mem_flags) * When the URB_ASYNC_UNLINK transfer flag for the URB is set, this * request is asynchronous. Success is indicated by returning -EINPROGRESS, * at which time the urb will normally not have been unlinked. - * The completion function will see urb->status -ECONNRESET. Failure + * The completion function will see urb->status == -ECONNRESET. Failure * is indicated by any other return value. + * + * Unlinking and Endpoint Queues: + * + * Host Controller Driver (HCDs) place all the URBs for a particular + * endpoint in a queue. Normally the queue advances as the controller + * hardware processes each request. But when an URB terminates with any + * fault (such as an error, or being unlinked) its queue stops, at least + * until that URB's completion routine returns. It is guaranteed that + * the queue will not restart until all its unlinked URBs have been fully + * retired, with their completion routines run, even if that's not until + * some time after the original completion handler returns. + * + * This means that USB device drivers can safely build deep queues for + * large or complex transfers, and clean them up reliably after any sort + * of aborted transfer by unlinking all pending URBs at the first fault. + * + * Note that an URB terminating early because a short packet was received + * will count as an error if and only if the URB_SHORT_NOT_OK flag is set. + * Also, that all unlinks performed in any URB completion handler must + * be asynchronous. + * + * Queues for isochronous endpoints are treated differently, because they + * advance at fixed rates. Such queues do not stop when an URB is unlinked. + * An unlinked URB may leave a gap in the stream of packets. It is undefined + * whether such gaps can be filled in. + * + * When control URBs terminates with an error, it is likely that the + * status stage of the transfer will not take place, even if it is merely + * a soft error resulting from a short-packet with URB_SHORT_NOT_OK set. */ int usb_unlink_urb(struct urb *urb) { diff --git a/include/linux/usb.h b/include/linux/usb.h index eb34f2ee39ea..29ef679bd92a 100644 --- a/include/linux/usb.h +++ b/include/linux/usb.h @@ -495,8 +495,8 @@ extern struct bus_type usb_bus_type; * @minor_base: the start of the minor range for this driver. * * This structure is used for the usb_register_dev() and - * usb_unregister_dev() functions, to consolodate a number of the - * paramaters used for them. + * usb_unregister_dev() functions, to consolidate a number of the + * parameters used for them. */ struct usb_class_driver { char *name; @@ -554,7 +554,7 @@ typedef void (*usb_complete_t)(struct urb *, struct pt_regs *); * @urb_list: For use by current owner of the URB. * @pipe: Holds endpoint number, direction, type, and more. * Create these values with the eight macros available; - * usb_{snd,rcv}TYPEpipe(dev,endpoint), where the type is "ctrl" + * usb_{snd,rcv}TYPEpipe(dev,endpoint), where the TYPE is "ctrl" * (control), "bulk", "int" (interrupt), or "iso" (isochronous). * For example usb_sndbulkpipe() or usb_rcvintpipe(). Endpoint * numbers range from zero to fifteen. Note that "in" endpoint two @@ -573,8 +573,8 @@ typedef void (*usb_complete_t)(struct urb *, struct pt_regs *); * the I/O request will be performed (unless URB_NO_TRANSFER_DMA_MAP * is set). This buffer must be suitable for DMA; allocate it with * kmalloc() or equivalent. For transfers to "in" endpoints, contents - * of this buffer will be modified. This buffer is used for data - * phases of control transfers. + * of this buffer will be modified. This buffer is used for the data + * stage of control transfers. * @transfer_dma: When transfer_flags includes URB_NO_TRANSFER_DMA_MAP, * the device driver is saying that it provided this DMA address, * which the host controller driver should use in preference to the @@ -597,8 +597,7 @@ typedef void (*usb_complete_t)(struct urb *, struct pt_regs *); * device driver has provided this DMA address for the setup packet. * The host controller driver should use this in preference to * setup_packet. - * @start_frame: Returns the initial frame for interrupt or isochronous - * transfers. + * @start_frame: Returns the initial frame for isochronous transfers. * @number_of_packets: Lists the number of ISO transfer buffers. * @interval: Specifies the polling interval for interrupt or isochronous * transfers. The units are frames (milliseconds) for for full and low @@ -666,13 +665,14 @@ typedef void (*usb_complete_t)(struct urb *, struct pt_regs *); * Interrupt UBS must provide an interval, saying how often (in milliseconds * or, for highspeed devices, 125 microsecond units) * to poll for transfers. After the URB has been submitted, the interval - * and start_frame fields reflect how the transfer was actually scheduled. + * field reflects how the transfer was actually scheduled. * The polling interval may be more frequent than requested. * For example, some controllers have a maximum interval of 32 microseconds, * while others support intervals of up to 1024 microseconds. * Isochronous URBs also have transfer intervals. (Note that for isochronous * endpoints, as well as high speed interrupt endpoints, the encoding of - * the transfer interval in the endpoint descriptor is logarithmic.) + * the transfer interval in the endpoint descriptor is logarithmic. + * Device drivers must convert that value to linear units themselves.) * * Isochronous URBs normally use the URB_ISO_ASAP transfer flag, telling * the host controller to schedule the transfer as soon as bandwidth @@ -705,8 +705,9 @@ typedef void (*usb_complete_t)(struct urb *, struct pt_regs *); * The context field is normally used to link URBs back to the relevant * driver or request state. * - * When completion callback is invoked for non-isochronous URBs, the - * actual_length field tells how many bytes were transferred. + * When the completion callback is invoked for non-isochronous URBs, the + * actual_length field tells how many bytes were transferred. This field + * is updated even when the URB terminated with an error or was unlinked. * * ISO transfer status is reported in the status and actual_length fields * of the iso_frame_desc array, and the number of errors is reported in @@ -733,9 +734,9 @@ struct urb int actual_length; /* (return) actual transfer length */ unsigned char *setup_packet; /* (in) setup packet (control only) */ dma_addr_t setup_dma; /* (in) dma addr for setup_packet */ - int start_frame; /* (modify) start frame (INT/ISO) */ + int start_frame; /* (modify) start frame (ISO) */ int number_of_packets; /* (in) number of ISO packets */ - int interval; /* (in) transfer interval (INT/ISO) */ + int interval; /* (modify) transfer interval (INT/ISO) */ int error_count; /* (return) number of ISO errors */ int timeout; /* (in) timeout, in jiffies */ void *context; /* (in) context for completion */ -- cgit v1.2.3 From 2328fb17c17ec156481290916ee8d9dc4eceab08 Mon Sep 17 00:00:00 2001 From: David Brownell Date: Tue, 9 Mar 2004 21:30:20 -0800 Subject: [PATCH] USB gadget: dualspeed {run,compile}-time flags This is the first several autoconfig patches; please merge. This particular one abstracts dual-speed (high and full) support. Support some more autoconfiguration for gadget drivers. Run-time: * Add gadget->is_dualspeed flag for controllers to set. * Tested by "ethernet" gadget, to decide whether certain operations are errors or not. * Turned on by net2280. Compile-time * Generic CONFIG_USB_GADGET_DUALSPEED, not net2280-specific. * Used by "ethernet" gadget, to decide whether to include extra code and data for dual-speed support. * Turned on by net2280. The basic idea behind this, and other autoconfig patches yet to come, is minimizing the controller-specific compile-time configuration needed by gadget drivers. --- drivers/usb/gadget/Kconfig | 8 ++++++++ drivers/usb/gadget/ether.c | 28 +++++++++++++++------------- drivers/usb/gadget/net2280.c | 1 + include/linux/usb_gadget.h | 3 +++ 4 files changed, 27 insertions(+), 13 deletions(-) (limited to 'include/linux') diff --git a/drivers/usb/gadget/Kconfig b/drivers/usb/gadget/Kconfig index 5f8dc0606d23..f7074d74beae 100644 --- a/drivers/usb/gadget/Kconfig +++ b/drivers/usb/gadget/Kconfig @@ -52,6 +52,7 @@ choice config USB_GADGET_NET2280 boolean "NetChip 2280" depends on PCI + select USB_GADGET_DUALSPEED help NetChip 2280 is a PCI based USB peripheral controller which supports both full and high speed USB 2.0 data transfers. @@ -135,6 +136,13 @@ config USB_SA1100 endchoice +config USB_GADGET_DUALSPEED + bool + depends on USB_GADGET + default n + help + Means that gadget drivers should include extra descriptors + and code to handle dual-speed controllers. # # USB Gadget Drivers diff --git a/drivers/usb/gadget/ether.c b/drivers/usb/gadget/ether.c index 1ab0afe74f02..aba9f0d3d377 100644 --- a/drivers/usb/gadget/ether.c +++ b/drivers/usb/gadget/ether.c @@ -124,7 +124,6 @@ struct eth_dev { * DRIVER_VERSION_NUM ... alerts the host side driver to differences * EP_*_NAME ... which endpoints do we use for which purpose? * EP_*_NUM ... numbers for them (often limited by hardware) - * HIGHSPEED ... define if ep0 and descriptors need high speed support * WAKEUP ... if hardware supports remote wakeup AND we will issue the * usb_gadget_wakeup() call to initiate it, USB_CONFIG_ATT_WAKEUP * @@ -162,7 +161,6 @@ static const char EP_IN_NAME [] = "ep-b"; #define EP_IN_NUM 2 static const char EP_STATUS_NAME [] = "ep-f"; #define EP_STATUS_NUM 3 -#define HIGHSPEED /* supports remote wakeup, but this driver doesn't */ extern int net2280_set_fifo_mode (struct usb_gadget *gadget, int mode); @@ -311,7 +309,7 @@ static const char EP_IN_NAME[] = "ep2in-bulk"; #define DEFAULT_QLEN 2 /* double buffering by default */ #endif -#ifdef HIGHSPEED +#ifdef CONFIG_USB_GADGET_DUALSPEED static unsigned qmult = 5; module_param (qmult, uint, S_IRUGO|S_IWUSR); @@ -324,7 +322,7 @@ module_param (qmult, uint, S_IRUGO|S_IWUSR); /* also defer IRQs on highspeed TX */ #define TX_DELAY DEFAULT_QLEN -#else /* !HIGHSPEED ... full speed: */ +#else /* full speed (low speed doesn't do bulk) */ #define qlen(gadget) DEFAULT_QLEN #endif @@ -626,7 +624,7 @@ static const struct usb_descriptor_header *fs_function [] = { 0, }; -#ifdef HIGHSPEED +#ifdef CONFIG_USB_GADGET_DUALSPEED /* * usb 2.0 devices need to expose both high speed and full speed @@ -707,7 +705,7 @@ static const struct usb_descriptor_header *hs_function [] = { /* if there's no high speed support, maxpacket doesn't change. */ #define ep_desc(g,hs,fs) fs -#endif /* !HIGHSPEED */ +#endif /* !CONFIG_USB_GADGET_DUALSPEED */ /*-------------------------------------------------------------------------*/ @@ -744,7 +742,7 @@ config_buf (enum usb_device_speed speed, u8 *buf, u8 type, unsigned index) { int len; const struct usb_descriptor_header **function = fs_function; -#ifdef HIGHSPEED +#ifdef CONFIG_USB_GADGET_DUALSPEED int hs = (speed == USB_SPEED_HIGH); if (type == USB_DT_OTHER_SPEED_CONFIG) @@ -969,7 +967,7 @@ eth_set_config (struct eth_dev *dev, unsigned number, int gfp_flags) switch (gadget->speed) { case USB_SPEED_FULL: speed = "full"; break; -#ifdef HIGHSPEED +#ifdef CONFIG_USB_GADGET_DUALSPEED case USB_SPEED_HIGH: speed = "high"; break; #endif default: speed = "?"; break; @@ -1140,15 +1138,19 @@ eth_setup (struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl) value = min (ctrl->wLength, (u16) sizeof device_desc); memcpy (req->buf, &device_desc, value); break; -#ifdef HIGHSPEED +#ifdef CONFIG_USB_GADGET_DUALSPEED case USB_DT_DEVICE_QUALIFIER: + if (!gadget->is_dualspeed) + break; value = min (ctrl->wLength, (u16) sizeof dev_qualifier); memcpy (req->buf, &dev_qualifier, value); break; case USB_DT_OTHER_SPEED_CONFIG: + if (!gadget->is_dualspeed) + break; // FALLTHROUGH -#endif /* HIGHSPEED */ +#endif /* CONFIG_USB_GADGET_DUALSPEED */ case USB_DT_CONFIG: value = config_buf (gadget->speed, req->buf, ctrl->wValue >> 8, @@ -1652,7 +1654,7 @@ static int eth_start_xmit (struct sk_buff *skb, struct net_device *net) #endif req->length = length; -#ifdef HIGHSPEED +#ifdef CONFIG_USB_GADGET_DUALSPEED /* throttle highspeed IRQ rate back slightly */ req->no_interrupt = (dev->gadget->speed == USB_SPEED_HIGH) ? ((atomic_read (&dev->tx_qlen) % TX_DELAY) != 0) @@ -1775,7 +1777,7 @@ eth_bind (struct usb_gadget *gadget) #endif device_desc.bMaxPacketSize0 = gadget->ep0->maxpacket; -#ifdef HIGHSPEED +#ifdef CONFIG_USB_GADGET_DUALSPEED /* assumes ep0 uses the same value for both speeds ... */ dev_qualifier.bMaxPacketSize0 = device_desc.bMaxPacketSize0; #endif @@ -1871,7 +1873,7 @@ fail: /*-------------------------------------------------------------------------*/ static struct usb_gadget_driver eth_driver = { -#ifdef HIGHSPEED +#ifdef CONFIG_USB_GADGET_DUALSPEED .speed = USB_SPEED_HIGH, #else .speed = USB_SPEED_FULL, diff --git a/drivers/usb/gadget/net2280.c b/drivers/usb/gadget/net2280.c index 5b33e473041f..9a71c7b73281 100644 --- a/drivers/usb/gadget/net2280.c +++ b/drivers/usb/gadget/net2280.c @@ -2736,6 +2736,7 @@ static int net2280_probe (struct pci_dev *pdev, const struct pci_device_id *id) spin_lock_init (&dev->lock); dev->pdev = pdev; dev->gadget.ops = &net2280_ops; + dev->gadget.is_dualspeed = 1; /* the "gadget" abstracts/virtualizes the controller */ strcpy (dev->gadget.dev.bus_id, "gadget"); diff --git a/include/linux/usb_gadget.h b/include/linux/usb_gadget.h index 041e2540a48e..04c2622b505d 100644 --- a/include/linux/usb_gadget.h +++ b/include/linux/usb_gadget.h @@ -465,6 +465,8 @@ struct usb_gadget_ops { * driver setup() requests * @ep_list: List of other endpoints supported by the device. * @speed: Speed of current connection to USB host. + * @is_dualspeed: True if the controller supports both high and full speed + * operation. If it does, the gadget driver must also support both. * @name: Identifies the controller hardware type. Used in diagnostics * and sometimes configuration. * @dev: Driver model state for this abstract device. @@ -488,6 +490,7 @@ struct usb_gadget { struct usb_ep *ep0; struct list_head ep_list; /* of usb_ep */ enum usb_device_speed speed; + unsigned is_dualspeed:1; const char *name; struct device dev; }; -- cgit v1.2.3