diff options
Diffstat (limited to 'drivers/usb/core')
| -rw-r--r-- | drivers/usb/core/Kconfig | 2 | ||||
| -rw-r--r-- | drivers/usb/core/config.c | 48 | ||||
| -rw-r--r-- | drivers/usb/core/devices.c | 10 | ||||
| -rw-r--r-- | drivers/usb/core/devio.c | 88 | ||||
| -rw-r--r-- | drivers/usb/core/hcd.c | 2 | ||||
| -rw-r--r-- | drivers/usb/core/hcd.h | 4 | ||||
| -rw-r--r-- | drivers/usb/core/hub.c | 40 | ||||
| -rw-r--r-- | drivers/usb/core/hub.h | 6 | ||||
| -rw-r--r-- | drivers/usb/core/message.c | 63 | 
9 files changed, 167 insertions, 96 deletions
diff --git a/drivers/usb/core/Kconfig b/drivers/usb/core/Kconfig index 69280c35b5c..ad925946f86 100644 --- a/drivers/usb/core/Kconfig +++ b/drivers/usb/core/Kconfig @@ -28,7 +28,7 @@ comment "Miscellaneous USB options"  	depends on USB  config USB_DEVICEFS -	bool "USB device filesystem (DEPRECATED)" if EMBEDDED +	bool "USB device filesystem (DEPRECATED)"  	depends on USB  	---help---  	  If you say Y here (and to "/proc file system support" in the "File diff --git a/drivers/usb/core/config.c b/drivers/usb/core/config.c index 24dfb33f90c..a16c538d013 100644 --- a/drivers/usb/core/config.c +++ b/drivers/usb/core/config.c @@ -80,38 +80,18 @@ static int usb_parse_ss_endpoint_companion(struct device *ddev, int cfgno,  	int max_tx;  	int i; -	/* Allocate space for the SS endpoint companion descriptor */ -	ep->ss_ep_comp = kzalloc(sizeof(struct usb_host_ss_ep_comp), -			GFP_KERNEL); -	if (!ep->ss_ep_comp) -		return -ENOMEM;  	desc = (struct usb_ss_ep_comp_descriptor *) buffer;  	if (desc->bDescriptorType != USB_DT_SS_ENDPOINT_COMP) {  		dev_warn(ddev, "No SuperSpeed endpoint companion for config %d "  				" interface %d altsetting %d ep %d: "  				"using minimum values\n",  				cfgno, inum, asnum, ep->desc.bEndpointAddress); -		ep->ss_ep_comp->desc.bLength = USB_DT_SS_EP_COMP_SIZE; -		ep->ss_ep_comp->desc.bDescriptorType = USB_DT_SS_ENDPOINT_COMP; -		ep->ss_ep_comp->desc.bMaxBurst = 0; -		/* -		 * Leave bmAttributes as zero, which will mean no streams for -		 * bulk, and isoc won't support multiple bursts of packets. -		 * With bursts of only one packet, and a Mult of 1, the max -		 * amount of data moved per endpoint service interval is one -		 * packet. -		 */ -		if (usb_endpoint_xfer_isoc(&ep->desc) || -				usb_endpoint_xfer_int(&ep->desc)) -			ep->ss_ep_comp->desc.wBytesPerInterval = -				ep->desc.wMaxPacketSize;  		/*  		 * The next descriptor is for an Endpoint or Interface,  		 * no extra descriptors to copy into the companion structure,  		 * and we didn't eat up any of the buffer.  		 */ -		retval = 0; -		goto valid; +		return 0;  	}  	memcpy(&ep->ss_ep_comp->desc, desc, USB_DT_SS_EP_COMP_SIZE);  	desc = &ep->ss_ep_comp->desc; @@ -320,6 +300,28 @@ static int usb_parse_endpoint(struct device *ddev, int cfgno, int inum,  		buffer += i;  		size -= i; +		/* Allocate space for the SS endpoint companion descriptor */ +		endpoint->ss_ep_comp = kzalloc(sizeof(struct usb_host_ss_ep_comp), +				GFP_KERNEL); +		if (!endpoint->ss_ep_comp) +			return -ENOMEM; + +		/* Fill in some default values (may be overwritten later) */ +		endpoint->ss_ep_comp->desc.bLength = USB_DT_SS_EP_COMP_SIZE; +		endpoint->ss_ep_comp->desc.bDescriptorType = USB_DT_SS_ENDPOINT_COMP; +		endpoint->ss_ep_comp->desc.bMaxBurst = 0; +		/* +		 * Leave bmAttributes as zero, which will mean no streams for +		 * bulk, and isoc won't support multiple bursts of packets. +		 * With bursts of only one packet, and a Mult of 1, the max +		 * amount of data moved per endpoint service interval is one +		 * packet. +		 */ +		if (usb_endpoint_xfer_isoc(&endpoint->desc) || +				usb_endpoint_xfer_int(&endpoint->desc)) +			endpoint->ss_ep_comp->desc.wBytesPerInterval = +				endpoint->desc.wMaxPacketSize; +  		if (size > 0) {  			retval = usb_parse_ss_endpoint_companion(ddev, cfgno,  					inum, asnum, endpoint, num_ep, buffer, @@ -329,6 +331,10 @@ static int usb_parse_endpoint(struct device *ddev, int cfgno, int inum,  				retval = buffer - buffer0;  			}  		} else { +			dev_warn(ddev, "config %d interface %d altsetting %d " +				"endpoint 0x%X has no " +				"SuperSpeed companion descriptor\n", +				cfgno, inum, asnum, d->bEndpointAddress);  			retval = buffer - buffer0;  		}  	} else { diff --git a/drivers/usb/core/devices.c b/drivers/usb/core/devices.c index 73c108d117b..96f11715cd2 100644 --- a/drivers/usb/core/devices.c +++ b/drivers/usb/core/devices.c @@ -136,17 +136,19 @@ static const struct class_info clas_info[] =  	{USB_CLASS_AUDIO,		"audio"},  	{USB_CLASS_COMM,		"comm."},  	{USB_CLASS_HID,			"HID"}, -	{USB_CLASS_HUB,			"hub"},  	{USB_CLASS_PHYSICAL,		"PID"}, +	{USB_CLASS_STILL_IMAGE,		"still"},  	{USB_CLASS_PRINTER,		"print"},  	{USB_CLASS_MASS_STORAGE,	"stor."}, +	{USB_CLASS_HUB,			"hub"},  	{USB_CLASS_CDC_DATA,		"data"}, -	{USB_CLASS_APP_SPEC,		"app."}, -	{USB_CLASS_VENDOR_SPEC,		"vend."}, -	{USB_CLASS_STILL_IMAGE,		"still"},  	{USB_CLASS_CSCID,		"scard"},  	{USB_CLASS_CONTENT_SEC,		"c-sec"},  	{USB_CLASS_VIDEO,		"video"}, +	{USB_CLASS_WIRELESS_CONTROLLER,	"wlcon"}, +	{USB_CLASS_MISC,		"misc"}, +	{USB_CLASS_APP_SPEC,		"app."}, +	{USB_CLASS_VENDOR_SPEC,		"vend."},  	{-1,				"unk."}		/* leave as last */  }; diff --git a/drivers/usb/core/devio.c b/drivers/usb/core/devio.c index 308609039c7..4247eccf858 100644 --- a/drivers/usb/core/devio.c +++ b/drivers/usb/core/devio.c @@ -325,21 +325,34 @@ static void async_completed(struct urb *urb)  	struct async *as = urb->context;  	struct dev_state *ps = as->ps;  	struct siginfo sinfo; +	struct pid *pid = NULL; +	uid_t uid = 0; +	uid_t euid = 0; +	u32 secid = 0; +	int signr;  	spin_lock(&ps->lock);  	list_move_tail(&as->asynclist, &ps->async_completed); -	spin_unlock(&ps->lock);  	as->status = urb->status; -	if (as->signr) { +	signr = as->signr; +	if (signr) {  		sinfo.si_signo = as->signr;  		sinfo.si_errno = as->status;  		sinfo.si_code = SI_ASYNCIO;  		sinfo.si_addr = as->userurb; -		kill_pid_info_as_uid(as->signr, &sinfo, as->pid, as->uid, -				      as->euid, as->secid); +		pid = as->pid; +		uid = as->uid; +		euid = as->euid; +		secid = as->secid;  	}  	snoop(&urb->dev->dev, "urb complete\n");  	snoop_urb(urb, as->userurb); +	spin_unlock(&ps->lock); + +	if (signr) +		kill_pid_info_as_uid(sinfo.si_signo, &sinfo, pid, uid, +				      euid, secid); +  	wake_up(&ps->wait);  } @@ -582,7 +595,7 @@ static int usbdev_open(struct inode *inode, struct file *file)  	if (!ps)  		goto out; -	ret = -ENOENT; +	ret = -ENODEV;  	/* usbdev device-node */  	if (imajor(inode) == USB_DEVICE_MAJOR) @@ -982,7 +995,7 @@ static int proc_do_submiturb(struct dev_state *ps, struct usbdevfs_urb *uurb,  				USBDEVFS_URB_ZERO_PACKET |  				USBDEVFS_URB_NO_INTERRUPT))  		return -EINVAL; -	if (!uurb->buffer) +	if (uurb->buffer_length > 0 && !uurb->buffer)  		return -EINVAL;  	if (!(uurb->type == USBDEVFS_URB_TYPE_CONTROL &&  	    (uurb->endpoint & ~USB_ENDPOINT_DIR_MASK) == 0)) { @@ -1038,11 +1051,6 @@ static int proc_do_submiturb(struct dev_state *ps, struct usbdevfs_urb *uurb,  			is_in = 0;  			uurb->endpoint &= ~USB_DIR_IN;  		} -		if (!access_ok(is_in ? VERIFY_WRITE : VERIFY_READ, -				uurb->buffer, uurb->buffer_length)) { -			kfree(dr); -			return -EFAULT; -		}  		snoop(&ps->dev->dev, "control urb: bRequest=%02x "  			"bRrequestType=%02x wValue=%04x "  			"wIndex=%04x wLength=%04x\n", @@ -1062,9 +1070,6 @@ static int proc_do_submiturb(struct dev_state *ps, struct usbdevfs_urb *uurb,  		uurb->number_of_packets = 0;  		if (uurb->buffer_length > MAX_USBFS_BUFFER_SIZE)  			return -EINVAL; -		if (!access_ok(is_in ? VERIFY_WRITE : VERIFY_READ, -				uurb->buffer, uurb->buffer_length)) -			return -EFAULT;  		snoop(&ps->dev->dev, "bulk urb\n");  		break; @@ -1106,28 +1111,35 @@ static int proc_do_submiturb(struct dev_state *ps, struct usbdevfs_urb *uurb,  			return -EINVAL;  		if (uurb->buffer_length > MAX_USBFS_BUFFER_SIZE)  			return -EINVAL; -		if (!access_ok(is_in ? VERIFY_WRITE : VERIFY_READ, -				uurb->buffer, uurb->buffer_length)) -			return -EFAULT;  		snoop(&ps->dev->dev, "interrupt urb\n");  		break;  	default:  		return -EINVAL;  	} -	as = alloc_async(uurb->number_of_packets); -	if (!as) { +	if (uurb->buffer_length > 0 && +			!access_ok(is_in ? VERIFY_WRITE : VERIFY_READ, +				uurb->buffer, uurb->buffer_length)) {  		kfree(isopkt);  		kfree(dr); -		return -ENOMEM; +		return -EFAULT;  	} -	as->urb->transfer_buffer = kmalloc(uurb->buffer_length, GFP_KERNEL); -	if (!as->urb->transfer_buffer) { +	as = alloc_async(uurb->number_of_packets); +	if (!as) {  		kfree(isopkt);  		kfree(dr); -		free_async(as);  		return -ENOMEM;  	} +	if (uurb->buffer_length > 0) { +		as->urb->transfer_buffer = kmalloc(uurb->buffer_length, +				GFP_KERNEL); +		if (!as->urb->transfer_buffer) { +			kfree(isopkt); +			kfree(dr); +			free_async(as); +			return -ENOMEM; +		} +	}  	as->urb->dev = ps->dev;  	as->urb->pipe = (uurb->type << 30) |  			__create_pipe(ps->dev, uurb->endpoint & 0xf) | @@ -1169,7 +1181,7 @@ static int proc_do_submiturb(struct dev_state *ps, struct usbdevfs_urb *uurb,  	kfree(isopkt);  	as->ps = ps;  	as->userurb = arg; -	if (uurb->endpoint & USB_DIR_IN) +	if (is_in && uurb->buffer_length > 0)  		as->userbuffer = uurb->buffer;  	else  		as->userbuffer = NULL; @@ -1179,9 +1191,9 @@ static int proc_do_submiturb(struct dev_state *ps, struct usbdevfs_urb *uurb,  	as->uid = cred->uid;  	as->euid = cred->euid;  	security_task_getsecid(current, &as->secid); -	if (!is_in) { +	if (!is_in && uurb->buffer_length > 0) {  		if (copy_from_user(as->urb->transfer_buffer, uurb->buffer, -				as->urb->transfer_buffer_length)) { +				uurb->buffer_length)) {  			free_async(as);  			return -EFAULT;  		} @@ -1231,22 +1243,22 @@ static int processcompl(struct async *as, void __user * __user *arg)  	if (as->userbuffer)  		if (copy_to_user(as->userbuffer, urb->transfer_buffer,  				 urb->transfer_buffer_length)) -			return -EFAULT; +			goto err_out;  	if (put_user(as->status, &userurb->status)) -		return -EFAULT; +		goto err_out;  	if (put_user(urb->actual_length, &userurb->actual_length)) -		return -EFAULT; +		goto err_out;  	if (put_user(urb->error_count, &userurb->error_count)) -		return -EFAULT; +		goto err_out;  	if (usb_endpoint_xfer_isoc(&urb->ep->desc)) {  		for (i = 0; i < urb->number_of_packets; i++) {  			if (put_user(urb->iso_frame_desc[i].actual_length,  				     &userurb->iso_frame_desc[i].actual_length)) -				return -EFAULT; +				goto err_out;  			if (put_user(urb->iso_frame_desc[i].status,  				     &userurb->iso_frame_desc[i].status)) -				return -EFAULT; +				goto err_out;  		}  	} @@ -1255,6 +1267,10 @@ static int processcompl(struct async *as, void __user * __user *arg)  	if (put_user(addr, (void __user * __user *)arg))  		return -EFAULT;  	return 0; + +err_out: +	free_async(as); +	return -EFAULT;  }  static struct async *reap_as(struct dev_state *ps) @@ -1305,7 +1321,8 @@ static int get_urb32(struct usbdevfs_urb *kurb,  		     struct usbdevfs_urb32 __user *uurb)  {  	__u32  uptr; -	if (get_user(kurb->type, &uurb->type) || +	if (!access_ok(VERIFY_READ, uurb, sizeof(*uurb)) || +	    __get_user(kurb->type, &uurb->type) ||  	    __get_user(kurb->endpoint, &uurb->endpoint) ||  	    __get_user(kurb->status, &uurb->status) ||  	    __get_user(kurb->flags, &uurb->flags) || @@ -1520,8 +1537,9 @@ static int proc_ioctl_compat(struct dev_state *ps, compat_uptr_t arg)  	u32 udata;  	uioc = compat_ptr((long)arg); -	if (get_user(ctrl.ifno, &uioc->ifno) || -	    get_user(ctrl.ioctl_code, &uioc->ioctl_code) || +	if (!access_ok(VERIFY_READ, uioc, sizeof(*uioc)) || +	    __get_user(ctrl.ifno, &uioc->ifno) || +	    __get_user(ctrl.ioctl_code, &uioc->ioctl_code) ||  	    __get_user(udata, &uioc->data))  		return -EFAULT;  	ctrl.data = compat_ptr(udata); diff --git a/drivers/usb/core/hcd.c b/drivers/usb/core/hcd.c index ce3f453f02e..95ccfa0b9fc 100644 --- a/drivers/usb/core/hcd.c +++ b/drivers/usb/core/hcd.c @@ -648,7 +648,7 @@ void usb_hcd_poll_rh_status(struct usb_hcd *hcd)  	struct urb	*urb;  	int		length;  	unsigned long	flags; -	char		buffer[4];	/* Any root hubs with > 31 ports? */ +	char		buffer[6];	/* Any root hubs with > 31 ports? */  	if (unlikely(!hcd->rh_registered))  		return; diff --git a/drivers/usb/core/hcd.h b/drivers/usb/core/hcd.h index d397ecfd5b1..ec5c67ea07b 100644 --- a/drivers/usb/core/hcd.h +++ b/drivers/usb/core/hcd.h @@ -227,6 +227,10 @@ struct hc_driver {  		/* has a port been handed over to a companion? */  	int	(*port_handed_over)(struct usb_hcd *, int); +		/* CLEAR_TT_BUFFER completion callback */ +	void	(*clear_tt_buffer_complete)(struct usb_hcd *, +				struct usb_host_endpoint *); +  	/* xHCI specific functions */  		/* Called by usb_alloc_dev to alloc HC device structures */  	int	(*alloc_dev)(struct usb_hcd *, struct usb_device *); diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c index 2af3b4f0605..71f86c60d83 100644 --- a/drivers/usb/core/hub.c +++ b/drivers/usb/core/hub.c @@ -450,10 +450,10 @@ hub_clear_tt_buffer (struct usb_device *hdev, u16 devinfo, u16 tt)   * talking to TTs must queue control transfers (not just bulk and iso), so   * both can talk to the same hub concurrently.   */ -static void hub_tt_kevent (struct work_struct *work) +static void hub_tt_work(struct work_struct *work)  {  	struct usb_hub		*hub = -		container_of(work, struct usb_hub, tt.kevent); +		container_of(work, struct usb_hub, tt.clear_work);  	unsigned long		flags;  	int			limit = 100; @@ -462,6 +462,7 @@ static void hub_tt_kevent (struct work_struct *work)  		struct list_head	*next;  		struct usb_tt_clear	*clear;  		struct usb_device	*hdev = hub->hdev; +		const struct hc_driver	*drv;  		int			status;  		next = hub->tt.clear_list.next; @@ -471,21 +472,25 @@ static void hub_tt_kevent (struct work_struct *work)  		/* drop lock so HCD can concurrently report other TT errors */  		spin_unlock_irqrestore (&hub->tt.lock, flags);  		status = hub_clear_tt_buffer (hdev, clear->devinfo, clear->tt); -		spin_lock_irqsave (&hub->tt.lock, flags); -  		if (status)  			dev_err (&hdev->dev,  				"clear tt %d (%04x) error %d\n",  				clear->tt, clear->devinfo, status); + +		/* Tell the HCD, even if the operation failed */ +		drv = clear->hcd->driver; +		if (drv->clear_tt_buffer_complete) +			(drv->clear_tt_buffer_complete)(clear->hcd, clear->ep); +  		kfree(clear); +		spin_lock_irqsave(&hub->tt.lock, flags);  	}  	spin_unlock_irqrestore (&hub->tt.lock, flags);  }  /** - * usb_hub_tt_clear_buffer - clear control/bulk TT state in high speed hub - * @udev: the device whose split transaction failed - * @pipe: identifies the endpoint of the failed transaction + * usb_hub_clear_tt_buffer - clear control/bulk TT state in high speed hub + * @urb: an URB associated with the failed or incomplete split transaction   *   * High speed HCDs use this to tell the hub driver that some split control or   * bulk transaction failed in a way that requires clearing internal state of @@ -495,8 +500,10 @@ static void hub_tt_kevent (struct work_struct *work)   * It may not be possible for that hub to handle additional full (or low)   * speed transactions until that state is fully cleared out.   */ -void usb_hub_tt_clear_buffer (struct usb_device *udev, int pipe) +int usb_hub_clear_tt_buffer(struct urb *urb)  { +	struct usb_device	*udev = urb->dev; +	int			pipe = urb->pipe;  	struct usb_tt		*tt = udev->tt;  	unsigned long		flags;  	struct usb_tt_clear	*clear; @@ -508,7 +515,7 @@ void usb_hub_tt_clear_buffer (struct usb_device *udev, int pipe)  	if ((clear = kmalloc (sizeof *clear, GFP_ATOMIC)) == NULL) {  		dev_err (&udev->dev, "can't save CLEAR_TT_BUFFER state\n");  		/* FIXME recover somehow ... RESET_TT? */ -		return; +		return -ENOMEM;  	}  	/* info that CLEAR_TT_BUFFER needs */ @@ -520,14 +527,19 @@ void usb_hub_tt_clear_buffer (struct usb_device *udev, int pipe)  			: (USB_ENDPOINT_XFER_BULK << 11);  	if (usb_pipein (pipe))  		clear->devinfo |= 1 << 15; -	 + +	/* info for completion callback */ +	clear->hcd = bus_to_hcd(udev->bus); +	clear->ep = urb->ep; +  	/* tell keventd to clear state for this TT */  	spin_lock_irqsave (&tt->lock, flags);  	list_add_tail (&clear->clear_list, &tt->clear_list); -	schedule_work (&tt->kevent); +	schedule_work(&tt->clear_work);  	spin_unlock_irqrestore (&tt->lock, flags); +	return 0;  } -EXPORT_SYMBOL_GPL(usb_hub_tt_clear_buffer); +EXPORT_SYMBOL_GPL(usb_hub_clear_tt_buffer);  /* If do_delay is false, return the number of milliseconds the caller   * needs to delay. @@ -818,7 +830,7 @@ static void hub_quiesce(struct usb_hub *hub, enum hub_quiescing_type type)  	if (hub->has_indicators)  		cancel_delayed_work_sync(&hub->leds);  	if (hub->tt.hub) -		cancel_work_sync(&hub->tt.kevent); +		cancel_work_sync(&hub->tt.clear_work);  }  /* caller has locked the hub device */ @@ -935,7 +947,7 @@ static int hub_configure(struct usb_hub *hub,  	spin_lock_init (&hub->tt.lock);  	INIT_LIST_HEAD (&hub->tt.clear_list); -	INIT_WORK (&hub->tt.kevent, hub_tt_kevent); +	INIT_WORK(&hub->tt.clear_work, hub_tt_work);  	switch (hdev->descriptor.bDeviceProtocol) {  		case 0:  			break; diff --git a/drivers/usb/core/hub.h b/drivers/usb/core/hub.h index 889c0f32a40..de8081f065e 100644 --- a/drivers/usb/core/hub.h +++ b/drivers/usb/core/hub.h @@ -188,16 +188,18 @@ struct usb_tt {  	/* for control/bulk error recovery (CLEAR_TT_BUFFER) */  	spinlock_t		lock;  	struct list_head	clear_list;	/* of usb_tt_clear */ -	struct work_struct			kevent; +	struct work_struct	clear_work;  };  struct usb_tt_clear {  	struct list_head	clear_list;  	unsigned		tt;  	u16			devinfo; +	struct usb_hcd		*hcd; +	struct usb_host_endpoint	*ep;  }; -extern void usb_hub_tt_clear_buffer(struct usb_device *dev, int pipe); +extern int usb_hub_clear_tt_buffer(struct urb *urb);  extern void usb_ep0_reinit(struct usb_device *);  #endif /* __LINUX_HUB_H */ diff --git a/drivers/usb/core/message.c b/drivers/usb/core/message.c index 2bed83caacb..9720e699f47 100644 --- a/drivers/usb/core/message.c +++ b/drivers/usb/core/message.c @@ -806,6 +806,48 @@ static int usb_string_sub(struct usb_device *dev, unsigned int langid,  	return rc;  } +static int usb_get_langid(struct usb_device *dev, unsigned char *tbuf) +{ +	int err; + +	if (dev->have_langid) +		return 0; + +	if (dev->string_langid < 0) +		return -EPIPE; + +	err = usb_string_sub(dev, 0, 0, tbuf); + +	/* If the string was reported but is malformed, default to english +	 * (0x0409) */ +	if (err == -ENODATA || (err > 0 && err < 4)) { +		dev->string_langid = 0x0409; +		dev->have_langid = 1; +		dev_err(&dev->dev, +			"string descriptor 0 malformed (err = %d), " +			"defaulting to 0x%04x\n", +				err, dev->string_langid); +		return 0; +	} + +	/* In case of all other errors, we assume the device is not able to +	 * deal with strings at all. Set string_langid to -1 in order to +	 * prevent any string to be retrieved from the device */ +	if (err < 0) { +		dev_err(&dev->dev, "string descriptor 0 read error: %d\n", +					err); +		dev->string_langid = -1; +		return -EPIPE; +	} + +	/* always use the first langid listed */ +	dev->string_langid = tbuf[2] | (tbuf[3] << 8); +	dev->have_langid = 1; +	dev_dbg(&dev->dev, "default language 0x%04x\n", +				dev->string_langid); +	return 0; +} +  /**   * usb_string - returns UTF-8 version of a string descriptor   * @dev: the device whose string descriptor is being retrieved @@ -837,24 +879,9 @@ int usb_string(struct usb_device *dev, int index, char *buf, size_t size)  	if (!tbuf)  		return -ENOMEM; -	/* get langid for strings if it's not yet known */ -	if (!dev->have_langid) { -		err = usb_string_sub(dev, 0, 0, tbuf); -		if (err < 0) { -			dev_err(&dev->dev, -				"string descriptor 0 read error: %d\n", -				err); -		} else if (err < 4) { -			dev_err(&dev->dev, "string descriptor 0 too short\n"); -		} else { -			dev->string_langid = tbuf[2] | (tbuf[3] << 8); -			/* always use the first langid listed */ -			dev_dbg(&dev->dev, "default language 0x%04x\n", -				dev->string_langid); -		} - -		dev->have_langid = 1; -	} +	err = usb_get_langid(dev, tbuf); +	if (err < 0) +		goto errout;  	err = usb_string_sub(dev, dev->string_langid, index, tbuf);  	if (err < 0)  |