Skip to content

Instantly share code, notes, and snippets.

@gvanem
Last active February 23, 2018 12:28
Show Gist options
  • Select an option

  • Save gvanem/7b2baf09b4f301d762c8668cdba6c499 to your computer and use it in GitHub Desktop.

Select an option

Save gvanem/7b2baf09b4f301d762c8668cdba6c499 to your computer and use it in GitHub Desktop.
GNU makefile for libusb using MSVC, MinGW or clang-cl (32-bit only). With an added "example/usb_enum.c" to print information on all USB-devices (optionally using HID-API from https://github.com/signal11/hidapi)
#
# GNU Makefile for libusb (MinGW + MSVC + clang-cl).
#
# By G. Vanem <[email protected]> 2013.
#
DATE := $(shell date +%d-%B-%Y)
VPATH = libusb libusb/os examples
THIS_FILE = Makefile.Windows
#
# Choose your weapons:
#
# Set 'USE_DEBUG=0' -> '-MD', RELEASE-model.
# Set 'USE_DEBUG=1' -> '-MDd', DEBUG-model.
#
# Set 'USE_DLL_FOR_TEST=1' to use 'libusb-1.0.dll' in $(PROGRAMS).
# Otherwise use static lib 'libusb-1.0.lib'.
#
# The 'usb_enum.exe' example requires Python (v2.7 or 3.x) to generate the
# 'examples/usb_vendors.c' file. Set 'HAVE_PYTHON = 0' if you
# don't have Python2/3 installed.
#
USE_DEBUG = 0
HAVE_PYTHON = 3
USE_DLL_FOR_TEST = 0
USE_MANIFEST = 0
#
# Set 'USE_HID_API=1' to try getting VID/PID info from Hid-API
# in the example/usb_enum.c program.
#
# But this API seems a bit buggy. Try for yourself:
# https://github.com/signal11/hidapi
#
HID_API_ROOT = ../HID-api
USE_HID_API = 1
define USAGE
Usage: "$(MAKE) -f $(THIS_FILE) CC=[cl | clang-cl | gcc] [all | clean | vclean | realclean | depend | install]"
Specify CC=cl - use MSVC
Specify CC=gcc - use MinGW
endef
WARNING = This file is generated at $(DATE) from by $(realpath $(THIS_FILE)).
#
# Where to copy 'libusb-1.0.dll' + lib + headers to.
# Note: 'make install' is not in default targets. Do it explicitly.
#
VC_ROOT = $(realpath $(VCINSTALLDIR))
MINGW_ROOT = $(realpath $(MINGW32))
#
# Define $(CC)-specific variables.
# First MSVC's cl:
#
ifeq ($(CC),cl)
INSTALL_ROOT = $(VC_ROOT)
STAT_LIB = libusb-1.0.lib
IMP_LIB = libusb-1.0_imp.lib
OBJ_DIR = MSVC_obj
O = obj
CFLAGS = -nologo -W3 -Zi -EHsc -D_CRT_SECURE_NO_WARNINGS
LDFLAGS = -nologo -incremental:no -map -debug -verbose
ifeq ($(USE_DEBUG),1)
CFLAGS += -MDd -Ot -GF -GS -RTCs -RTCu -RTCc
else
CFLAGS += -MD -O2
endif
#
# clang-cl:
#
else ifeq ($(CC),clang-cl)
export CL=
INSTALL_ROOT = $(VC_ROOT)
STAT_LIB = libusb-1.0.lib
IMP_LIB = libusb-1.0_imp.lib
OBJ_DIR = clang_obj
O = obj
CFLAGS = -nologo -W3 -Zi -EHsc -D_CRT_SECURE_NO_WARNINGS
LDFLAGS = -nologo -incremental:no -map -debug -verbose
ifeq ($(USE_DEBUG),1)
CFLAGS += -MDd -Ot -GF -GS -RTCs -RTCu -RTCc
else
CFLAGS += -MD -O2
endif
#
# MinGW:
#
else ifeq ($(CC),gcc)
INSTALL_ROOT = $(MINGW_ROOT)
STAT_LIB = libusb-1.0.a
IMP_LIB = libusb-1.0.dll.a
OBJ_DIR = MinGW_obj
O = o
CFLAGS = -Wall -m32
LDFLAGS = -m32 -Wl,--print-map,--sort-common,--cref
ifeq ($(USE_DEBUG),1)
CFLAGS += -O0 -ggdb
else
CFLAGS += -O2 -fomit-frame-pointer
LDFLAGS += -s
endif
else
$(error $(USAGE))
endif
TARGETS = $(STAT_LIB) $(IMP_LIB) libusb-1.0.dll
#
# This produces debug traces all the time w/o user application having to call
# 'libusb_set_debug()'. We don't want that. It's easier to simply set the
# env-var 'LIBUSB_DEBUG=n'. Where 'n' is a number 0-100.
#
# CFLAGS += -DENABLE_DEBUG_LOGGING
#
# This will trace using OutputDebugString() instead of 'stderr' when
# libusb_set_debug() is called.
#
# CFLAGS += -DUSE_SYSTEM_LOGGING_FACILITY
#
# Enable lots of debug in poll_windows.c.
# Also needs 'ENABLE_LOGGING=1'
#
# CFLAGS += -DDEBUG_POLL_WINDOWS
ifeq ($(USE_DLL_FOR_TEST),1)
TEST_LIB = $(IMP_LIB)
else
TEST_LIB = $(STAT_LIB)
endif
CFLAGS += -I. -I./libusb -DHAVE_CONFIG_H -D_UNICODE -DUNICODE
ifeq ($(CC),gcc)
GETOPT_C =
else
CFLAGS += -I./examples/getopt
GETOPT_C = examples/getopt/getopt.c
endif
#
# To avoid warnings like:
# libusb/os/windows_nt_common.c(420,8) :
# warning: 'GetVersionExA' is deprecated
# if (!GetVersionExA((OSVERSIONINFOA *)&vi))
# ^
CFLAGS += -DBUILD_WINDOWS
#
# To avoid warnings like:
# ./libusb/libusb.h(2033,5) :
# warning: stdcall calling convention ignored on variadic function [-Wignored-attributes]
#
# examples/usb_vendors.c(14695,29) :
# warning: illegal character encoding in string literal [-Winvalid-source-encoding]
# { 0x0ccd005c, "Cinergy T<B2>" },
# ^~~~
#
ifeq ($(CC),clang-cl)
CFLAGS += -Wno-ignored-attributes -Wno-invalid-source-encoding
else ifeq ($(CC),gcc)
CFLAGS += -Wno-pointer-sign
endif
ifneq ($(CC),gcc)
ifeq ($(USE_HID_API),1)
CFLAGS += -I$(HID_API_ROOT)/hidapi -DUSE_HID_API
EX_LIBS = setupapi.lib
endif
EX_LIBS += advapi32.lib user32.lib
else
EX_LIBS += -ladvapi32
#
# No need to add this (gcc already does it for us):
# EX_LIBS += -luser32
#
endif
LIBUSB_SRC = $(addprefix libusb/, \
core.c \
descriptor.c \
hotplug.c \
io.c \
strerror.c \
sync.c \
$(addprefix os/, \
poll_windows.c \
threads_windows.c \
windows_nt_common.c \
windows_winusb.c \
windows_usbdk.c ) )
LIBUSB_OBJ = $(addprefix $(OBJ_DIR)/, $(notdir $(LIBUSB_SRC:.c=.$(O))))
PROGRAM_SRC = $(addprefix examples/, \
ezusb.c fxload.c hotplugtest.c listdevs.c sam3u_benchmark.c \
testlibusb.c usb_enum.c xusb.c)
PROGRAMS = fxload.exe listdevs.exe hotplugtest.exe testlibusb.exe stress.exe xusb.exe
ifneq ($(HAVE_PYTHON),0)
PROGRAMS += usb_enum.exe
endif
TARGETS += $(PROGRAMS)
define CONFIG_H
/*
* $(WARNING)
* DO NOT EDIT.
*/
/* Default visibility
*/
#define DEFAULT_VISIBILITY /**/
/* Enable global message logging
*/
#define ENABLE_LOGGING 1
/* type of second poll() argument
*/
#define POLL_NFDS_TYPE unsigned int
#define OS_WINDOWS 1
#define HAVE_SYS_TYPES_H 1
#define HAVE_STRING_H 1
#if defined(__MINGW32__)
#define HAVE_SYS_TIME_H 1
#elif defined(__clang__)
#define HAVE_STRUCT_TIMESPEC 1 /* An assumption */
#define _TIMESPEC_DEFINED 1
#elif defined(_MSC_VER) && (_MSC_VER >= 1900)
#define HAVE_STRUCT_TIMESPEC 1
#define _TIMESPEC_DEFINED 1
#endif
endef
all: $(OBJ_DIR) config.h $(TARGETS)
@echo 'Welcome to libusb ($$(CC)=$(CC)).'
@echo 'Do a "make -f $(THIS_FILE) install" at own risk!'
$(OBJ_DIR):
- mkdir $(OBJ_DIR)
config.h: $(THIS_FILE)
$(info Creating $@)
$(file > $@,$(CONFIG_H))
install: libusb/libusb.h $(TARGETS)
cp --update libusb/libusb.h $(INSTALL_ROOT)/include
cp --update libusb-1.0.dll $(INSTALL_ROOT)/bin
cp --update $(STAT_LIB) $(IMP_LIB) $(INSTALL_ROOT)/lib
@echo
$(IMP_LIB): libusb-1.0.dll
ifeq ($(CC),gcc)
libusb-1.0.dll: $(LIBUSB_OBJ) $(OBJ_DIR)/libusb-1.0.res libusb/libusb-1.0.def
$(CC) $(LDFLAGS) -shared -o libusb-1.0.dll -Wl,--enable-stdcall-fixup,--out-implib,$(IMP_LIB) \
$^ > libusb-1.0.map
@echo
$(STAT_LIB): $(LIBUSB_OBJ)
ar rs $@ $^
@echo
else
libusb-1.0.dll: $(LIBUSB_OBJ) $(OBJ_DIR)/libusb-1.0.res libusb/libusb-1.0.def
link $(LDFLAGS) -dll -out:$@ -implib:$(IMP_LIB) -def:libusb/libusb-1.0.def \
$(LIBUSB_OBJ) $(OBJ_DIR)/libusb-1.0.res $(EX_LIBS) > link.tmp
@cat libusb-1.0.map >> link.tmp
@rm -f libusb-1.0_imp.exp
@echo
$(STAT_LIB): $(LIBUSB_OBJ)
lib -nologo -out:$@ $^
@echo
endif
$(OBJ_DIR)/%.obj: %.c
$(CC) $(CFLAGS) -Fo$@ -c $<
@echo
$(OBJ_DIR)/%.o: %.c
$(CC) $(CFLAGS) -o $@ -c $<
@echo
$(OBJ_DIR)/%.res: libusb/%.rc
$(call make_res, $<, $@)
#
# .c -> .exe macro
# arg1, $(1): The .exe file.
# arg2, $(2): The .c file(s)
#
# The 'manifest mess' doesn't work.
#
ifeq ($(CC),gcc)
define link_EXE
$(CC) -o $(1) $(CFLAGS) $(LDFLAGS) $(2) $(TEST_LIB) $(EX_LIBS) > $(1:.exe=.map)
@echo
endef
define make_res
windres -O COFF --target=pe-i386 -o $(2) -i $(1)
@echo
endef
else
ifeq ($(USE_MANIFEST),1)
MAN_ARG = -manifestfile:$(strip $(1)).intermediate.manifest \
-manifestuac:"level='asInvoker' uiAccess='false'"
MAN_CMD = mt -nologo -verbose -outputresource:$(strip $(1)) \
-manifest $(strip $(1)).intermediate.manifest ; \
rm -f $(strip $(1)).intermediate.manifest
else
MAN_ARG =
MAN_CMD =
endif
define link_EXE
$(CC) -c $(CFLAGS) -Fo$(OBJ_DIR)\\ $(2)
link $(LDFLAGS) -out:$(strip $(1)) $(addprefix $(OBJ_DIR)/, $(notdir $(2:.c=.obj))) \
$(MAN_ARG) $(TEST_LIB) $(EX_LIBS) > link.tmp
$(call MAN_CMD, $(1))
@cat link.tmp >> $(1:.exe=.map)
@rm -f $(1:.exe=.exp) $(1:.exe=.lib) link.tmp
@echo
endef
define make_res
rc -nologo -Fo $(2) $(1)
@echo
endef
endif
fxload.exe: examples/fxload.c examples/ezusb.c $(GETOPT_C) $(TEST_LIB)
$(call link_EXE, $@, examples/fxload.c examples/ezusb.c $(GETOPT_C))
hotplugtest.exe: examples/hotplugtest.c $(TEST_LIB)
$(call link_EXE, $@, examples/hotplugtest.c)
listdevs.exe: examples/listdevs.c $(TEST_LIB)
$(call link_EXE, $@, examples/listdevs.c)
testlibusb.exe: examples/testlibusb.c $(TEST_LIB)
$(call link_EXE, $@, examples/testlibusb.c)
stress.exe: tests/stress.c tests/testlib.c $(TEST_LIB)
$(call link_EXE, $@, tests/stress.c tests/testlib.c)
usb_enum.exe: examples/usb_enum.c examples/usb_vendors.c $(GETOPT_C) $(TEST_LIB)
$(call link_EXE, $@, examples/usb_enum.c examples/usb_vendors.c $(GETOPT_C))
xusb.exe: examples/xusb.c $(TEST_LIB)
$(call link_EXE, $@, examples/xusb.c)
#
# Use the Python launcher; 'py -2' or 'py -3':
# 'py.exe' gets installed into '%WINDIR' by default
# (I think. So this should be on PATH).
#
examples/usb_vendors.c: examples/make-usb.py $(THIS_FILE)
echo '/* $(WARNING) */' > $@
py -$(HAVE_PYTHON) $< >> $@
clean:
rm -f $(OBJ_DIR)/* $(PROGRAMS:.exe=.pdb) $(PROGRAMS:.exe=.map) \
libusb-1.0.map libusb-1.0.pdb link.tmp
@echo
vclean realclean: clean
rm -f $(TARGETS) .depend.Windows examples/usb_vendors.c
- rmdir $(OBJ_DIR)
#
# Use gcc to generated the dependencies. Strip off the MSVC specific args.
#
REPLACE = sed -e 's/\(.*\)\.o: /\n$$(OBJ_DIR)\/\1.$$(O): /'
DEP_CFLAGS = -MM $(filter -D% -I%, $(CFLAGS))
depend: config.h
@echo 'Generating dependencies. Please wait....'
@ (echo -e \
'#\n' \
'# $(WARNING)\n' \
'# DO NOT EDIT!.\n' \
'#\n' ) > .depend.Windows
gcc $(DEP_CFLAGS) $(LIBUSB_SRC) $(PROGRAM_SRC) | $(REPLACE) >> .depend.Windows
ifneq (,$(findstring depend,$(MAKECMDGOALS)))
$(warning Not reading .depend.Windows)
else
-include .depend.Windows
endif
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <malloc.h>
#include <getopt.h>
#include "libusb.h"
#include "usb_enum.h"
#if defined(_MSC_VER)
#define putenv _putenv
#if (_MSC_VER < 1900) && !defined(__clang__)
#define snprintf _snprintf
#endif
#endif
#if defined(USE_HID_API)
#undef _DEBUG
#define _DEBUG
#include "../windows/hid.c" /* Relative to '$(HID_API_ROOT)/hidapi' */
void print_hid_info (const char *indent, uint16_t vid, uint16_t pid);
#endif
/*
* At high verbose-level, we must flush both stdout and stderr
* to get a synced output.
*/
static int flush_each_line = 0;
#define FLUSH() do { \
if (flush_each_line) { \
fflush (stdout); \
fflush (stderr); \
} \
} while (0)
void Fatal (const char *func, int err)
{
FLUSH();
if (err)
fprintf (stderr, "%s(): %s\n", func, libusb_strerror(err));
else fprintf (stderr, "%s\n", func);
exit (1);
}
/*
* The generated arrays 'usb_vendors_vals[]' and 'usb_products_vals[]' are
* sorted on 'val'. So we break the loops below early when there can be
* no match.
*/
const char *find_vendor_name (uint16_t vendor)
{
const struct value_string *list = usb_vendors_vals;
for ( ; list->string && vendor >= list->val; list++)
{
if (list->val == vendor)
return (list->string);
}
return (NULL);
}
const char *find_product_name (uint16_t vendor, uint16_t product)
{
const struct value_string *list = usb_products_vals;
uint32_t val = (vendor << 16) + product;
for ( ; list->string && vendor >= (list->val >> 16); list++)
{
if (list->val == val)
return (list->string);
}
return (NULL);
}
int device_compare (const struct libusb_device_descriptor *d1,
const struct libusb_device_descriptor *d2)
{
if (d1->idVendor != d2->idVendor)
return (int) (d1->idVendor - d2->idVendor);
return (int) (d1->idProduct - d2->idProduct);
}
int sort_list (struct libusb_device_descriptor *dd, size_t num)
{
typedef int (*CmpFunc) (const void *, const void *);
qsort (dd, num, sizeof(*dd), (CmpFunc)device_compare);
return (num);
}
void print_libusb_details (const char *indent,
libusb_context *ctx,
const struct libusb_device_descriptor *dd)
{
libusb_device_handle *handle;
libusb_device *dev;
uint8_t bus, port_path[8];
int i, r;
char string[128];
uint8_t string_index[3]; /* indexes of the string descriptors */
const char *speed_name[5] = { "Unknown",
"1.5 Mbit/s (USB LowSpeed)",
"12 Mbit/s (USB FullSpeed)",
"480 Mbit/s (USB HighSpeed)",
"5000 Mbit/s (USB SuperSpeed)"
};
uint16_t vid = dd->idVendor;
uint16_t pid = dd->idProduct;
printf ("%sOpening device %04X:%04X...%c", indent, vid, pid, flush_each_line ? '\n' :' ');
FLUSH();
handle = libusb_open_device_with_vid_pid (ctx, vid, pid);
FLUSH();
if (handle == NULL)
{
puts (" Failed.\n");
return;
}
dev = libusb_get_device (handle);
bus = libusb_get_bus_number (dev);
r = libusb_get_port_numbers (dev, port_path, sizeof(port_path));
if (r > 0)
{
FLUSH();
printf ("\n%sDevice properties:\n", indent);
printf ("%s bus number: %d\n", indent, bus);
printf ("%s port path: %d", indent, port_path[0]);
for (i = 1; i < r; i++)
printf ("->%d", port_path[i]);
printf (" (from root hub)\n");
}
else if (r < 0)
{
printf ("\n%sError:%s\n", indent, libusb_strerror((enum libusb_error)r));
}
FLUSH();
r = libusb_get_device_speed (dev);
FLUSH();
if (r < 0 || r > 4)
r = 0;
printf ("%s speed: %s\n", indent, speed_name[r]);
printf ("\n%sReading string descriptors:\n", indent);
for (i = 0; i < 3; i++)
{
if (string_index[i] == 0)
continue;
FLUSH();
if (libusb_get_string_descriptor_ascii(handle, string_index[i], (unsigned char*)string, sizeof(string)) >= 0)
{
FLUSH();
printf ("%s String (0x%02X): \"%s\"\n", indent, string_index[i], string);
}
}
FLUSH();
libusb_close (handle);
puts ("");
FLUSH();
}
void print_libusb_descriptor (const char *indent, const struct libusb_device_descriptor *dd)
{
printf ("%sDescr-type:%02Xh, BCD:%02Xh, Class:%02Xh, Sub-class:%02Xh, Proto:%02Xh\n",
indent,
dd->bDescriptorType, dd->bcdUSB, dd->bDeviceClass,
dd->bDeviceSubClass, dd->bDeviceProtocol);
FLUSH();
}
void print_vendor_prod_names (int index, int vendor, int product, uint8_t bus, uint8_t addr)
{
const char *vendor_name, *product_name;
FLUSH();
printf ("%2d: %04X %04X %02X:%02X ",
index, vendor, product, bus, addr);
vendor_name = find_vendor_name (vendor);
product_name = find_product_name (vendor, product);
printf ("%-30s %s\n",
vendor_name ? vendor_name : "<not found>",
product_name ? product_name : "<not found>");
FLUSH();
}
const char *heading = " # Vendor Product BUS:Addr Vendor name "
"Product name\n"
"------------------------------------------------------"
"---------------------------------------------------------";
#if 0
struct hid_device_info {
/** Platform-specific device path */
char *path;
/** Device Vendor ID */
unsigned short vendor_id;
/** Device Product ID */
unsigned short product_id;
/** Serial Number */
wchar_t *serial_number;
/** Device Release Number in binary-coded decimal,
also known as Device Version Number */
unsigned short release_number;
/** Manufacturer String */
wchar_t *manufacturer_string;
/** Product string */
wchar_t *product_string;
/** Usage Page for this Device/Interface
(Windows/Mac only). */
unsigned short usage_page;
/** Usage for this Device/Interface
(Windows/Mac only).*/
unsigned short usage;
/** The USB interface which this logical device
represents. Valid on both Linux implementations
in all cases, and valid on the Windows implementation
only if the device contains more than one interface. */
int interface_number;
/** Pointer to the next device */
struct hid_device_info *next;
};
#endif
void list_devices_using_hidapi (int verbose)
{
#if !defined(USE_HID_API)
Fatal ("'USE_HID_API' not compiled into program.\n", 0);
#else
const struct hid_device_info *hid_info = hid_enumerate (0, 0);
const struct hid_device_info *cur;
int i;
if (!hid_info)
{
printf ("hid_enumerate() failed\n");
FLUSH();
}
/* to-do: sort this linked list on VID/PID like in sort_list().
*/
puts (heading);
for (i = 0, cur = hid_info; cur; cur = cur->next, i++)
{
print_vendor_prod_names (i, cur->vendor_id, cur->product_id, 0, 0);
if (verbose >= 1)
print_hid_info (" ", cur->vendor_id, cur->product_id);
#if 0
if (verbose >= 2)
{
print_hid_info ("path: %s, S/N: %s", cur->path, cur->serial_number);
}
#endif
}
hid_free_enumeration ((struct hid_device_info*)hid_info);
hid_exit();
#endif
}
void list_devices_using_libusb (int verbose)
{
libusb_context *ctx;
libusb_device **list;
struct libusb_device_descriptor *dd_list;
int i, j, rc, count;
rc = libusb_init (&ctx);
if (rc != 0)
Fatal ("libusb_init", rc);
libusb_set_option (NULL, LIBUSB_OPTION_LOG_LEVEL, verbose);
rc = libusb_get_device_list (ctx, &list);
if (rc < 0)
{
libusb_exit (ctx);
Fatal ("libusb_get_device_list", rc);
}
count = rc;
dd_list = alloca (count * sizeof(*dd_list));
for (i = j = 0; i < count; i++)
{
if (libusb_get_device_descriptor(list[i], dd_list+j) != 0)
printf ("libusb_get_device_descriptor (list[%d]) failed!!\n", i);
else j++;
FLUSH();
}
count = sort_list (dd_list, j);
puts (heading);
FLUSH();
for (i = 0; i < count; i++)
{
struct libusb_device_descriptor *dd = dd_list + i;
int bus = libusb_get_bus_number ((libusb_device*)dd);
int addr = libusb_get_device_address ((libusb_device*)dd);
print_vendor_prod_names (i, dd->idVendor, dd->idProduct, bus, addr);
if (verbose >= 2)
print_libusb_descriptor (" ", dd);
if (verbose >= 3)
print_libusb_details (" ", ctx, dd);
}
libusb_free_device_list (list, 1);
libusb_exit (ctx);
}
void list_devices (int verbose, BOOL use_HID_API)
{
if (verbose >= 4)
{
putenv ("LIBUSB_DEBUG=4");
flush_each_line = 1;
}
if (use_HID_API)
list_devices_using_hidapi (verbose);
else list_devices_using_libusb (verbose);
}
void show_help (void)
{
puts ("usb_enum Usage:\n"
" -v: Increase verbose mode. E.g. use '-vvv' for level 3 verbosity.\n"
" -H: Check only HID [1] devices using HID-API [2]\n"
" Otherwise use libusb [3]");
puts (" Footnotes:\n"
" [1] https://en.wikipedia.org/wiki/USB_human_interface_device_class\n"
" [2] https://github.com/signal11/hidapi\n"
" [3] https://github.com/libusb/libusb");
exit (0);
}
int main (int argc, char **argv)
{
int opt, verbose = 0, use_hid_api = 0;
while ((opt = getopt(argc, argv, "vhH")) != EOF)
{
if (opt == 'v')
verbose++;
else if (opt == 'h')
show_help();
else if (opt == 'H')
use_hid_api = 1;
}
if (usb_ids_date)
printf ("Using USB VID/PIDs obtained from http://www.linux-usb.org/usb.ids at %s.\n\n",
usb_ids_date);
list_devices (verbose, use_hid_api);
return (0);
}
#if defined(USE_HID_API)
/*
* Adapted from:
* https://raw.githubusercontent.com/signal11/hidapi/master/hidtest/hidtest.cpp
*
* But HidAPI seems buggy...
*/
#define MAX_STR 255
#define PRINTF(...) do { \
fputs (indent, stdout); \
printf (__VA_ARGS__); \
FLUSH(); \
} while (0)
void print_hid_info (const char *indent, uint16_t vid, uint16_t pid)
{
int res, i;
uint8_t buf [256];
wchar_t wstr [MAX_STR];
hid_device *handle;
printf ("%sInfo from HidAPI:\n", indent);
/* Open the device using the VID, PID,
* and optionally the Serial number.
*/
handle = hid_open (vid, pid, NULL);
if (!handle)
{
PRINTF ("Unable to open device. VID:%04X PID:%04X\n\n", vid, pid);
return;
}
/* Read the Manufacturer String.
*/
wstr[0] = 0x0000;
res = hid_get_manufacturer_string (handle, wstr, MAX_STR);
if (res < 0)
{
PRINTF ("Unable to read manufacturer string: %ls\n", hid_error(handle));
goto quit;
}
PRINTF ("Manufacturer String: %ls\n", wstr);
/* Read the Product String.
*/
wstr[0] = 0x0000;
res = hid_get_product_string (handle, wstr, MAX_STR);
if (res < 0)
{
PRINTF ("Unable to read product string\n: %ls\n", hid_error(handle));
goto quit;
}
PRINTF ("Product String: %ls\n", wstr);
/* Read the Serial Number String.
*/
wstr[0] = 0x0000;
res = hid_get_serial_number_string (handle, wstr, MAX_STR);
if (res < 0)
{
PRINTF ("Unable to read serial number string: %ls\n", hid_error(handle));
goto quit;
}
PRINTF ("Serial Number String: (%d) %ls\n", wstr[0], wstr);
/* Read Indexed String 1.
*/
wstr[0] = 0x0000;
res = hid_get_indexed_string(handle, 1, wstr, MAX_STR);
if (res < 0)
{
PRINTF ("Unable to read indexed string 1: %ls\n", hid_error(handle));
goto quit;
}
PRINTF ("Indexed String 1: %ls\n", wstr);
/* Set the hid_read() function to be non-blocking.
*/
hid_set_nonblocking (handle, 1);
/* Try to read from the device. There should be no
* data here, but execution should not block.
*/
res = hid_read (handle, buf, 17);
/* Send a Feature Report to the device.
*/
buf[0] = 0x2;
buf[1] = 0xA0;
buf[2] = 0x0A;
buf[3] = 0x00;
buf[4] = 0x00;
res = hid_send_feature_report (handle, buf, 17);
if (res < 0)
{
PRINTF ("Unable to send a feature report: %ls\n", hid_error(handle));
goto quit;
}
memset (buf, 0, sizeof(buf));
/* Read a Feature Report from the device.
*/
buf[0] = 0x2;
res = hid_get_feature_report (handle, buf, sizeof(buf));
if (res < 0)
{
PRINTF ("Unable to get a feature report.\n%ls\n", hid_error(handle));
goto quit;
}
/* Print out the returned buffer.
*/
PRINTF ("Feature Report\n ");
for (i = 0; i < res; i++)
printf ("%02hhx ", buf[i]);
printf ("\n");
memset (buf,0,sizeof(buf));
/* Toggle LED (cmd 0x80). The first byte is the report number (0x1).
*/
buf[0] = 0x1;
buf[1] = 0x80;
res = hid_write(handle, buf, 17);
if (res < 0)
{
PRINTF ("Unable to write(): %ls\n", hid_error(handle));
goto quit;
}
/* Request state (cmd 0x81). The first byte is the report number (0x1).
*/
buf[0] = 0x1;
buf[1] = 0x81;
hid_write (handle, buf, 17);
if (res < 0)
{
PRINTF ("Unable to write(): %ls\n", hid_error(handle));
goto quit;
}
/* Read requested state. hid_read() has been set to be
* non-blocking by the call to hid_set_nonblocking() above.
* This loop demonstrates the non-blocking nature of hid_read().
*/
res = 0;
while (res == 0)
{
res = hid_read (handle, buf, sizeof(buf));
if (res == 0)
PRINTF ("waiting...\n");
if (res < 0)
printf ("Unable to read()\n");
Sleep (500);
}
PRINTF ("Data read:\n ");
/* Print out the returned buffer.
*/
for (i = 0; i < res; i++)
PRINTF ("%02hhx ", buf[i]);
printf ("\n");
quit:
hid_close (handle);
}
#endif
#ifndef _USB_ENUM_H
#define _USB_ENUM_H
struct value_string {
unsigned long val;
const char *string;
};
/* These arrays are generated by the examples/make-usb.py script.
*/
extern const struct value_string usb_vendors_vals[];
extern const struct value_string usb_products_vals[];
extern unsigned num_usb_products;
extern unsigned num_usb_vendors;
extern const char *usb_ids_date;
#endif
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment