Subject: Re: Incompatibilities between <=3.0 & current: USB_DEVICEINFO ioctl
To: Stephan Thesing <thesing@cs.uni-sb.de>
From: Pavel Cahyna <pavel@NetBSD.org>
List: current-users
Date: 08/29/2006 14:00:26
--MGYHOYXEY6WxJCY8
Content-Type: text/plain; charset=us-ascii
Content-Disposition: inline

On Tue, Mar 21, 2006 at 10:04:02PM +0100, Stephan Thesing wrote:
> attached is a try for a patch which
>  - adds compat ioctls with the right numbers from <=3.0 for
>      USB_GET_DEVICEINFO and USB_DEVICEINFO named
>       USB_GET_DEVICEINFO_OLD and USB_DEVICEINFO_OLD
>  - adds structs usb_device_info_old and usb_event_old, which are
>      compatible with the usb_device_info and usb_event structs form <=3.0
>  - adds code to restore the old string handling for the compat ioctls 
>  (which is
>      quite ugly)
>  - adds compat code in `usbread' that transforms results into the 
>  usb_event_old
>     struct if the read size was for that struct (to handle event reads from
>     /dev/usb)
> 
> This seems to work (digikam now works both with a -current compile libusb 
> and a
>  3.0 one, reads from /dev/usb seem to work as well).
> 
> As this patch is the result of hacking after a long day of work, it's 
> probably not as nice at it could be.  One thing that should probably be 
> changed is making the compat code dependend on COMPAT_{16,20,30} being 
> defined...

Hello,

thanks for your patch, I think it should be integrated before the 4.0 release.

Some comments:

> RCS file: /cvsroot/src/sys/dev/usb/usb.c,v
> retrieving revision 1.85
> diff -u -r1.85 usb.c
> --- sys/dev/usb/usb.c	1 Mar 2006 12:38:13 -0000	1.85
> +++ sys/dev/usb/usb.c	21 Mar 2006 20:30:30 -0000
> @@ -842,3 +883,56 @@
>  
>  	return (0);
>  }
> +
> +Static void
> +usb_copy_old_devinfo(struct usb_device_info_old *uo, struct usb_device_info *ue)
> +{
> +	unsigned char *p, *q;
> +	int n;
> +
> +	uo->udi_bus = ue->udi_bus;
> +	uo->udi_addr = ue->udi_addr;       
> +	uo->udi_cookie = ue->udi_cookie;
> +	for (p=(unsigned char *)ue->udi_product,
> +	     q=(unsigned char *)uo->udi_product; *p; p++) {
> +		if ((int)*p<0x80)
> +			*q++=*p;
> +		else {
> +			*q++='?';
> +			if ((((int)*p)&0xe0)==0xe0)
> +				p++;
> +			p++;
> +		}
> +	}
> +
> +	for (p=ue->udi_vendor,
> +	     q=uo->udi_vendor; *p; p++) {
> +		if (*p<0x80)
> +			*q++=*p;
> +		else {
> +			*q++='?';
> +			p++;
> +			if ((*p&0xe0)==0xe0)
> +				p++;
> +		}
> +	}

I miss protection against overruning the buffer in those two cycles.

> +	bcopy(ue->udi_release,
> +	      uo->udi_release, sizeof(uo->udi_release));
> +
> +	uo->udi_productNo = ue->udi_productNo;
> +	uo->udi_vendorNo = ue->udi_vendorNo;
> +	uo->udi_releaseNo = ue->udi_releaseNo;
> +	uo->udi_class = ue->udi_class;
> +	uo->udi_subclass = ue->udi_subclass;
> +	uo->udi_protocol = ue->udi_protocol;
> +	uo->udi_config = ue->udi_config;
> +	uo->udi_speed = ue->udi_speed;
> +	uo->udi_power = ue->udi_power;    
> +	uo->udi_nports = ue->udi_nports;
> +
> +	for (n=0; n<USB_MAX_DEVNAMES; n++)
> +		bcopy(uo->udi_devnames[n],
> +		      ue->udi_devnames[n], USB_MAX_DEVNAMELEN);
> +	bcopy(uo->udi_ports, ue->udi_ports, sizeof(uo->udi_ports));

I think you swapped uo and ue in those two calls to bcopy. I would constify ue
to prevent such mistakes and use memcpy instead.


> diff -u -r1.110 usbdi.c
> --- sys/dev/usb/usbdi.c	24 Dec 2005 20:27:52 -0000	1.110
> +++ sys/dev/usb/usbdi.c	21 Mar 2006 19:37:05 -0000
> @@ -1176,7 +1176,7 @@
>  }
>  
>  usbd_status
> -usbd_get_string(usbd_device_handle dev, int si, char *buf)
> +usbd_get_string(usbd_device_handle dev, int si, char *buf, int unicode)
>  {
>  	int swap = dev->quirks->uq_flags & UQ_SWAP_UNICODE;
>  	usb_string_descriptor_t us;
> @@ -1213,7 +1213,9 @@
>  		if (swap)
>  			c = (c >> 8) | (c << 8);
>  		/* Encode (16-bit) Unicode as UTF8. */
> -		if (c < 0x0080) {
> +		if (!unicode)
> +			*s++=(c<0x80)?c:'?';
> +		else if (c < 0x0080) {
>  			*s++ = c;
>  		} else if (c < 0x0800) {
>  			*s++ = 0xc0 | (c >> 6);

I've put buffer overrun protection here (not sure if it is really necessary).

An updated diff is attached.

Pavel

--MGYHOYXEY6WxJCY8
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment; filename="usbnew.diff"

Index: ugen.c
===================================================================
RCS file: /cvsroot/src/sys/dev/usb/ugen.c,v
retrieving revision 1.84
diff -u -r1.84 ugen.c
--- ugen.c	24 Jul 2006 14:24:50 -0000	1.84
+++ ugen.c	29 Aug 2006 13:57:54 -0000
@@ -1800,10 +1800,16 @@
 			free(ptr, M_TEMP);
 		return (error);
 	}
+
 	case USB_GET_DEVICEINFO:
 		usbd_fill_deviceinfo(sc->sc_udev,
 				     (struct usb_device_info *)addr, 1);
 		break;
+	case USB_GET_DEVICEINFO_OLD:
+		usbd_fill_deviceinfo_old(sc->sc_udev,
+					 (struct usb_device_info_old *)addr, 1);
+
+		break;
 	default:
 		return (EINVAL);
 	}
Index: uhid.c
===================================================================
RCS file: /cvsroot/src/sys/dev/usb/uhid.c,v
retrieving revision 1.69
diff -u -r1.69 uhid.c
--- uhid.c	28 Mar 2006 17:38:35 -0000	1.69
+++ uhid.c	29 Aug 2006 13:57:54 -0000
@@ -532,7 +532,13 @@
 
 	case USB_GET_DEVICEINFO:
 		usbd_fill_deviceinfo(sc->sc_hdev.sc_parent->sc_udev,
-				     (struct usb_device_info *)addr, 1);
+			             (struct usb_device_info *)addr, 1);
+		break;
+
+	case USB_GET_DEVICEINFO_OLD:
+		usbd_fill_deviceinfo_old(sc->sc_hdev.sc_parent->sc_udev,
+					 (struct usb_device_info_old *)addr, 1);
+
 		break;
 
         case USB_GET_STRING_DESC:
Index: usb.c
===================================================================
RCS file: /cvsroot/src/sys/dev/usb/usb.c,v
retrieving revision 1.87
diff -u -r1.87 usb.c
--- usb.c	9 Jun 2006 21:33:42 -0000	1.87
+++ usb.c	29 Aug 2006 13:57:54 -0000
@@ -142,6 +142,8 @@
 
 Static int usb_get_next_event(struct usb_event *);
 
+Static void usb_copy_old_devinfo(struct usb_device_info_old *, const struct usb_device_info *);
+
 Static const char *usbrev_str[] = USBREV_STR;
 
 USB_DECLARE_DRIVER(usb);
@@ -408,14 +410,28 @@
 int
 usbread(dev_t dev, struct uio *uio, int flag)
 {
-	struct usb_event *ue = usb_alloc_event();
-	int s, error, n;
+	struct usb_event *ue;
+	struct usb_event_old *ueo;
+	int s, error, n, useold;
 
 	if (minor(dev) != USB_DEV_MINOR)
 		return (ENXIO);
 
-	if (uio->uio_resid != sizeof(struct usb_event))
+	useold = 0;
+	/* XXXGCC */
+	ueo = NULL;
+	switch (uio->uio_resid) {
+	case sizeof(struct usb_event_old):
+		ueo = malloc(sizeof(struct usb_event_old), M_USBDEV,
+			     M_WAITOK|M_ZERO);
+		useold = 1;
+		/* FALLTHRU */
+	case sizeof(struct usb_event):
+		ue = usb_alloc_event();
+		break;
+	default:
 		return (EINVAL);
+	}
 
 	error = 0;
 	s = splusb();
@@ -432,9 +448,40 @@
 			break;
 	}
 	splx(s);
-	if (!error)
-		error = uiomove((void *)ue, uio->uio_resid, uio);
+	if (!error) {
+		if (useold) { /* copy fields to old struct */
+			ueo->ue_type = ue->ue_type;
+			memcpy(&ueo->ue_time, &ue->ue_time,
+			      sizeof(struct timespec));
+			switch (ue->ue_type) {
+				case USB_EVENT_DEVICE_ATTACH:
+				case USB_EVENT_DEVICE_DETACH:
+					usb_copy_old_devinfo(&ueo->u.ue_device, &ue->u.ue_device);
+					break;
+
+				case USB_EVENT_CTRLR_ATTACH:
+				case USB_EVENT_CTRLR_DETACH:
+					ueo->u.ue_ctrlr.ue_bus=ue->u.ue_ctrlr.ue_bus;
+					break;
+
+				case USB_EVENT_DRIVER_ATTACH:
+				case USB_EVENT_DRIVER_DETACH:
+					ueo->u.ue_driver.ue_cookie=ue->u.ue_driver.ue_cookie;
+					memcpy(ueo->u.ue_driver.ue_devname,
+					       ue->u.ue_driver.ue_devname,  
+					       sizeof(ue->u.ue_driver.ue_devname));
+					break;
+				default:
+					;
+			}
+
+			error = uiomove((void *)ueo, uio->uio_resid, uio);
+		} else
+			error = uiomove((void *)ue, uio->uio_resid, uio);
+	}
 	usb_free_event(ue);
+	if (useold)
+		free(ueo, M_USBDEV);
 
 	return (error);
 }
@@ -554,17 +601,26 @@
 	}
 
 	case USB_DEVICEINFO:
+	case USB_DEVICEINFO_OLD:
 	{
 		struct usb_device_info *di = (void *)data;
-		int addr = di->udi_addr;
+		struct usb_device_info_old *dio = (void *)data;
+		int addr;
 		usbd_device_handle dev;
 
+		if (cmd == USB_DEVICEINFO)
+			addr=di->udi_addr;
+		else
+			addr=dio->udi_addr;
 		if (addr < 1 || addr >= USB_MAX_DEVICES)
 			return (EINVAL);
 		dev = sc->sc_bus->devices[addr];
 		if (dev == NULL)
 			return (ENXIO);
-		usbd_fill_deviceinfo(dev, di, 1);
+		if (cmd == USB_DEVICEINFO)
+			usbd_fill_deviceinfo(dev, di, 1);
+		else
+			usbd_fill_deviceinfo_old(dev, dio, 1);
 		break;
 	}
 
@@ -860,3 +916,60 @@
 
 	return (0);
 }
+
+Static void
+usb_copy_old_devinfo(struct usb_device_info_old *uo,
+		     const struct usb_device_info *ue)
+{
+	const unsigned char *p;
+	unsigned char *q;
+	int i, n;
+
+	uo->udi_bus = ue->udi_bus;
+	uo->udi_addr = ue->udi_addr;       
+	uo->udi_cookie = ue->udi_cookie;
+	for (i = 0, p = (const unsigned char *)ue->udi_product,
+	     q = (unsigned char *)uo->udi_product;
+	     *p && i < USB_MAX_STRING_LEN - 1; p++) {
+		if (*p < 0x80)
+			q[i++] = *p;
+		else {
+			q[i++] = '?';
+			if ((*p & 0xe0) == 0xe0)
+				p++;
+			p++;
+		}
+	}
+	q[i] = 0;
+
+	for (i = 0, p = ue->udi_vendor, q = uo->udi_vendor;
+	     *p && i < USB_MAX_STRING_LEN - 1; p++) {
+		if (* p < 0x80)
+			q[i++] = *p;
+		else {
+			q[i++] = '?';
+			p++;
+			if ((*p & 0xe0) == 0xe0)
+				p++;
+		}
+	}
+	q[i] = 0;
+
+	memcpy(uo->udi_release, ue->udi_release, sizeof(uo->udi_release));
+
+	uo->udi_productNo = ue->udi_productNo;
+	uo->udi_vendorNo = ue->udi_vendorNo;
+	uo->udi_releaseNo = ue->udi_releaseNo;
+	uo->udi_class = ue->udi_class;
+	uo->udi_subclass = ue->udi_subclass;
+	uo->udi_protocol = ue->udi_protocol;
+	uo->udi_config = ue->udi_config;
+	uo->udi_speed = ue->udi_speed;
+	uo->udi_power = ue->udi_power;    
+	uo->udi_nports = ue->udi_nports;
+
+	for (n=0; n<USB_MAX_DEVNAMES; n++)
+		memcpy(uo->udi_devnames[n],
+		       ue->udi_devnames[n], USB_MAX_DEVNAMELEN);
+	memcpy(uo->udi_ports, ue->udi_ports, sizeof(uo->udi_ports));
+}
Index: usb.h
===================================================================
RCS file: /cvsroot/src/sys/dev/usb/usb.h,v
retrieving revision 1.74
diff -u -r1.74 usb.h
--- usb.h	24 Jul 2006 14:24:50 -0000	1.74
+++ usb.h	29 Aug 2006 13:57:55 -0000
@@ -621,6 +621,28 @@
 #define USB_PORT_DISABLED 0xfc
 };
 
+/* <=3.0 had this layout of the structure */
+struct usb_device_info_old {
+        u_int8_t        udi_bus;
+        u_int8_t        udi_addr;       /* device address */
+        usb_event_cookie_t udi_cookie;
+        char            udi_product[USB_MAX_STRING_LEN];
+        char            udi_vendor[USB_MAX_STRING_LEN];
+        char            udi_release[8];
+        u_int16_t       udi_productNo;
+        u_int16_t       udi_vendorNo;
+        u_int16_t       udi_releaseNo;
+        u_int8_t        udi_class;
+        u_int8_t        udi_subclass;
+        u_int8_t        udi_protocol;
+        u_int8_t        udi_config;
+        u_int8_t        udi_speed;
+        int             udi_power;      /* power consumption in mA, 0 if selfpowered */
+        int             udi_nports;
+        char            udi_devnames[USB_MAX_DEVNAMES][USB_MAX_DEVNAMELEN];
+        u_int8_t        udi_ports[16];/* hub only: addresses of devices on ports */
+};
+
 struct usb_ctl_report {
 	int	ucr_report;
 	u_char	ucr_data[1024];	/* filled data size will vary */
@@ -659,11 +681,29 @@
 	} u;
 };
 
+/* old <=3.0 compat event */
+struct usb_event_old {
+	int                     ue_type;
+	struct timespec         ue_time;
+	union {
+		struct {
+			int                     ue_bus;
+		} ue_ctrlr;
+		struct usb_device_info_old          ue_device;
+		struct {
+			usb_event_cookie_t      ue_cookie;
+			char                    ue_devname[16];
+		} ue_driver;
+	} u;
+};
+
+
 /* USB controller */
 #define USB_REQUEST		_IOWR('U', 1, struct usb_ctl_request)
 #define USB_SETDEBUG		_IOW ('U', 2, int)
 #define USB_DISCOVER		_IO  ('U', 3)
 #define USB_DEVICEINFO		_IOWR('U', 4, struct usb_device_info)
+#define USB_DEVICEINFO_OLD	_IOWR('U', 4, struct usb_device_info_old)
 #define USB_DEVICESTATS		_IOR ('U', 5, struct usb_device_stats)
 
 /* Generic HID device */
@@ -687,6 +727,7 @@
 #define USB_GET_STRING_DESC	_IOWR('U', 110, struct usb_string_desc)
 #define USB_DO_REQUEST		_IOWR('U', 111, struct usb_ctl_request)
 #define USB_GET_DEVICEINFO	_IOR ('U', 112, struct usb_device_info)
+#define USB_GET_DEVICEINFO_OLD	_IOR ('U', 112, struct usb_device_info_old)
 #define USB_SET_SHORT_XFER	_IOW ('U', 113, int)
 #define USB_SET_TIMEOUT		_IOW ('U', 114, int)
 #define USB_SET_BULK_RA		_IOW ('U', 115, int)
Index: usb_subr.c
===================================================================
RCS file: /cvsroot/src/sys/dev/usb/usb_subr.c,v
retrieving revision 1.135
diff -u -r1.135 usb_subr.c
--- usb_subr.c	11 Jun 2006 16:00:08 -0000	1.135
+++ usb_subr.c	29 Aug 2006 13:57:55 -0000
@@ -83,8 +83,9 @@
 Static usbd_status usbd_set_config(usbd_device_handle, int);
 Static void usbd_devinfo(usbd_device_handle, int, char *, size_t);
 Static void usbd_devinfo_vp(usbd_device_handle dev,
-			    char v[USB_MAX_ENCODED_STRING_LEN],
-			    char p[USB_MAX_ENCODED_STRING_LEN], int usedev);
+			    char *v,
+			    char *p, int usedev,
+			    int useencoded );
 Static int usbd_getnewaddr(usbd_bus_handle bus);
 #if defined(__NetBSD__)
 Static int usbd_print(void *, const char *);
@@ -208,8 +209,8 @@
 }
 
 Static void
-usbd_devinfo_vp(usbd_device_handle dev, char v[USB_MAX_ENCODED_STRING_LEN],
-		char p[USB_MAX_ENCODED_STRING_LEN], int usedev)
+usbd_devinfo_vp(usbd_device_handle dev, char *v,
+		char *p, int usedev, int useencoded)
 {
 	usb_device_descriptor_t *udd = &dev->ddesc;
 #ifdef USBVERBOSE
@@ -221,10 +222,10 @@
 		return;
 
 	if (usedev) {
-		if (usbd_get_string(dev, udd->iManufacturer, v) ==
+		if (usbd_get_string(dev, udd->iManufacturer, v, useencoded) ==
 		    USBD_NORMAL_COMPLETION)
 			usbd_trim_spaces(v);
-		if (usbd_get_string(dev, udd->iProduct, p) ==
+		if (usbd_get_string(dev, udd->iProduct, p, useencoded) ==
 		    USBD_NORMAL_COMPLETION)
 			usbd_trim_spaces(p);
 	}
@@ -273,7 +274,7 @@
 
 	ep = cp + l;
 
-	usbd_devinfo_vp(dev, vendor, product, 1);
+	usbd_devinfo_vp(dev, vendor, product, 1, 1);
 	cp += snprintf(cp, ep - cp, "%s %s", vendor, product);
 	if (showclass)
 		cp += snprintf(cp, ep - cp, ", class %d/%d",
@@ -1254,13 +1255,13 @@
 	di->udi_bus = USBDEVUNIT(dev->bus->bdev);
 	di->udi_addr = dev->address;
 	di->udi_cookie = dev->cookie;
-	usbd_devinfo_vp(dev, di->udi_vendor, di->udi_product, usedev);
+	usbd_devinfo_vp(dev, di->udi_vendor, di->udi_product, usedev, 1);
 	usbd_printBCD(di->udi_release, sizeof(di->udi_release),
 	    UGETW(dev->ddesc.bcdDevice));
 	di->udi_serial[0] = 0;
 	if (usedev)
 		(void)usbd_get_string(dev, dev->ddesc.iSerialNumber,
-				      di->udi_serial);
+				      di->udi_serial, 1);
 	di->udi_vendorNo = UGETW(dev->ddesc.idVendor);
 	di->udi_productNo = UGETW(dev->ddesc.idProduct);
 	di->udi_releaseNo = UGETW(dev->ddesc.bcdDevice);
@@ -1311,6 +1312,70 @@
 }
 
 void
+usbd_fill_deviceinfo_old(usbd_device_handle dev, struct usb_device_info_old *di,
+                         int usedev)
+{
+	struct usbd_port *p;
+	int i, err, s;
+
+	di->udi_bus = USBDEVUNIT(dev->bus->bdev);
+	di->udi_addr = dev->address;
+	di->udi_cookie = dev->cookie;
+	usbd_devinfo_vp(dev, di->udi_vendor, di->udi_product, usedev, 0);
+	usbd_printBCD(di->udi_release, sizeof(di->udi_release),
+	    UGETW(dev->ddesc.bcdDevice));
+	di->udi_vendorNo = UGETW(dev->ddesc.idVendor);
+	di->udi_productNo = UGETW(dev->ddesc.idProduct);
+	di->udi_releaseNo = UGETW(dev->ddesc.bcdDevice);
+	di->udi_class = dev->ddesc.bDeviceClass;
+	di->udi_subclass = dev->ddesc.bDeviceSubClass;
+	di->udi_protocol = dev->ddesc.bDeviceProtocol;
+	di->udi_config = dev->config;
+	di->udi_power = dev->self_powered ? 0 : dev->power;
+	di->udi_speed = dev->speed;
+
+	if (dev->subdevs != NULL) {
+		for (i = 0; dev->subdevs[i] &&
+			     i < USB_MAX_DEVNAMES; i++) {
+			strncpy(di->udi_devnames[i], USBDEVPTRNAME(dev->subdevs[i]),
+				USB_MAX_DEVNAMELEN);
+			di->udi_devnames[i][USB_MAX_DEVNAMELEN-1] = '\0';
+		}
+	} else {
+		i = 0;
+	}
+	for (/*i is set */; i < USB_MAX_DEVNAMES; i++)
+		di->udi_devnames[i][0] = 0;		 /* empty */
+
+	if (dev->hub) {
+		for (i = 0;
+		     i < sizeof(di->udi_ports) / sizeof(di->udi_ports[0]) &&
+			     i < dev->hub->hubdesc.bNbrPorts;
+		     i++) {
+			p = &dev->hub->ports[i];
+			if (p->device)
+				err = p->device->address;
+			else {
+				s = UGETW(p->status.wPortStatus);
+				if (s & UPS_PORT_ENABLED)
+					err = USB_PORT_ENABLED;
+				else if (s & UPS_SUSPEND)
+					err = USB_PORT_SUSPENDED;
+				else if (s & UPS_PORT_POWER)
+					err = USB_PORT_POWERED;
+				else
+					err = USB_PORT_DISABLED;
+			}
+			di->udi_ports[i] = err;
+		}
+		di->udi_nports = dev->hub->hubdesc.bNbrPorts;
+	} else
+		di->udi_nports = 0;
+}
+
+
+
+void
 usb_free_device(usbd_device_handle dev)
 {
 	int ifcidx, nifc;
Index: usbdi.c
===================================================================
RCS file: /cvsroot/src/sys/dev/usb/usbdi.c,v
retrieving revision 1.110
diff -u -r1.110 usbdi.c
--- usbdi.c	24 Dec 2005 20:27:52 -0000	1.110
+++ usbdi.c	29 Aug 2006 13:57:55 -0000
@@ -1176,12 +1176,12 @@
 }
 
 usbd_status
-usbd_get_string(usbd_device_handle dev, int si, char *buf)
+usbd_get_string(usbd_device_handle dev, int si, char *buf, int unicode)
 {
 	int swap = dev->quirks->uq_flags & UQ_SWAP_UNICODE;
 	usb_string_descriptor_t us;
 	char *s;
-	int i, n;
+	int i, j, n;
 	u_int16_t c;
 	usbd_status err;
 	int size;
@@ -1208,23 +1208,34 @@
 		return (err);
 	s = buf;
 	n = size / 2 - 1;
-	for (i = 0; i < n; i++) {
-		c = UGETW(us.bString[i]);
-		if (swap)
-			c = (c >> 8) | (c << 8);
-		/* Encode (16-bit) Unicode as UTF8. */
-		if (c < 0x0080) {
-			*s++ = c;
-		} else if (c < 0x0800) {
-			*s++ = 0xc0 | (c >> 6);
-			*s++ = 0x80 | (c & 0x3f);
-		} else {
-			*s++ = 0xe0 | (c >> 12);
-			*s++ = 0x80 | ((c >> 6) & 0x3f);
-			*s++ = 0x80 | (c & 0x3f);
+	if (unicode) {
+		for (i = 0; i < n; i++) {
+			c = UGETW(us.bString[i]);
+			if (swap)
+				c = (c >> 8) | (c << 8);
+			if (c < 0x0080) {
+				*s++ = c;
+			} else if (c < 0x0800) {
+				*s++ = 0xc0 | (c >> 6);
+				*s++ = 0x80 | (c & 0x3f);
+			} else {
+				*s++ = 0xe0 | (c >> 12);
+				*s++ = 0x80 | ((c >> 6) & 0x3f);
+				*s++ = 0x80 | (c & 0x3f);
+			}
+		}
+		*s++ = 0;
+	}
+	else {
+		for (i = j = 0; i < n && j < USB_MAX_STRING_LEN - 1; i++) {
+			c = UGETW(us.bString[i]);
+			if (swap)
+				c = (c >> 8) | (c << 8);
+			/* Encode (16-bit) Unicode as UTF8. */
+			s[j++] = (c < 0x80) ? c : '?';
 		}
+		s[j] = 0;
 	}
-	*s++ = 0;
 	return (USBD_NORMAL_COMPLETION);
 }
 
Index: usbdi.h
===================================================================
RCS file: /cvsroot/src/sys/dev/usb/usbdi.h,v
retrieving revision 1.69
diff -u -r1.69 usbdi.h
--- usbdi.h	28 Nov 2005 13:14:48 -0000	1.69
+++ usbdi.h	29 Aug 2006 13:57:55 -0000
@@ -150,6 +150,7 @@
 int usbd_get_no_alts(usb_config_descriptor_t *, int);
 usbd_status  usbd_get_interface(usbd_interface_handle, u_int8_t *);
 void usbd_fill_deviceinfo(usbd_device_handle, struct usb_device_info *, int);
+void usbd_fill_deviceinfo_old(usbd_device_handle, struct usb_device_info_old *, int);
 int usbd_get_interface_altindex(usbd_interface_handle);
 
 usb_interface_descriptor_t *usbd_find_idesc(usb_config_descriptor_t *,
@@ -176,7 +177,7 @@
 
 int usbd_ratecheck(struct timeval *);
 
-usbd_status usbd_get_string(usbd_device_handle, int, char *);
+usbd_status usbd_get_string(usbd_device_handle, int, char *, int);
 
 /* An iterator for descriptors. */
 typedef struct {

--MGYHOYXEY6WxJCY8--