summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDaniel Drake <dsd@gentoo.org>2008-05-16 22:37:56 +0100
committerDaniel Drake <dsd@gentoo.org>2008-05-16 22:37:56 +0100
commit2b2e9c40b195261b09ac52ebdb93eef25c79de90 (patch)
tree324484c5a2583d6d1f85b601edbc5082113a1f0e
parent1298c51f516a7bf04ca9add1b7db14417cdc66f3 (diff)
downloadlibusb-2b2e9c40b195261b09ac52ebdb93eef25c79de90.tar.gz
Fix endianness with descriptor handling
Alan Stern pointed out that usbfs gives host-endian data, but sysfs gives bus-endian.
-rw-r--r--libusb/core.c3
-rw-r--r--libusb/descriptor.c94
-rw-r--r--libusb/libusbi.h22
-rw-r--r--libusb/os/linux_usbfs.c26
4 files changed, 87 insertions, 58 deletions
diff --git a/libusb/core.c b/libusb/core.c
index 28dafec..74ad527 100644
--- a/libusb/core.c
+++ b/libusb/core.c
@@ -356,8 +356,9 @@ int usbi_sanitize_device(struct libusb_device *dev)
int r;
unsigned char raw_desc[DEVICE_DESC_LENGTH];
uint8_t num_configurations;
+ int host_endian;
- r = usbi_backend->get_device_descriptor(dev, raw_desc);
+ r = usbi_backend->get_device_descriptor(dev, raw_desc, &host_endian);
if (r < 0)
return r;
diff --git a/libusb/descriptor.c b/libusb/descriptor.c
index 4a3be62..37dcc64 100644
--- a/libusb/descriptor.c
+++ b/libusb/descriptor.c
@@ -36,11 +36,13 @@
* for detected devices
*/
-int usbi_parse_descriptor(unsigned char *source, char *descriptor, void *dest)
+/* set host_endian if the w values are already in host endian format,
+ * as opposed to bus endian. */
+int usbi_parse_descriptor(unsigned char *source, char *descriptor, void *dest,
+ int host_endian)
{
unsigned char *sp = source, *dp = dest;
uint16_t w;
- uint32_t d;
char *cp;
for (cp = descriptor; *cp; cp++) {
@@ -49,22 +51,16 @@ int usbi_parse_descriptor(unsigned char *source, char *descriptor, void *dest)
*dp++ = *sp++;
break;
case 'w': /* 16-bit word, convert from little endian to CPU */
- w = (sp[1] << 8) | sp[0]; sp += 2;
dp += ((unsigned long)dp & 1); /* Align to word boundary */
- *((uint16_t *)dp) = w; dp += 2;
- break;
- case 'd': /* 32-bit dword, convert from little endian to CPU */
- d = (sp[3] << 24) | (sp[2] << 16) | (sp[1] << 8) | sp[0]; sp += 4;
- dp += ((unsigned long)dp & 2); /* Align to dword boundary */
- *((uint32_t *)dp) = d; dp += 4;
- break;
- case 'W': /* 16-bit word, keep CPU endianess */
- dp += ((unsigned long)dp & 1); /* Align to word boundary */
- memcpy(dp, sp, 2); sp += 2; dp += 2;
- break;
- case 'D': /* 32-bit dword, keep CPU endianess */
- dp += ((unsigned long)dp & 2); /* Align to dword boundary */
- memcpy(dp, sp, 4); sp += 4; dp += 4;
+
+ if (host_endian) {
+ memcpy(dp, sp, 2);
+ } else {
+ w = (sp[1] << 8) | sp[0];
+ *((uint16_t *)dp) = w;
+ }
+ sp += 2;
+ dp += 2;
break;
}
}
@@ -79,7 +75,7 @@ static void clear_endpoint(struct libusb_endpoint_descriptor *endpoint)
}
static int parse_endpoint(struct libusb_endpoint_descriptor *endpoint,
- unsigned char *buffer, int size)
+ unsigned char *buffer, int size, int host_endian)
{
struct usb_descriptor_header header;
unsigned char *extra;
@@ -87,7 +83,7 @@ static int parse_endpoint(struct libusb_endpoint_descriptor *endpoint,
int parsed = 0;
int len;
- usbi_parse_descriptor(buffer, "bb", &header);
+ usbi_parse_descriptor(buffer, "bb", &header, 0);
/* Everything should be fine being passed into here, but we sanity */
/* check JIC */
@@ -103,9 +99,9 @@ static int parse_endpoint(struct libusb_endpoint_descriptor *endpoint,
}
if (header.bLength >= ENDPOINT_AUDIO_DESC_LENGTH)
- usbi_parse_descriptor(buffer, "bbbbwbbb", endpoint);
+ usbi_parse_descriptor(buffer, "bbbbwbbb", endpoint, host_endian);
else if (header.bLength >= ENDPOINT_DESC_LENGTH)
- usbi_parse_descriptor(buffer, "bbbbwb", endpoint);
+ usbi_parse_descriptor(buffer, "bbbbwb", endpoint, host_endian);
buffer += header.bLength;
size -= header.bLength;
@@ -115,7 +111,7 @@ static int parse_endpoint(struct libusb_endpoint_descriptor *endpoint,
/* descriptors */
begin = buffer;
while (size >= DESC_HEADER_LENGTH) {
- usbi_parse_descriptor(buffer, "bb", &header);
+ usbi_parse_descriptor(buffer, "bb", &header, 0);
if (header.bLength < 2) {
usbi_err("invalid descriptor length %d", header.bLength);
@@ -182,7 +178,7 @@ static void clear_interface(struct libusb_interface *interface)
}
static int parse_interface(struct libusb_interface *interface,
- unsigned char *buffer, int size)
+ unsigned char *buffer, int size, int host_endian)
{
int i;
int len;
@@ -209,7 +205,7 @@ static int parse_interface(struct libusb_interface *interface,
ifp = altsetting + interface->num_altsetting;
interface->num_altsetting++;
- usbi_parse_descriptor(buffer, "bbbbbbbbb", ifp);
+ usbi_parse_descriptor(buffer, "bbbbbbbbb", ifp, 0);
ifp->extra = NULL;
ifp->extra_length = 0;
ifp->endpoint = NULL;
@@ -223,7 +219,7 @@ static int parse_interface(struct libusb_interface *interface,
/* Skip over any interface, class or vendor descriptors */
while (size >= DESC_HEADER_LENGTH) {
- usbi_parse_descriptor(buffer, "bb", &header);
+ usbi_parse_descriptor(buffer, "bb", &header, 0);
if (header.bLength < 2) {
usbi_err("invalid descriptor of length %d", header.bLength);
r = LIBUSB_ERROR_IO;
@@ -256,7 +252,7 @@ static int parse_interface(struct libusb_interface *interface,
}
/* Did we hit an unexpected descriptor? */
- usbi_parse_descriptor(buffer, "bb", &header);
+ usbi_parse_descriptor(buffer, "bb", &header, 0);
if ((size >= DESC_HEADER_LENGTH) &&
((header.bDescriptorType == LIBUSB_DT_CONFIG) ||
(header.bDescriptorType == LIBUSB_DT_DEVICE)))
@@ -280,7 +276,7 @@ static int parse_interface(struct libusb_interface *interface,
memset(endpoint, 0, tmp);
for (i = 0; i < ifp->bNumEndpoints; i++) {
- usbi_parse_descriptor(buffer, "bb", &header);
+ usbi_parse_descriptor(buffer, "bb", &header, 0);
if (header.bLength > size) {
usbi_err("ran out of descriptors parsing");
@@ -288,7 +284,7 @@ static int parse_interface(struct libusb_interface *interface,
goto err;
}
- r = parse_endpoint(endpoint + i, buffer, size);
+ r = parse_endpoint(endpoint + i, buffer, size, host_endian);
if (r < 0)
goto err;
@@ -326,7 +322,7 @@ static void clear_configuration(struct libusb_config_descriptor *config)
}
static int parse_configuration(struct libusb_config_descriptor *config,
- unsigned char *buffer)
+ unsigned char *buffer, int host_endian)
{
int i;
int r;
@@ -335,7 +331,7 @@ static int parse_configuration(struct libusb_config_descriptor *config,
struct usb_descriptor_header header;
struct libusb_interface *interface;
- usbi_parse_descriptor(buffer, "bbwbbbbb", config);
+ usbi_parse_descriptor(buffer, "bbwbbbbb", config, host_endian);
size = config->wTotalLength;
if (config->bNumInterfaces > USB_MAXINTERFACES) {
@@ -364,7 +360,7 @@ static int parse_configuration(struct libusb_config_descriptor *config,
/* Specific descriptors */
begin = buffer;
while (size >= DESC_HEADER_LENGTH) {
- usbi_parse_descriptor(buffer, "bb", &header);
+ usbi_parse_descriptor(buffer, "bb", &header, 0);
if ((header.bLength > size) ||
(header.bLength < DESC_HEADER_LENGTH)) {
@@ -402,7 +398,7 @@ static int parse_configuration(struct libusb_config_descriptor *config,
}
}
- r = parse_interface(interface + i, buffer, size);
+ r = parse_interface(interface + i, buffer, size, host_endian);
if (r < 0)
goto err;
@@ -430,14 +426,21 @@ API_EXPORTED int libusb_get_device_descriptor(libusb_device *dev,
struct libusb_device_descriptor *desc)
{
unsigned char raw_desc[DEVICE_DESC_LENGTH];
+ int host_endian = 0;
int r;
usbi_dbg("");
- r = usbi_backend->get_device_descriptor(dev, raw_desc);
+ r = usbi_backend->get_device_descriptor(dev, raw_desc, &host_endian);
if (r < 0)
return r;
- usbi_parse_descriptor(raw_desc, "bbWbbbbWWWbbbb", desc);
+ memcpy((unsigned char *) desc, raw_desc, sizeof(raw_desc));
+ if (host_endian) {
+ desc->bcdUSB = libusb_cpu_to_le16(desc->bcdUSB);
+ desc->idVendor = libusb_cpu_to_le16(desc->idVendor);
+ desc->idProduct = libusb_cpu_to_le16(desc->idProduct);
+ desc->bcdDevice = libusb_cpu_to_le16(desc->bcdDevice);
+ }
return 0;
}
@@ -461,17 +464,19 @@ API_EXPORTED int libusb_get_active_config_descriptor(libusb_device *dev,
struct libusb_config_descriptor *_config = malloc(sizeof(*_config));
unsigned char tmp[8];
unsigned char *buf = NULL;
+ int host_endian = 0;
int r;
usbi_dbg("");
if (!_config)
return LIBUSB_ERROR_NO_MEM;
- r = usbi_backend->get_active_config_descriptor(dev, tmp, sizeof(tmp));
+ r = usbi_backend->get_active_config_descriptor(dev, tmp, sizeof(tmp),
+ &host_endian);
if (r < 0)
goto err;
- usbi_parse_descriptor(tmp, "bbw", _config);
+ usbi_parse_descriptor(tmp, "bbw", _config, host_endian);
buf = malloc(_config->wTotalLength);
if (!buf) {
r = LIBUSB_ERROR_NO_MEM;
@@ -479,11 +484,11 @@ API_EXPORTED int libusb_get_active_config_descriptor(libusb_device *dev,
}
r = usbi_backend->get_active_config_descriptor(dev, buf,
- _config->wTotalLength);
+ _config->wTotalLength, &host_endian);
if (r < 0)
goto err;
- r = parse_configuration(_config, buf);
+ r = parse_configuration(_config, buf, host_endian);
if (r < 0) {
usbi_err("parse_configuration failed with error %d", r);
goto err;
@@ -523,6 +528,7 @@ API_EXPORTED int libusb_get_config_descriptor(libusb_device *dev,
struct libusb_config_descriptor *_config;
unsigned char tmp[8];
unsigned char *buf = NULL;
+ int host_endian = 0;
int r;
usbi_dbg("index %d", config_index);
@@ -534,11 +540,11 @@ API_EXPORTED int libusb_get_config_descriptor(libusb_device *dev,
return LIBUSB_ERROR_NO_MEM;
r = usbi_backend->get_config_descriptor(dev, config_index, tmp,
- sizeof(tmp));
+ sizeof(tmp), &host_endian);
if (r < 0)
goto err;
- usbi_parse_descriptor(tmp, "bbw", _config);
+ usbi_parse_descriptor(tmp, "bbw", _config, host_endian);
buf = malloc(_config->wTotalLength);
if (!buf) {
r = LIBUSB_ERROR_NO_MEM;
@@ -546,11 +552,11 @@ API_EXPORTED int libusb_get_config_descriptor(libusb_device *dev,
}
r = usbi_backend->get_config_descriptor(dev, config_index, buf,
- _config->wTotalLength);
+ _config->wTotalLength, &host_endian);
if (r < 0)
goto err;
- r = parse_configuration(_config, buf);
+ r = parse_configuration(_config, buf, host_endian);
if (r < 0) {
usbi_err("parse_configuration failed with error %d", r);
goto err;
@@ -581,7 +587,9 @@ int usbi_get_config_index_by_value(struct libusb_device *dev,
usbi_dbg("value %d", bConfigurationValue);
for (i = 0; i < dev->num_configurations; i++) {
unsigned char tmp[6];
- int r = usbi_backend->get_config_descriptor(dev, i, tmp, sizeof(tmp));
+ int host_endian;
+ int r = usbi_backend->get_config_descriptor(dev, i, tmp, sizeof(tmp),
+ &host_endian);
if (r < 0)
return r;
if (tmp[5] == bConfigurationValue) {
diff --git a/libusb/libusbi.h b/libusb/libusbi.h
index 015d489..41c8097 100644
--- a/libusb/libusbi.h
+++ b/libusb/libusbi.h
@@ -217,7 +217,8 @@ void usbi_handle_transfer_completion(struct usbi_transfer *itransfer,
enum libusb_transfer_status status);
void usbi_handle_transfer_cancellation(struct usbi_transfer *transfer);
-int usbi_parse_descriptor(unsigned char *source, char *descriptor, void *dest);
+int usbi_parse_descriptor(unsigned char *source, char *descriptor, void *dest,
+ int host_endian);
int usbi_get_config_index_by_value(struct libusb_device *dev,
uint8_t bConfigurationValue, int *idx);
@@ -378,10 +379,14 @@ struct usbi_os_backend {
* it to the list of discovered devices, and also when the user requests
* to read the device descriptor.
*
+ * This function is expected to return the descriptor in bus-endian format
+ * (LE). If it returns the multi-byte values in host-endian format,
+ * set the host_endian output parameter to "1".
+ *
* Return 0 on success or a LIBUSB_ERROR code on failure.
*/
int (*get_device_descriptor)(struct libusb_device *device,
- unsigned char *buffer);
+ unsigned char *buffer, int *host_endian);
/* Get the ACTIVE configuration descriptor for a device.
*
@@ -394,13 +399,17 @@ struct usbi_os_backend {
* is guaranteed to be big enough. If you can only do a partial write,
* return an error code.
*
+ * This function is expected to return the descriptor in bus-endian format
+ * (LE). If it returns the multi-byte values in host-endian format,
+ * set the host_endian output parameter to "1".
+ *
* Return:
* - 0 on success
* - LIBUSB_ERROR_NOT_FOUND if the device is in unconfigured state
* - another LIBUSB_ERROR code on other failure
*/
int (*get_active_config_descriptor)(struct libusb_device *device,
- unsigned char *buffer, size_t len);
+ unsigned char *buffer, size_t len, int *host_endian);
/* Get a specific configuration descriptor for a device.
*
@@ -417,10 +426,15 @@ struct usbi_os_backend {
* is guaranteed to be big enough. If you can only do a partial write,
* return an error code.
*
+ * This function is expected to return the descriptor in bus-endian format
+ * (LE). If it returns the multi-byte values in host-endian format,
+ * set the host_endian output parameter to "1".
+ *
* Return 0 on success or a LIBUSB_ERROR code on failure.
*/
int (*get_config_descriptor)(struct libusb_device *device,
- uint8_t config_index, unsigned char *buffer, size_t len);
+ uint8_t config_index, unsigned char *buffer, size_t len,
+ int *host_endian);
/* Set the active configuration for a device.
*
diff --git a/libusb/os/linux_usbfs.c b/libusb/os/linux_usbfs.c
index 1ed9ad8..82f1ac8 100644
--- a/libusb/os/linux_usbfs.c
+++ b/libusb/os/linux_usbfs.c
@@ -239,12 +239,14 @@ static int sysfs_get_device_descriptor(struct libusb_device *dev,
}
static int op_get_device_descriptor(struct libusb_device *dev,
- unsigned char *buffer)
+ unsigned char *buffer, int *host_endian)
{
- if (sysfs_has_descriptors)
+ if (sysfs_has_descriptors) {
return sysfs_get_device_descriptor(dev, buffer);
- else
+ } else {
+ *host_endian = 1;
return usbfs_get_device_descriptor(dev, buffer);
+ }
}
static int usbfs_get_active_config_descriptor(struct libusb_device *dev,
@@ -297,12 +299,14 @@ static int sysfs_get_active_config_descriptor(struct libusb_device *dev,
}
static int op_get_active_config_descriptor(struct libusb_device *dev,
- unsigned char *buffer, size_t len)
+ unsigned char *buffer, size_t len, int *host_endian)
{
- if (sysfs_has_descriptors)
+ if (sysfs_has_descriptors) {
return sysfs_get_active_config_descriptor(dev, buffer, len);
- else
+ } else {
+ *host_endian = 1;
return usbfs_get_active_config_descriptor(dev, buffer, len);
+ }
}
@@ -337,7 +341,7 @@ static int get_config_descriptor(int fd, uint8_t config_index,
return LIBUSB_ERROR_IO;
}
- usbi_parse_descriptor(tmp, "bbwbb", &config);
+ usbi_parse_descriptor(tmp, "bbwbb", &config, 1);
/* seek forward to end of config */
off = lseek(fd, config.wTotalLength - sizeof(tmp), SEEK_CUR);
@@ -363,7 +367,7 @@ static int get_config_descriptor(int fd, uint8_t config_index,
}
static int op_get_config_descriptor(struct libusb_device *dev,
- uint8_t config_index, unsigned char *buffer, size_t len)
+ uint8_t config_index, unsigned char *buffer, size_t len, int *host_endian)
{
char filename[PATH_MAX + 1];
int fd;
@@ -381,11 +385,13 @@ static int op_get_config_descriptor(struct libusb_device *dev,
r = get_config_descriptor(fd, config_index, buffer, len);
close(fd);
+ *host_endian = 1;
return r;
}
/* cache the active config descriptor in memory. a value of -1 means that
- * we aren't sure which one is active, so just assume the first one. */
+ * we aren't sure which one is active, so just assume the first one.
+ * only for usbfs. */
static int cache_active_config(struct libusb_device *dev, int fd,
int active_config)
{
@@ -412,7 +418,7 @@ static int cache_active_config(struct libusb_device *dev, int fd,
return r;
}
- usbi_parse_descriptor(tmp, "bbw", &config);
+ usbi_parse_descriptor(tmp, "bbw", &config, 1);
buf = malloc(config.wTotalLength);
if (!buf)
return LIBUSB_ERROR_NO_MEM;