summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorNikolai Kondrashov <spbnick@gmail.com>2010-11-17 20:14:41 +0300
committerNikolai Kondrashov <spbnick@gmail.com>2010-11-17 20:14:41 +0300
commitfbb23a3cb06b0bc528c4cf9345646f7e82756949 (patch)
tree0cea3519f28ed0f2fec9bd33319d8e8debe03f69
parent07fbdb8ba0c5bdc85697bbc0588a610c9e4dad18 (diff)
downloadusbhid-dump-fbb23a3cb06b0bc528c4cf9345646f7e82756949.tar.gz
Add multi-device dumping with limiting
Add multi-device dumping. Add independent limiting by bus number, device address, vendor, product and interface number.
-rw-r--r--include/uhd/Makefile.am2
-rw-r--r--include/uhd/dev.h76
-rw-r--r--include/uhd/dev_list.h108
-rw-r--r--include/uhd/iface.h14
-rw-r--r--include/uhd/iface_list.h20
-rw-r--r--lib/Makefile.am2
-rw-r--r--lib/dev.c89
-rw-r--r--lib/dev_list.c151
-rw-r--r--lib/iface.c46
-rw-r--r--lib/iface_list.c115
-rw-r--r--src/usbhid-dump.c538
11 files changed, 901 insertions, 260 deletions
diff --git a/include/uhd/Makefile.am b/include/uhd/Makefile.am
index 7a84851..71cdc11 100644
--- a/include/uhd/Makefile.am
+++ b/include/uhd/Makefile.am
@@ -17,6 +17,8 @@
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
noinst_HEADERS = \
+ dev.h \
+ dev_list.h \
iface.h \
iface_list.h \
libusb.h \
diff --git a/include/uhd/dev.h b/include/uhd/dev.h
new file mode 100644
index 0000000..21775db
--- /dev/null
+++ b/include/uhd/dev.h
@@ -0,0 +1,76 @@
+/** @file
+ * @brief usbhid-dump - device
+ *
+ * Copyright (C) 2010 Nikolai Kondrashov
+ *
+ * This file is part of usbhid-dump.
+ *
+ * Usbhid-dump 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.
+ *
+ * Usbhid-dump 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 usbhid-dump; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * @author Nikolai Kondrashov <spbnick@gmail.com>
+ *
+ * @(#) $Id$
+ */
+
+#ifndef __UHD_DEV_H__
+#define __UHD_DEV_H__
+
+#include <stdbool.h>
+#include <libusb-1.0/libusb.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/** usbhid-dump device */
+typedef struct uhd_dev uhd_dev;
+
+struct uhd_dev {
+ uhd_dev *next; /**< Next device in the list */
+ libusb_device_handle *handle; /**< Handle */
+};
+
+/**
+ * Check if a device is valid.
+ *
+ * @param dev Device to check.
+ *
+ * @return True if the device is valid, false otherwise.
+ */
+extern bool uhd_dev_valid(const uhd_dev *dev);
+
+/**
+ * Open a device.
+ *
+ * @param lusb_dev Libusb device.
+ * @param pdev Location for the opened device pointer.
+ *
+ * @return Libusb error code.
+ */
+extern enum libusb_error uhd_dev_open(libusb_device *lusb_dev,
+ uhd_dev **pdev);
+
+/**
+ * Close a device.
+ *
+ * @param dev The device to close.
+ */
+extern void uhd_dev_close(uhd_dev *dev);
+
+#ifdef __cplusplus
+} /* extern "C" */
+#endif
+
+#endif /* __UHD_DEV_H__ */
diff --git a/include/uhd/dev_list.h b/include/uhd/dev_list.h
new file mode 100644
index 0000000..d75c613
--- /dev/null
+++ b/include/uhd/dev_list.h
@@ -0,0 +1,108 @@
+/** @file
+ * @brief usbhid-dump - device list
+ *
+ * Copyright (C) 2010 Nikolai Kondrashov
+ *
+ * This file is part of usbhid-dump.
+ *
+ * Usbhid-dump 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.
+ *
+ * Usbhid-dump 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 usbhid-dump; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * @author Nikolai Kondrashov <spbnick@gmail.com>
+ *
+ * @(#) $Id$
+ */
+
+#ifndef __UHD_DEV_LIST_H__
+#define __UHD_DEV_LIST_H__
+
+#include "uhd/dev.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * Check if a device list is valid.
+ *
+ * @param list Device list to check.
+ *
+ * @return True if the device list is valid, false otherwise.
+ */
+extern bool uhd_dev_list_valid(const uhd_dev *list);
+
+/**
+ * Check if a device list is empty.
+ *
+ * @param list Device list to check.
+ *
+ * @return True if the device list is empty, false otherwise.
+ */
+static inline bool
+uhd_dev_list_empty(const uhd_dev *list)
+{
+ return list == NULL;
+}
+
+/**
+ * Calculate length of a device list.
+ *
+ * @param list The list to calculate length of.
+ *
+ * @return The list length.
+ */
+extern size_t uhd_dev_list_len(const uhd_dev *list);
+
+/**
+ * Close every device in a device list.
+ *
+ * @param list The device list to close.
+ */
+extern void uhd_dev_list_close(uhd_dev *list);
+
+/**
+ * Iterate over a device list.
+ *
+ * @param _dev Loop device variable.
+ * @param _list Device list to iterate over.
+ */
+#define UHD_DEV_LIST_FOR_EACH(_dev, _list) \
+ for (_dev = _list; _dev != NULL; _dev = _dev->next)
+
+/**
+ * Open a list of devices optionally matching bus number/device address and
+ * vendor/product IDs.
+ *
+ * @param ctx Libusb context.
+ * @param bus_num Bus number, or 0 for any bus.
+ * @param dev_addr Device address, or 0 for any address.
+ * @param vendor Vendor ID, or 0 for any vendor.
+ * @param product Product ID, or 0 for any product.
+ * @param plist Location for the resulting device list head; could be
+ * NULL.
+ *
+ * @return Libusb error code.
+ */
+extern enum libusb_error uhd_dev_list_open(libusb_context *ctx,
+ uint8_t bus_num,
+ uint8_t dev_addr,
+ uint16_t vendor,
+ uint16_t product,
+ uhd_dev **plist);
+
+#ifdef __cplusplus
+} /* extern "C" */
+#endif
+
+#endif /* __UHD_DEV_LIST_H__ */
diff --git a/include/uhd/iface.h b/include/uhd/iface.h
index b87e6db..d6bd67d 100644
--- a/include/uhd/iface.h
+++ b/include/uhd/iface.h
@@ -27,8 +27,7 @@
#ifndef __UHD_IFACE_H__
#define __UHD_IFACE_H__
-#include <stdbool.h>
-#include <libusb-1.0/libusb.h>
+#include "uhd/dev.h"
#ifdef __cplusplus
extern "C" {
@@ -39,8 +38,9 @@ typedef struct uhd_iface uhd_iface;
struct uhd_iface {
uhd_iface *next;
- libusb_device_handle *handle; /**< Device handle */
+ const uhd_dev *dev; /**< Device */
uint8_t number; /**< Interface number */
+ char addr_str[12]; /**< Address string */
uint8_t int_in_ep_addr; /**< Interrupt IN EP address */
uint16_t int_in_ep_maxp; /**< Interrupt IN EP maximum
packet size */
@@ -79,10 +79,10 @@ extern bool uhd_iface_valid(const uhd_iface *iface);
*
* @return New interface or NULL, if failed to allocate.
*/
-extern uhd_iface *uhd_iface_new(libusb_device_handle *handle,
- uint8_t number,
- uint8_t int_in_ep_addr,
- uint16_t int_in_ep_maxp);
+extern uhd_iface *uhd_iface_new(const uhd_dev *dev,
+ uint8_t number,
+ uint8_t int_in_ep_addr,
+ uint16_t int_in_ep_maxp);
/**
* Free an interface.
diff --git a/include/uhd/iface_list.h b/include/uhd/iface_list.h
index c93a3da..17ad778 100644
--- a/include/uhd/iface_list.h
+++ b/include/uhd/iface_list.h
@@ -27,6 +27,7 @@
#ifndef __UHD_IFACE_LIST_H__
#define __UHD_IFACE_LIST_H__
+#include "uhd/dev_list.h"
#include "uhd/iface.h"
#ifdef __cplusplus
@@ -81,29 +82,26 @@ extern void uhd_iface_list_free(uhd_iface *list);
for (_iface = _list; _iface != NULL; _iface = _iface->next)
/**
- * Fetch a list of HID interfaces from a device.
+ * Fetch a list of HID interfaces from a device list.
*
- * @param handle The device handle to fetch interface list from.
+ * @param dev_list The device list to fetch interface list from.
* @param plist Location for the resulting list head; could be NULL.
*
* @return Libusb error code.
*/
-enum libusb_error
-uhd_iface_list_new_from_dev(libusb_device_handle *handle,
- uhd_iface **plist);
+extern enum libusb_error uhd_iface_list_new(uhd_dev *dev_list,
+ uhd_iface **plist);
/**
- * Filter an interface list by an optional interface number, resulting
- * either in an empty, a single-interface, or an unmodified list.
+ * Filter an interface list by an interface number.
*
* @param plist The original list head.
- * @param number The interface number to match against, or a negative
- * integer meaning there is no restriction.
+ * @param number The interface number to match against.
*
- * @return The resulting list head
+ * @return The resulting list head.
*/
extern uhd_iface *uhd_iface_list_fltr_by_num(uhd_iface *list,
- int number);
+ uint8_t number);
#ifdef __cplusplus
} /* extern "C" */
diff --git a/lib/Makefile.am b/lib/Makefile.am
index 5422448..b9ce040 100644
--- a/lib/Makefile.am
+++ b/lib/Makefile.am
@@ -19,6 +19,8 @@
noinst_LTLIBRARIES = libuhd.la
libuhd_la_SOURCES = \
+ dev.c \
+ dev_list.c \
iface.c \
iface_list.c \
libusb.c \
diff --git a/lib/dev.c b/lib/dev.c
new file mode 100644
index 0000000..2e0a487
--- /dev/null
+++ b/lib/dev.c
@@ -0,0 +1,89 @@
+/** @file
+ * @brief usbhid-dump - device
+ *
+ * Copyright (C) 2010 Nikolai Kondrashov
+ *
+ * This file is part of usbhid-dump.
+ *
+ * Usbhid-dump 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.
+ *
+ * Usbhid-dump 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 usbhid-dump; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * @author Nikolai Kondrashov <spbnick@gmail.com>
+ *
+ * @(#) $Id$
+ */
+
+#include "uhd/dev.h"
+#include <assert.h>
+#include <stdlib.h>
+
+bool
+uhd_dev_valid(const uhd_dev *dev)
+{
+ return dev != NULL &&
+ dev->handle != NULL;
+}
+
+
+enum libusb_error
+uhd_dev_open(libusb_device *lusb_dev,
+ uhd_dev **pdev)
+{
+ enum libusb_error err;
+ uhd_dev *dev;
+
+ assert(lusb_dev != NULL);
+
+ dev = malloc(sizeof(*dev));
+ if (dev == NULL)
+ return LIBUSB_ERROR_NO_MEM;
+
+ dev->next = NULL;
+
+ err = libusb_open(lusb_dev, &dev->handle);
+ if (err != LIBUSB_SUCCESS)
+ {
+ free(dev);
+ return err;
+ }
+
+ assert(uhd_dev_valid(dev));
+
+ if (pdev != NULL)
+ *pdev = dev;
+ else
+ {
+ libusb_close(dev->handle);
+ free(dev);
+ }
+
+ return LIBUSB_SUCCESS;
+}
+
+
+void
+uhd_dev_close(uhd_dev *dev)
+{
+ if (dev == NULL)
+ return;
+
+ assert(uhd_dev_valid(dev));
+
+ libusb_close(dev->handle);
+ dev->handle = NULL;
+
+ free(dev);
+}
+
+
diff --git a/lib/dev_list.c b/lib/dev_list.c
new file mode 100644
index 0000000..3318b4f
--- /dev/null
+++ b/lib/dev_list.c
@@ -0,0 +1,151 @@
+/** @file
+ * @brief usbhid-dump - device list
+ *
+ * Copyright (C) 2010 Nikolai Kondrashov
+ *
+ * This file is part of usbhid-dump.
+ *
+ * Usbhid-dump 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.
+ *
+ * Usbhid-dump 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 usbhid-dump; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * @author Nikolai Kondrashov <spbnick@gmail.com>
+ *
+ * @(#) $Id$
+ */
+
+#include "uhd/dev_list.h"
+#include <assert.h>
+#include <stdlib.h>
+#include <stdio.h>
+
+bool
+uhd_dev_list_valid(const uhd_dev *list)
+{
+ UHD_DEV_LIST_FOR_EACH(list, list)
+ if (!uhd_dev_valid(list))
+ return false;
+
+ return true;
+}
+
+
+size_t
+uhd_dev_list_len(const uhd_dev *list)
+{
+ size_t len = 0;
+
+ UHD_DEV_LIST_FOR_EACH(list, list)
+ len++;
+
+ return len;
+}
+
+
+void
+uhd_dev_list_close(uhd_dev *list)
+{
+ uhd_dev *next;
+
+ for (; list != NULL; list = next)
+ {
+ next = list->next;
+ uhd_dev_close(list);
+ }
+}
+
+
+enum libusb_error
+uhd_dev_list_open(libusb_context *ctx,
+ uint8_t bus_num, uint8_t dev_addr,
+ uint16_t vendor, uint16_t product,
+ uhd_dev **plist)
+{
+ enum libusb_error err = LIBUSB_ERROR_OTHER;
+ libusb_device **lusb_list = NULL;
+ ssize_t num;
+ ssize_t idx;
+ libusb_device *lusb_dev;
+ struct libusb_device_descriptor desc;
+ uhd_dev *list = NULL;
+ uhd_dev *dev;
+
+ assert(ctx != NULL);
+
+ /* Retrieve libusb device list */
+ num = libusb_get_device_list(ctx, &lusb_list);
+ if (num == LIBUSB_ERROR_NO_MEM)
+ {
+ err = num;
+ goto cleanup;
+ }
+
+ /* Find and open the devices */
+ for (idx = 0; idx < num; idx++)
+ {
+ lusb_dev = lusb_list[idx];
+
+ /* Skip devices not matching bus_num/dev_addr mask */
+ if ((bus_num != 0 && libusb_get_bus_number(lusb_dev) != bus_num) ||
+ (dev_addr != 0 && libusb_get_device_address(lusb_dev) != dev_addr))
+ continue;
+
+ /* Skip devices not matching vendor/product mask */
+ if (vendor != 0 || product != 0)
+ {
+ err = libusb_get_device_descriptor(lusb_dev, &desc);
+ if (err != LIBUSB_SUCCESS)
+ goto cleanup;
+
+ if ((vendor != 0 && vendor != desc.idVendor) ||
+ (product != 0 && product != desc.idProduct))
+ continue;
+ }
+
+ /* Open and append the device to the list */
+ err = uhd_dev_open(lusb_dev, &dev);
+ if (err != LIBUSB_SUCCESS)
+ goto cleanup;
+
+ dev->next = list;
+ list = dev;
+ }
+
+ /* Free the libusb device list freeing unused devices */
+ libusb_free_device_list(lusb_list, true);
+ lusb_list = NULL;
+
+ /* Output device list, if requested */
+ assert(uhd_dev_list_valid(list));
+ if (plist != NULL)
+ {
+ *plist = list;
+ list = NULL;
+ }
+
+ /* Done! */
+ err = LIBUSB_SUCCESS;
+
+cleanup:
+
+ /* Close the device list if not output */
+ uhd_dev_list_close(list);
+
+ /* Free the libusb device list along with devices */
+ if (lusb_list != NULL)
+ libusb_free_device_list(lusb_list, true);
+
+ return err;
+}
+
+
diff --git a/lib/iface.c b/lib/iface.c
index 445d902..e84516e 100644
--- a/lib/iface.c
+++ b/lib/iface.c
@@ -28,29 +28,33 @@
#include <assert.h>
#include <stdlib.h>
#include <stdio.h>
+#include <string.h>
bool
uhd_iface_valid(const uhd_iface *iface)
{
return iface != NULL &&
- iface->handle != NULL &&
- iface->number < UINT8_MAX;
+ uhd_dev_valid(iface->dev) &&
+ iface->number < UINT8_MAX &&
+ strlen(iface->addr_str) == (sizeof(iface->addr_str) - 1);
}
uhd_iface *
-uhd_iface_new(libusb_device_handle *handle,
- uint8_t number,
- uint8_t int_in_ep_addr,
- uint16_t int_in_ep_maxp)
+uhd_iface_new(const uhd_dev *dev,
+ uint8_t number,
+ uint8_t int_in_ep_addr,
+ uint16_t int_in_ep_maxp)
{
- uhd_iface *iface;
+ uhd_iface *iface;
+ libusb_device *lusb_dev;
+ int rc;
iface = malloc(sizeof(*iface));
if (iface == NULL)
return NULL;
iface->next = NULL;
- iface->handle = handle;
+ iface->dev = dev;
iface->number = number;
iface->int_in_ep_addr = int_in_ep_addr;
iface->int_in_ep_maxp = int_in_ep_maxp;
@@ -58,6 +62,17 @@ uhd_iface_new(libusb_device_handle *handle,
iface->claimed = false;
iface->submitted = false;
+ /* Format address string */
+ lusb_dev = libusb_get_device(dev->handle);
+ rc = snprintf(iface->addr_str, sizeof(iface->addr_str),
+ "%.3hhu:%.3hhu:%.3hhu",
+ libusb_get_bus_number(lusb_dev),
+ libusb_get_device_address(lusb_dev),
+ number);
+ assert(rc == (sizeof(iface->addr_str) - 1));
+
+ assert(uhd_iface_valid(iface));
+
return iface;
}
@@ -81,7 +96,7 @@ uhd_iface_detach(uhd_iface *iface)
assert(uhd_iface_valid(iface));
- err = libusb_detach_kernel_driver(iface->handle, iface->number);
+ err = libusb_detach_kernel_driver(iface->dev->handle, iface->number);
if (err == LIBUSB_SUCCESS)
iface->detached = true;
else if (err != LIBUSB_ERROR_NOT_FOUND)
@@ -100,7 +115,8 @@ uhd_iface_attach(uhd_iface *iface)
if (iface->detached)
{
- err = libusb_attach_kernel_driver(iface->handle, iface->number);
+ err = libusb_attach_kernel_driver(iface->dev->handle,
+ iface->number);
if (err != LIBUSB_SUCCESS)
return err;
iface->detached = false;
@@ -117,7 +133,7 @@ uhd_iface_claim(uhd_iface *iface)
assert(uhd_iface_valid(iface));
- err = libusb_claim_interface(iface->handle, iface->number);
+ err = libusb_claim_interface(iface->dev->handle, iface->number);
if (err != LIBUSB_SUCCESS)
return err;
@@ -134,7 +150,7 @@ uhd_iface_release(uhd_iface *iface)
assert(uhd_iface_valid(iface));
- err = libusb_release_interface(iface->handle, iface->number);
+ err = libusb_release_interface(iface->dev->handle, iface->number);
if (err != LIBUSB_SUCCESS)
return err;
@@ -151,7 +167,7 @@ uhd_iface_clear_halt(uhd_iface *iface)
assert(uhd_iface_valid(iface));
- err = libusb_clear_halt(iface->handle, iface->int_in_ep_addr);
+ err = libusb_clear_halt(iface->dev->handle, iface->int_in_ep_addr);
if (err != LIBUSB_SUCCESS)
return err;
@@ -168,7 +184,7 @@ uhd_iface_set_idle(const uhd_iface *iface,
assert(uhd_iface_valid(iface));
- rc = libusb_control_transfer(iface->handle,
+ rc = libusb_control_transfer(iface->dev->handle,
/* host->device, class, interface */
0x21,
/* Set_Idle */
@@ -200,7 +216,7 @@ uhd_iface_set_protocol(const uhd_iface *iface,
assert(uhd_iface_valid(iface));
- rc = libusb_control_transfer(iface->handle,
+ rc = libusb_control_transfer(iface->dev->handle,
/* host->device, class, interface */
0x21,
/* Set_Protocol */
diff --git a/lib/iface_list.c b/lib/iface_list.c
index d54dc3f..6ac30bd 100644
--- a/lib/iface_list.c
+++ b/lib/iface_list.c
@@ -66,89 +66,105 @@ uhd_iface_list_free(uhd_iface *list)
enum libusb_error
-uhd_iface_list_new_from_dev(libusb_device_handle *handle,
- uhd_iface **plist)
+uhd_iface_list_new(uhd_dev *dev_list,
+ uhd_iface **plist)
{
- enum libusb_error err = LIBUSB_ERROR_OTHER;
+ enum libusb_error err = LIBUSB_ERROR_OTHER;
+ uhd_dev *dev;
struct libusb_config_descriptor *config = NULL;
- const struct libusb_interface *libusb_iface;
+ const struct libusb_interface *lusb_iface;
const struct libusb_endpoint_descriptor *ep_list;
uint8_t ep_num;
const struct libusb_endpoint_descriptor *ep;
uhd_iface *list = NULL;
- uhd_iface *last = NULL;
uhd_iface *iface;
- assert(handle != NULL);
+ assert(uhd_dev_list_valid(dev_list));
- /* Retrieve active configuration descriptor */
- err = libusb_get_active_config_descriptor(libusb_get_device(handle),
- &config);
- if (err != LIBUSB_SUCCESS)
- goto cleanup;
-
- /* Build the matching interface list */
- for (libusb_iface = config->interface;
- libusb_iface - config->interface < config->bNumInterfaces;
- libusb_iface++)
+ UHD_DEV_LIST_FOR_EACH(dev, dev_list)
{
- if (libusb_iface->num_altsetting != 1 ||
- libusb_iface->altsetting->bInterfaceClass != LIBUSB_CLASS_HID)
- continue;
-
- ep_list = libusb_iface->altsetting->endpoint;
- ep_num = libusb_iface->altsetting->bNumEndpoints;
-
- for (ep = ep_list; (ep - ep_list) < ep_num; ep++)
+ /* Retrieve active configuration descriptor */
+ err = libusb_get_active_config_descriptor(libusb_get_device(dev->handle),
+ &config);
+ if (err != LIBUSB_SUCCESS)
+ goto cleanup;
+
+ /*
+ * Build the matching interface list
+ */
+
+ /* For each interface */
+ for (lusb_iface = config->interface;
+ lusb_iface - config->interface < config->bNumInterfaces;
+ lusb_iface++)
{
- if ((ep->bmAttributes & LIBUSB_TRANSFER_TYPE_MASK) !=
- LIBUSB_TRANSFER_TYPE_INTERRUPT ||
- (ep->bEndpointAddress & LIBUSB_ENDPOINT_DIR_MASK) !=
- LIBUSB_ENDPOINT_IN)
+ /* Skip interfaces with altsettings and non-HID interfaces */
+ if (lusb_iface->num_altsetting != 1 ||
+ lusb_iface->altsetting->bInterfaceClass != LIBUSB_CLASS_HID)
continue;
- iface = uhd_iface_new(
- handle,
- libusb_iface->altsetting->bInterfaceNumber,
- ep->bEndpointAddress, ep->wMaxPacketSize);
- if (iface == NULL)
- {
- err = LIBUSB_ERROR_NO_MEM;
- goto cleanup;
- }
+ /* Retrieve endpoint list */
+ ep_list = lusb_iface->altsetting->endpoint;
+ ep_num = lusb_iface->altsetting->bNumEndpoints;
- if (last == NULL)
+ /* For each endpoint */
+ for (ep = ep_list; (ep - ep_list) < ep_num; ep++)
+ {
+ /* Skip non-interrupt and non-in endpoints */
+ if ((ep->bmAttributes & LIBUSB_TRANSFER_TYPE_MASK) !=
+ LIBUSB_TRANSFER_TYPE_INTERRUPT ||
+ (ep->bEndpointAddress & LIBUSB_ENDPOINT_DIR_MASK) !=
+ LIBUSB_ENDPOINT_IN)
+ continue;
+
+ /* Create the interface */
+ iface = uhd_iface_new(
+ dev,
+ lusb_iface->altsetting->bInterfaceNumber,
+ ep->bEndpointAddress, ep->wMaxPacketSize);
+ if (iface == NULL)
+ {
+ err = LIBUSB_ERROR_NO_MEM;
+ goto cleanup;
+ }
+
+ /* Add the interface */
+ iface->next = list;
list = iface;
- else
- last->next = iface;
- last = iface;
- break;
+ break;
+ }
}
+
+ /* Free the config descriptor */
+ libusb_free_config_descriptor(config);
+ config = NULL;
}
/* Output the resulting list, if requested */
+ assert(uhd_iface_list_valid(list));
if (plist != NULL)
{
*plist = list;
list = NULL;
}
+ /* Done! */
+ err = LIBUSB_SUCCESS;
+
cleanup:
+ libusb_free_config_descriptor(config);
uhd_iface_list_free(list);
- if (config != NULL)
- libusb_free_config_descriptor(config);
-
return err;
}
uhd_iface *
uhd_iface_list_fltr_by_num(uhd_iface *list,
- int number)
+ uint8_t number)
{
uhd_iface *prev;
uhd_iface *iface;
@@ -157,13 +173,10 @@ uhd_iface_list_fltr_by_num(uhd_iface *list,
assert(uhd_iface_list_valid(list));
assert(number < UINT8_MAX);
- if (number < 0)
- return list;
-
for (prev = NULL, iface = list; iface != NULL; iface = next)
{
next = iface->next;
- if (iface->number == (uint8_t)number)
+ if (iface->number == number)
prev = iface;
else
{
@@ -171,7 +184,7 @@ uhd_iface_list_fltr_by_num(uhd_iface *list,
list = next;
else
prev->next = next;
- free(iface);
+ uhd_iface_free(iface);
}
}
diff --git a/src/usbhid-dump.c b/src/usbhid-dump.c
index 466c874..101ad67 100644
--- a/src/usbhid-dump.c
+++ b/src/usbhid-dump.c
@@ -1,6 +1,5 @@
/** @file
- * @brief usbhid-dump - entry point
- *
+ * @brief usbhid-dump - entry point *
* Copyright (C) 2010 Nikolai Kondrashov
*
* This file is part of usbhid-dump.
@@ -49,6 +48,17 @@
*/
#define MAX_DESCRIPTOR_SIZE 4096
+/** Wildcard bus number */
+#define BUS_NUM_ANY 0
+/** Wildcard device address */
+#define DEV_ADDR_ANY 0
+/** Wildcard vendor ID */
+#define VID_ANY 0
+/** Wildcard product ID */
+#define PID_ANY 0
+/** Wildcard interface number */
+#define IFACE_NUM_ANY UINT8_MAX
+
/**
* USB I/O timeout
*/
@@ -122,61 +132,11 @@ stream_resume_sighandler(int signum)
/**< "Stream feedback" flag - non-zero if feedback is enabled */
static volatile sig_atomic_t stream_feedback = 0;
-static bool
-usage(FILE *stream, const char *progname)
-{
- return
- fprintf(
- stream,
-"Usage: %s [OPTION]... <bus> <dev> [if]\n"
-"Dump a USB device HID report descriptor(s) and/or stream(s)."
-"\n"
-"Arguments:\n"
-" bus bus number (1-255)\n"
-" dev device address (1-255)\n"
-" if interface number (0-254);\n"
-" if omitted, all device HID interfaces\n"
-" are dumped\n"
-"\n"
-"Options:\n"
-" -h, --help output this help message and exit\n"
-" -v, --version output version information and exit\n"
-" -e, --entity=STRING what to dump: either \"descriptor\",\n"
-" \"stream\" or \"all\"; value can be\n"
-" abbreviated\n"
-" -p, --stream-paused start with the stream dump output paused\n"
-" -f, --stream-feedback enable stream dumping feedback: for every\n"
-" transfer dumped a dot is printed to stderr\n"
-"\n"
-"Default options: --entity=descriptor\n"
-"\n"
-"Signals:\n"
-" USR1/USR2 pause/resume the stream dump output\n"
-"\n",
- progname) >= 0;
-}
-
-
-static bool
-version(FILE *stream)
-{
- return
- fprintf(
- stream,
-PACKAGE_STRING "\n"
-"Copyright (C) 2010 Nikolai Kondrashov\n"
-"License GPLv2+: GNU GPL version 2 or later <http://gnu.org/licenses/gpl.html>.\n"
-"\n"
-"This is free software: you are free to change and redistribute it.\n"
-"There is NO WARRANTY, to the extent permitted by law.\n") >= 0;
-}
-
-
static void
-dump(uint8_t iface_num,
- const char *entity,
- const uint8_t *ptr,
- size_t len)
+dump(const uhd_iface *iface,
+ const char *entity,
+ const uint8_t *ptr,
+ size_t len)
{
static const char xd[] = "0123456789ABCDEF";
static char buf[] = " XX\n";
@@ -186,8 +146,8 @@ dump(uint8_t iface_num,
gettimeofday(&tv, NULL);
- fprintf(stdout, "%.3hhu:%-16s %20llu.%.6u\n",
- iface_num, entity,
+ fprintf(stdout, "%s:%-16s %12llu.%.6u\n",
+ iface->addr_str, entity,
(unsigned long long int)tv.tv_sec,
(unsigned int)tv.tv_usec);
@@ -218,7 +178,7 @@ dump_iface_list_descriptor(const uhd_iface *list)
UHD_IFACE_LIST_FOR_EACH(iface, list)
{
- rc = libusb_control_transfer(iface->handle,
+ rc = libusb_control_transfer(iface->dev->handle,
/* See HID spec, 7.1.1 */
0x81,
LIBUSB_REQUEST_GET_DESCRIPTOR,
@@ -231,7 +191,7 @@ dump_iface_list_descriptor(const uhd_iface *list)
"report descriptor", iface->number);
return false;
}
- dump(iface->number, "DESCRIPTOR", buf, rc);
+ dump(iface, "DESCRIPTOR", buf, rc);
}
return true;
@@ -258,7 +218,7 @@ dump_iface_list_stream_cb(struct libusb_transfer *transfer)
/* Dump the result */
if (!stream_paused)
{
- dump(iface->number, "STREAM",
+ dump(iface, "STREAM",
transfer->buffer, transfer->actual_length);
if (stream_feedback)
fputc('.', stderr);
@@ -347,13 +307,13 @@ dump_iface_list_stream(libusb_context *ctx, uhd_iface *list)
const size_t len = iface->int_in_ep_maxp;
/* Allocate the transfer buffer */
- buf = malloc(len);
+ buf = malloc(len);
if (len > 0 && buf == NULL)
FAILURE_CLEANUP("allocate a transfer buffer");
/* Initialize the transfer */
libusb_fill_interrupt_transfer(*ptransfer,
- iface->handle, iface->int_in_ep_addr,
+ iface->dev->handle, iface->int_in_ep_addr,
buf, len,
dump_iface_list_stream_cb,
(void *)iface,
@@ -488,39 +448,41 @@ cleanup:
static int
-run(bool dump_descriptor,
- bool dump_stream,
- uint8_t bus_num,
- uint8_t dev_addr,
- int iface_num)
+run(bool dump_descriptor,
+ bool dump_stream,
+ uint8_t bus_num,
+ uint8_t dev_addr,
+ uint16_t vid,
+ uint16_t pid,
+ int iface_num)
{
- int result = 1;
- enum libusb_error err;
- libusb_context *ctx = NULL;
- libusb_device_handle *handle = NULL;
- uhd_iface *iface_list = NULL;
- uhd_iface *iface;
-
- /* Initialize libusb context */
- err = libusb_init(&ctx);
- if (err != LIBUSB_SUCCESS)
- LIBUSB_FAILURE_CLEANUP("create libusb context");
+ int result = 1;
+ enum libusb_error err;
+ libusb_context *ctx = NULL;
+ uhd_dev *dev_list = NULL;
+ uhd_iface *iface_list = NULL;
+ uhd_iface *iface;
+
+ /* Create libusb context */
+ LIBUSB_GUARD(libusb_init(&ctx), "create libusb context");
/* Set libusb debug level */
libusb_set_debug(ctx, 3);
- /* Find and open the device */
- err = libusb_open_device_with_bus_dev(ctx, bus_num, dev_addr, &handle);
- if (err != LIBUSB_SUCCESS)
- LIBUSB_FAILURE_CLEANUP("find and open the device");
+ /* Open device list */
+ LIBUSB_GUARD(uhd_dev_list_open(ctx, bus_num, dev_addr,
+ vid, pid, &dev_list),
+ "find and open the devices");
- /* Retrieve the list of HID interfaces from a device */
- err = uhd_iface_list_new_from_dev(handle, &iface_list);
- if (err != LIBUSB_SUCCESS)
- LIBUSB_FAILURE_CLEANUP("find a HID interface");
+ /* Retrieve the list of HID interfaces from the device list */
+ LIBUSB_GUARD(uhd_iface_list_new(dev_list, &iface_list),
+ "find HID interfaces");
/* Filter the interface list by specified interface number */
- iface_list = uhd_iface_list_fltr_by_num(iface_list, iface_num);
+ if (iface_num != IFACE_NUM_ANY)
+ iface_list = uhd_iface_list_fltr_by_num(iface_list, iface_num);
+
+ /* Check if there are any interfaces left */
if (uhd_iface_list_empty(iface_list))
ERROR_CLEANUP("No matching HID interfaces");
@@ -553,11 +515,10 @@ cleanup:
/* Free the interface list */
uhd_iface_list_free(iface_list);
- /* Free the device */
- if (handle != NULL)
- libusb_close(handle);
+ /* Close the device list */
+ uhd_dev_list_close(dev_list);
- /* Cleanup libusb context, if necessary */
+ /* Destroy the libusb context */
if (ctx != NULL)
libusb_exit(ctx);
@@ -565,73 +526,309 @@ cleanup:
}
+static bool
+parse_number_pair(const char *str,
+ int base,
+ long *pn1,
+ long *pn2)
+{
+ const char *p;
+ char *end;
+ long n1;
+ long n2;
+
+ assert(str != NULL);
+
+ p = str;
+
+ /* Skip space (prevent strtol doing so) */
+ while (isspace(*p))
+ p++;
+
+ /* Extract the first number */
+ errno = 0;
+ n1 = strtol(p, &end, base);
+ if (errno != 0)
+ return false;
+
+ /* If nothing was read */
+ if (end == p)
+ return false;
+
+ /* Move on */
+ p = end;
+
+ /* Skip space */
+ while (isspace(*p))
+ p++;
+
+ /* If it is the end of string */
+ if (*p == '\0')
+ n2 = 0;
+ else
+ {
+ /* If it is not the number separator */
+ if (*p != ':')
+ return false;
+
+ /* Skip the number separator */
+ p++;
+
+ /* Skip space (prevent strtol doing so) */
+ while (isspace(*p))
+ p++;
+
+ /* Extract the second number */
+ errno = 0;
+ n2 = strtol(p, &end, base);
+ if (errno != 0)
+ return false;
+ /* If nothing was read */
+ if (end == p)
+ return false;
+
+ /* Move on */
+ p = end;
+
+ /* Skip space */
+ while (isspace(*p))
+ p++;
+
+ /* If it is not the end of string */
+ if (*p != '\0')
+ return false;
+ }
+
+ /* Output the numbers */
+ if (pn1 != NULL)
+ *pn1 = n1;
+ if (pn2 != NULL)
+ *pn2 = n2;
+
+ return true;
+}
+
+
+static bool
+parse_address(const char *str,
+ uint8_t *pbus_num,
+ uint8_t *pdev_addr)
+{
+ long bus_num;
+ long dev_addr;
+
+ assert(str != NULL);
+
+ if (!parse_number_pair(str, 10, &bus_num, &dev_addr))
+ return false;
+
+ if (bus_num < 0 || bus_num > UINT8_MAX ||
+ dev_addr < 0 || dev_addr > UINT8_MAX)
+ return false;
+
+ if (pbus_num != NULL)
+ *pbus_num = bus_num;
+ if (pdev_addr != NULL)
+ *pdev_addr = dev_addr;
+
+ return true;
+}
+
+
+static bool
+parse_model(const char *str,
+ uint16_t *pvid,
+ uint16_t *ppid)
+{
+ long vid;
+ long pid;
+
+ assert(str != NULL);
+
+ if (!parse_number_pair(str, 16, &vid, &pid))
+ return false;
+
+ if (vid < 0 || vid > UINT16_MAX ||
+ pid < 0 || pid > UINT16_MAX)
+ return false;
+
+ if (pvid != NULL)
+ *pvid = vid;
+ if (ppid != NULL)
+ *ppid = pid;
+
+ return true;
+}
+
+
+static bool
+parse_iface_num(const char *str,
+ uint8_t *piface_num)
+{
+ long iface_num;
+ const char *p;
+ char *end;
+
+ assert(str != NULL);
+
+ p = str;
+
+ /* Skip space (prevent strtol doing so) */
+ while (isspace(*p))
+ p++;
+
+ /* Extract interface number */
+ errno = 0;
+ iface_num = strtol(p, &end, 10);
+ if (errno != 0 || end == p || iface_num < 0 || iface_num > UINT8_MAX)
+ return false;
+
+ /* Output interface number */
+ if (piface_num != NULL)
+ *piface_num = iface_num;
+
+ return true;
+}
+
+
+static bool
+version(FILE *stream)
+{
+ return
+ fprintf(
+ stream,
+PACKAGE_STRING "\n"
+"Copyright (C) 2010 Nikolai Kondrashov\n"
+"License GPLv2+: GNU GPL version 2 or later <http://gnu.org/licenses/gpl.html>.\n"
+"\n"
+"This is free software: you are free to change and redistribute it.\n"
+"There is NO WARRANTY, to the extent permitted by law.\n") >= 0;
+}
+
+
+static bool
+usage(FILE *stream)
+{
+ return
+ fprintf(
+ stream,
+"Usage: %s [OPTION]...\n"
+"Dump USB device HID report descriptor(s) and/or stream(s).\n"
+"\n"
+"Options:\n"
+" -h, --help output this help message and exit\n"
+" -v, --version output version information and exit\n"
+"\n"
+" -s, -a, --address=bus[:dev] limit interfaces by bus number\n"
+" (1-255) and device address (1-255),\n"
+" decimal; zeroes match any\n"
+" -d, -m, --model=vid[:pid] limit interfaces by vendor and\n"
+" product IDs (0001-ffff), hexadecimal;\n"
+" zeroes match any\n"
+" -i, --interface=NUMBER limit interfaces by number (0-254),\n"
+" decimal; 255 matches any\n"
+"\n"
+" -e, --entity=STRING what to dump: either \"descriptor\",\n"
+" \"stream\" or \"all\"; value can be\n"
+" abbreviated\n"
+"\n"
+" -p, --stream-paused start with the stream dump output\n"
+" paused\n"
+" -f, --stream-feedback enable stream dumping feedback: for\n"
+" every transfer dumped a dot is\n"
+" printed to stderr\n"
+"\n"
+"Default options: --entity=descriptor\n"
+"\n"
+"Signals:\n"
+" USR1/USR2 pause/resume the stream dump output\n"
+"\n",
+ program_invocation_short_name) >= 0;
+}
+
+
typedef enum opt_val {
OPT_VAL_HELP = 'h',
OPT_VAL_VERSION = 'v',
+ OPT_VAL_ADDRESS = 'a',
+ OPT_VAL_ADDRESS_COMP = 's',
+ OPT_VAL_MODEL = 'm',
+ OPT_VAL_MODEL_COMP = 'd',
+ OPT_VAL_INTERFACE = 'i',
OPT_VAL_ENTITY = 'e',
OPT_VAL_STREAM_PAUSED = 'p',
OPT_VAL_STREAM_FEEDBACK = 'f',
} opt_val;
+static const struct option long_opt_list[] = {
+ {.val = OPT_VAL_HELP,
+ .name = "help",
+ .has_arg = no_argument,
+ .flag = NULL},
+ {.val = OPT_VAL_VERSION,
+ .name = "version",
+ .has_arg = no_argument,
+ .flag = NULL},
+ {.val = OPT_VAL_ADDRESS,
+ .name = "address",
+ .has_arg = required_argument,
+ .flag = NULL},
+ {.val = OPT_VAL_MODEL,
+ .name = "model",
+ .has_arg = required_argument,
+ .flag = NULL},
+ {.val = OPT_VAL_INTERFACE,
+ .name = "interface",
+ .has_arg = required_argument,
+ .flag = NULL},
+ {.val = OPT_VAL_ENTITY,
+ .name = "entity",
+ .has_arg = required_argument,
+ .flag = NULL},
+ {.val = OPT_VAL_STREAM_PAUSED,
+ .name = "stream-paused",
+ .has_arg = no_argument,
+ .flag = NULL},
+ {.val = OPT_VAL_STREAM_FEEDBACK,
+ .name = "stream-feedback",
+ .has_arg = no_argument,
+ .flag = NULL},
+ {.val = 0,
+ .name = NULL,
+ .has_arg = 0,
+ .flag = NULL}
+};
+
+
+static const char *short_opt_list = "hvs::a::d::m::i:e:pf";
+
+
int
main(int argc, char **argv)
{
- static const struct option long_opt_list[] = {
- {.val = OPT_VAL_HELP,
- .name = "help",
- .has_arg = no_argument,
- .flag = NULL},
- {.val = OPT_VAL_VERSION,
- .name = "version",
- .has_arg = no_argument,
- .flag = NULL},
- {.val = OPT_VAL_ENTITY,
- .name = "entity",
- .has_arg = required_argument,
- .flag = NULL},
- {.val = OPT_VAL_STREAM_PAUSED,
- .name = "stream-paused",
- .has_arg = no_argument,
- .flag = NULL},
- {.val = OPT_VAL_STREAM_FEEDBACK,
- .name = "stream-feedback",
- .has_arg = no_argument,
- .flag = NULL},
- {.val = 0,
- .name = NULL,
- .has_arg = 0,
- .flag = NULL}
- };
-
- static const char *short_opt_list = "hve:pf";
-
- int result;
-
- char c;
- const char *bus_str;
- const char *dev_str;
- const char *if_str = "";
-
- const char **arg_list[] = {&bus_str, &dev_str, &if_str};
- const size_t req_arg_num = 2;
- const size_t max_arg_num = 3;
- size_t i;
- char *end;
-
- bool dump_descriptor = true;
- bool dump_stream = false;
- long bus_num;
- long dev_num;
- long if_num = -1;
+ int result;
+
+ char c;
+
+ uint8_t bus_num = BUS_NUM_ANY;
+ uint8_t dev_addr = DEV_ADDR_ANY;
+
+ uint16_t vid = VID_ANY;
+ uint16_t pid = PID_ANY;
+
+ uint8_t iface_num = IFACE_NUM_ANY;
+
+ bool dump_descriptor = true;
+ bool dump_stream = false;
struct sigaction sa;
#define USAGE_ERROR(_fmt, _args...) \
- do { \
- fprintf(stderr, _fmt "\n", ##_args); \
- usage(stderr, program_invocation_short_name); \
- return 1; \
+ do { \
+ fprintf(stderr, _fmt "\n", ##_args); \
+ usage(stderr); \
+ return 1; \
} while (0)
/*
@@ -643,13 +840,27 @@ main(int argc, char **argv)
switch (c)
{
case OPT_VAL_HELP:
- usage(stdout, program_invocation_short_name);
+ usage(stdout);
return 0;
break;
case OPT_VAL_VERSION:
version(stdout);
return 0;
break;
+ case OPT_VAL_ADDRESS:
+ case OPT_VAL_ADDRESS_COMP:
+ if (!parse_address(optarg, &bus_num, &dev_addr))
+ USAGE_ERROR("Invalid device address \"%s\"", optarg);
+ break;
+ case OPT_VAL_MODEL:
+ case OPT_VAL_MODEL_COMP:
+ if (!parse_model(optarg, &vid, &pid))
+ USAGE_ERROR("Invalid model \"%s\"", optarg);
+ break;
+ case OPT_VAL_INTERFACE:
+ if (!parse_iface_num(optarg, &iface_num))
+ USAGE_ERROR("Invalid interface number \"%s\"", optarg);
+ break;
case OPT_VAL_ENTITY:
if (strncmp(optarg, "descriptor", strlen(optarg)) == 0)
{
@@ -677,44 +888,17 @@ main(int argc, char **argv)
stream_feedback = 1;
break;
case '?':
- usage(stderr, program_invocation_short_name);
+ usage(stderr);
return 1;
break;
}
}
/*
- * Assign positional parameters
+ * Verify positional arguments
*/
- for (i = 0; i < max_arg_num && optind < argc; i++, optind++)
- *arg_list[i] = argv[optind];
-
- if (i < req_arg_num)
- USAGE_ERROR("Not enough arguments");
-
- /*
- * Parse and verify positional parameters
- */
- errno = 0;
- bus_num = strtol(bus_str, &end, 0);
- if (errno != 0 || !uhd_strisblank(end) ||
- bus_num <= 0 || bus_num > 255)
- USAGE_ERROR("Invalid bus number \"%s\"", bus_str);
-
- errno = 0;
- dev_num = strtol(dev_str, &end, 0);
- if (errno != 0 || !uhd_strisblank(end) ||
- dev_num <= 0 || dev_num > 255)
- USAGE_ERROR("Invalid device address \"%s\"", dev_str);
-
- if (!uhd_strisblank(if_str))
- {
- errno = 0;
- if_num = strtol(if_str, &end, 0);
- if (errno != 0 || !uhd_strisblank(end) ||
- if_num < 0 || if_num >= 255)
- USAGE_ERROR("Invalid interface number \"%s\"", if_str);
- }
+ if (optind < argc)
+ USAGE_ERROR("Positional arguments are not accepted");
/*
* Setup signal handlers
@@ -752,7 +936,9 @@ main(int argc, char **argv)
/* Make stdout buffered - we will flush it explicitly */
setbuf(stdout, NULL);
- result = run(dump_descriptor, dump_stream, bus_num, dev_num, if_num);
+ /* Run! */
+ result = run(dump_descriptor, dump_stream,
+ bus_num, dev_addr, vid, pid, iface_num);
/*
* Restore signal handlers