Commit 0cce2eda authored by Daniel Mack's avatar Daniel Mack Committed by Greg Kroah-Hartman

USB: fix LANGID=0 regression

commit b7af0bb2 ("USB: allow malformed LANGID descriptors") broke support
for devices without string descriptor support.

Reporting string descriptors is optional to USB devices, and a device
lets us know it can't deal with strings by responding to the LANGID
request with a STALL token.

The kernel handled that correctly before b7af0bb2 came in, but failed
hard if the LANGID was reported but broken. More than that, if a device
was not able to provide string descriptors, the LANGID was retrieved
over and over again at each string read request.

This patch changes the behaviour so that

 a) the LANGID is only queried once
 b) devices which can't handle string requests are not asked again
 c) devices with malformed LANGID values have a sane fallback to 0x0409
Signed-off-by: default avatarDaniel Mack <daniel@caiaq.de>
Acked-by: default avatarAlan Stern <stern@rowland.harvard.edu>
Cc: stable <stable@kernel.org>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@suse.de>
parent c5f3d87d
...@@ -806,6 +806,48 @@ static int usb_string_sub(struct usb_device *dev, unsigned int langid, ...@@ -806,6 +806,48 @@ static int usb_string_sub(struct usb_device *dev, unsigned int langid,
return rc; 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 * usb_string - returns UTF-8 version of a string descriptor
* @dev: the device whose string descriptor is being retrieved * @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) ...@@ -837,24 +879,9 @@ int usb_string(struct usb_device *dev, int index, char *buf, size_t size)
if (!tbuf) if (!tbuf)
return -ENOMEM; return -ENOMEM;
/* get langid for strings if it's not yet known */ err = usb_get_langid(dev, tbuf);
if (!dev->have_langid) { if (err < 0)
err = usb_string_sub(dev, 0, 0, tbuf); goto errout;
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_string_sub(dev, dev->string_langid, index, tbuf); err = usb_string_sub(dev, dev->string_langid, index, tbuf);
if (err < 0) if (err < 0)
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment