CMD_UTILITY=hidusb-relay-cmd
-# For Unix/Linux, Mac OS X:
-# Using old simple version of libusb
+# For Unix/Linux :
+# Using old simple version 0.1 of libusb
USBFLAGS= `libusb-config --cflags`
USBLIBS= `libusb-config --libs`
EXE_SUFFIX=
# Use the following 3 lines on Windows and comment out the 3 above:
#USBFLAGS=
-#USBLIBS= -lhid -lusb -lsetupapi
+#USBLIBS= -lhid -lsetupapi
#EXE_SUFFIX= .exe
-#+pa GCC on my ubuntu won't pick this dir by default ?!
+#+pa GCC on my ubuntu 12 won't pick this dir by default ?!
#USBFLAGS+=-I/usr/include
+HIDDATA=hiddata_libusb01
+
CC= gcc
DEBUGFLAGS=
CFLAGS= -O -Wall $(USBFLAGS) $(DEBUGFLAGS)
LIBS= $(USBLIBS)
-OBJ= usbrelay-cmd.o hiddata.o
+OBJ= usbrelay-cmd.o $(HIDDATA).o
PROGRAM= $(CMD_UTILITY)$(EXE_SUFFIX)
all: $(PROGRAM)
--- /dev/null
+/**
+ * File: hiddata-OSX.c
+ * Version for OS/X 10.9+
+ * 05-jan-2014 pa
+ * License: ?
+ */
+
+#include "hiddata.h"
+#include <stdio.h>
+#include <string.h>
+#include <stdint.h>
+#include <errno.h>
+
+// OS X: Using IOKit HID functionality
+#include <IOKit/IOKitLib.h>
+#include <IOKit/hid/IOHIDDevice.h>
+
+
+#if 0 //ifdef DEBUG
+#define DEBUG_PRINT(arg) printf arg
+#else
+#define DEBUG_PRINT(arg)
+#endif
+
+static IOHIDManagerRef g_hidmgr = NULL;
+static int g_hidmgr_open_count = 0;
+
+// Device handle is IOHIDDeviceRef
+static inline IOHIDDeviceRef usbh_to_IOHIDDeviceRef(USBDEVHANDLE usbh)
+{
+ return (IOHIDDeviceRef)(void*)usbh;
+}
+
+
+static void _hidmgrDeref(void)
+{
+ if ( g_hidmgr && (--g_hidmgr_open_count <= 0) ) {
+ IOHIDManagerClose(g_hidmgr, kIOHIDOptionsTypeNone);
+ CFRelease(g_hidmgr);
+ g_hidmgr = NULL;
+ }
+}
+
+
+static int32_t get_int_property(IOHIDDeviceRef device, CFStringRef key)
+{
+ int32_t value = -1;
+
+ CFTypeRef ref = IOHIDDeviceGetProperty(device, key);
+ if (ref) {
+ if (CFGetTypeID(ref) == CFNumberGetTypeID()) {
+ value = 0;
+ CFNumberGetValue((CFNumberRef) ref, kCFNumberSInt32Type, &value);
+ }
+ }
+ return value;
+}
+
+static int get_string_property_utf8(IOHIDDeviceRef device, CFStringRef prop, char *buf, ssize_t len)
+{
+ *buf = 0;
+ if (len < 2)
+ return 0;
+ CFStringRef str = IOHIDDeviceGetProperty(device, prop);
+ if (!str)
+ return USBHID_ERR_BAD_ARG;
+
+ CFIndex str_len = CFStringGetLength(str);
+ CFIndex chars_copied = 0;
+ if (str_len) {
+ CFIndex used_buf_len;
+ len--;
+ chars_copied = CFStringGetBytes(str,
+ CFRangeMake(0, CFStringGetLength(str)),
+ kCFStringEncodingUTF8,
+ '?',
+ FALSE,
+ (void*)buf,
+ len,
+ &used_buf_len);
+ buf[used_buf_len] = 0;
+ }
+ return (int)chars_copied;
+}
+
+
+// Enumerate devices
+int usbhidEnumDevices(int vendor, int product,
+ void *context,
+ int (*usbhidEnumFunc)(USBDEVHANDLE usbh, void *ctx))
+
+{
+ /* Enumerate thru HID devices */
+ IOHIDManagerRef mgr;
+ mgr = IOHIDManagerCreate(kCFAllocatorDefault, kIOHIDOptionsTypeNone);
+ if (!mgr)
+ return USBHID_ERR_IO_HID;
+ // *** TODO: match by vendor/product right here ***
+ IOHIDManagerSetDeviceMatching(mgr, NULL);
+ IOReturn ir = IOHIDManagerOpen(mgr, kIOHIDOptionsTypeNone);
+ if (ir != kIOReturnSuccess)
+ return USBHID_ERR_IO_HID;
+
+ CFSetRef device_set = IOHIDManagerCopyDevices(mgr);
+ CFIndex num_devices = CFSetGetCount(device_set);
+ if (0 == num_devices)
+ return USBHID_ERR_NOTFOUND;
+
+ g_hidmgr = mgr;
+ g_hidmgr_open_count = 1;
+
+ IOHIDDeviceRef *device_array = calloc(num_devices, sizeof(IOHIDDeviceRef));
+ CFSetGetValues(device_set, (const void **) device_array);
+
+ int i;
+ for (i = 0; i < num_devices; i++) {
+ IOHIDDeviceRef dev = device_array[i];
+ char buf[32];
+ uint16_t nvid, npid;
+
+ if (!dev)
+ continue; /* device gone during enumeration? */
+
+ /* Skip non-USB devices */
+ get_string_property_utf8(dev, CFSTR(kIOHIDTransportKey), buf, sizeof(buf));
+ if (0 != strcasecmp(buf, "USB"))
+ continue;
+
+ nvid = (uint16_t)get_int_property(dev, CFSTR(kIOHIDVendorIDKey));
+ npid = (uint16_t)get_int_property(dev, CFSTR(kIOHIDProductIDKey));
+
+ if (nvid != (uint16_t)vendor || npid != (uint16_t)product) {
+ continue;
+ }
+ //printf("Device: %p\n", dev);
+
+ /* the device handle is owned by the callback, it shoud call usbhidCloseDevice() on it */
+ /* No need to open the device only for control pipe io (get/set HID feature) */
+ ++g_hidmgr_open_count;
+ if ( 0 == usbhidEnumFunc((USBDEVHANDLE)dev, context) )
+ {
+ break; /* stop enumeration */
+ }
+
+ }
+
+ free(device_array);
+ _hidmgrDeref();
+ return 0;
+}
+
+/* ------------------------------------------------------------------------- */
+
+void usbhidCloseDevice(USBDEVHANDLE usbh)
+{
+ IOHIDDeviceRef dev = usbh_to_IOHIDDeviceRef(usbh);
+ //printf("close %p\n", dev);
+ if (dev) {
+ _hidmgrDeref();
+ }
+}
+
+/* ------------------------------------------------------------------------- */
+
+int usbhidGetVendorString(USBDEVHANDLE usbh, char *buffer, int len)
+{
+ IOHIDDeviceRef dev = usbh_to_IOHIDDeviceRef(usbh);
+ int len2 = get_string_property_utf8(dev, CFSTR(kIOHIDManufacturerKey), buffer, len);
+ if (len2 < 0) {
+ fprintf(stderr, "Warning: cannot query vendor for device\n");
+ return USBHID_ERR_IO_HID;
+ }
+ return 0;
+}
+
+int usbhidGetProductString(USBDEVHANDLE usbh, char *buffer, int len)
+{
+ IOHIDDeviceRef dev = usbh_to_IOHIDDeviceRef(usbh);
+ int len2 = get_string_property_utf8(dev, CFSTR(kIOHIDProductKey), buffer, len);
+ if (len2 < 0) {
+ fprintf(stderr, "Warning: cannot query product for device\n");
+ return USBHID_ERR_IO_HID;
+ }
+ return 0;
+}
+
+
+static inline bool usesReportIDs(USBDEVHANDLE usbh) {
+ /* true => the device (or underlying HID layer) returns the report ID
+ in the 1st byte of Get report buffer, and expects the id in Set request.
+ The v-usb relay devices do not do this.
+ */
+ return false;
+}
+
+
+int usbhidSetReport(USBDEVHANDLE usbh, char *buffer, int len)
+{
+ IOHIDDeviceRef dev = usbh_to_IOHIDDeviceRef(usbh);
+ char reportNumber = buffer[0];
+
+ if ( !usesReportIDs(usbh) ) {
+ buffer++; /* skip report ID */
+ len--;
+ }
+
+ IOReturn
+ res = IOHIDDeviceSetReport(dev, kIOHIDReportTypeFeature, (CFIndex)reportNumber, (void*)buffer, (CFIndex)len);
+ if (res != kIOReturnSuccess) {
+ fprintf(stderr, "Error sending message: %d\n", (int)res);
+ return USBHID_ERR_IO_HID;
+ }
+ return 0;
+}
+
+/* ------------------------------------------------------------------------- */
+
+int usbhidGetReport(USBDEVHANDLE usbh, int reportNumber, char *buffer, int *len)
+{
+ IOHIDDeviceRef dev = usbh_to_IOHIDDeviceRef(usbh);
+ int maxLen = *len;
+ if (maxLen < 2)
+ return USBHID_ERR_BAD_ARG;
+
+ if (!usesReportIDs(usbh)) {
+ buffer++; /* make room for dummy report ID */
+ maxLen--;
+ }
+
+ CFIndex rb = maxLen;
+ IOReturn
+ res = IOHIDDeviceGetReport(dev, kIOHIDReportTypeFeature, (CFIndex)reportNumber, (void*)buffer, &rb);
+
+ if (res != kIOReturnSuccess || ((unsigned)rb <= 0)) {
+ fprintf(stderr, "Error get report: %d\n", res);
+ return USBHID_ERR_IO_HID;
+ }
+
+ *len = (int)rb;
+
+ if (!usesReportIDs(usbh)) {
+ buffer[-1] = (char)reportNumber; /* add dummy report ID */
+ (*len)++;
+ }
+ return 0;
+}
+
+
+void usbhidSetUsesReportId(USBDEVHANDLE usbh)
+{
+ //TODO Implement if some devices prepend report IDs
+}
+
+
+int usbhidStrerror_r( int err, char *buf, int len)
+{
+ const char *s;
+ switch (err) {
+ case USBHID_ERR_ACCESS: s = "Access to device denied";
+ case USBHID_ERR_NOTFOUND: s = "The specified device was not found";
+ case USBHID_ERR_IO: s = "Communication error with device";
+ case USBHID_ERR_IO_HID: s = "HID I/O error with device";
+ default:
+ s = "";
+ }
+
+ return snprintf(buf, len, "%s", s);
+}
-/* Name: hiddata.c
- * Author: Christian Starkjohann
- * Creation Date: 2008-04-11
- * Tabsize: 4
+/* hiddata_libusb01.c
+ * Variant for libusb v 0.1 (old simple version, found in many PC Linux distros)
+ */
+
+/* Inspired by hiddata.c|h by Christian Starkjohann
* Copyright: (c) 2008 by OBJECTIVE DEVELOPMENT Software GmbH
- * License: GNU GPL v2 (see License.txt), GNU GPL v3 or proprietary (CommercialLicense.txt)
*/
+
#include "hiddata.h"
#include <stdio.h>
#include <string.h>
#include <errno.h>
+#include <usb.h>
#if 0 //ifdef DEBUG
#define DEBUG_PRINT(arg) printf arg
#define DEBUG_PRINT(arg)
#endif
-#define A_MAX_USB_STRING_LEN 126
-
-
-/* ######################################################################## */
-#if defined(_WIN32)
-/* ######################################################################## */
-
-#include "targetver.h"
-#include <windows.h>
-#include <setupapi.h>
-#include <hidsdi.h>
-#include <hidpi.h>
-
-#ifdef _MSC_VER
-#pragma comment(lib, "setupapi")
-#pragma comment(lib, "hid")
-
-#define snprintf _snprintf
-#endif /*_MSC_VER*/
-
-/*
- * Convert UTF-16 null term. string to single byte (ASCII or ISO Latin)
- * change all weird characters to "?"
- */
-static void usbstring_to_ascii(unsigned short *wp, char *cp, int size)
-{
- unsigned short *wpend = wp + (size/sizeof(unsigned short));
- for( ; wp < wpend; )
- {
- unsigned short h = *wp++;
- *cp++ = (h < 0xFF) ? (char)h : '?';
- if (h == 0)
- break;
- }
-}
-
-/*
- * Read HID string for vendor and device, return as ASCII (or ISO Latin...)
- */
-int usbhidGetVendorString(USBDEVHANDLE usbh, char *buffer, int len)
-{
- /* HidD_GetManufacturerString returns zero terminated UTF-16 string */
- /* Error if buffer is too short */
- if ( !HidD_GetManufacturerString((HANDLE)usbh, (void*)buffer, len ) ) {
- DEBUG_PRINT(("error obtaining vendor name\n"));
- return USBHID_ERR_IO_HID;
- }
- usbstring_to_ascii((UINT16*)buffer, buffer, len);
- return 0;
-}
-
-int usbhidGetProductString(USBDEVHANDLE usbh, char *buffer, int len)
-{
- /* HidD_GetProductString returns zero terminated UTF-16 string */
- /* Error if buffer is too short */
- if (!HidD_GetProductString((HANDLE)usbh, (void*)buffer, len ) ) {
- DEBUG_PRINT(("error obtaining product name\n"));
- return USBHID_ERR_IO_HID;
- }
- usbstring_to_ascii((UINT16*)buffer, buffer, len);
- return 0;
-}
-
-/*
- * Enumerate HID USB devices.
- * In Windows this will find also non-USB devices, but assume that
- * filtering by PID & VID is enough.
- * Some HID devices (mice, kbd) are locked by Windows and cannot be opened.
- * If we cannot open a device for R&W, we skip it without error.
- * Assume our devices are not of types reserved by Windows.
- */
-int usbhidEnumDevices(int vendor, int product,
- void *context,
- int (*usbhidEnumFunc)(USBDEVHANDLE usbh, void *ctx))
-{
- GUID hidGuid; /* GUID for HID class */
- HDEVINFO deviceInfoList;
- SP_DEVICE_INTERFACE_DATA deviceInfo;
- SP_DEVICE_INTERFACE_DETAIL_DATA_W *deviceDetails = NULL;
- DWORD size;
- int i, openFlag = 0; /* may be FILE_FLAG_OVERLAPPED */
- int errorCode = USBHID_ERR_NOTFOUND;
- HANDLE handle = INVALID_HANDLE_VALUE;
- HIDD_ATTRIBUTES deviceAttributes;
-
- HidD_GetHidGuid(&hidGuid);
- deviceInfoList = SetupDiGetClassDevsW(&hidGuid, NULL, NULL, DIGCF_PRESENT | DIGCF_INTERFACEDEVICE);
- if (!deviceInfoList || deviceInfoList == INVALID_HANDLE_VALUE)
- {
- return USBHID_ERR_NOTFOUND;
- }
-
- deviceInfo.cbSize = sizeof(deviceInfo);
- for (i=0; ; i++) {
- if(handle != INVALID_HANDLE_VALUE){
- CloseHandle(handle);
- handle = INVALID_HANDLE_VALUE;
- }
- if( !SetupDiEnumDeviceInterfaces(deviceInfoList, 0, &hidGuid, i, &deviceInfo) )
- break; /* no more entries */
- /* first do a dummy call just to determine the actual size required */
- SetupDiGetDeviceInterfaceDetailW(deviceInfoList, &deviceInfo, NULL, 0, &size, NULL);
- if(deviceDetails != NULL)
- free(deviceDetails);
- deviceDetails = malloc(size);
- deviceDetails->cbSize = sizeof(*deviceDetails);
- /* this call is for real: */
- SetupDiGetDeviceInterfaceDetailW(deviceInfoList, &deviceInfo, deviceDetails, size, &size, NULL);
- DEBUG_PRINT(("checking HID path \"%s\"\n", deviceDetails->DevicePath));
-
- handle = CreateFileW(deviceDetails->DevicePath,
- GENERIC_READ|GENERIC_WRITE, FILE_SHARE_READ|FILE_SHARE_WRITE, NULL, OPEN_EXISTING, openFlag, NULL);
- if(handle == INVALID_HANDLE_VALUE){
- DEBUG_PRINT(("open USB device failed: gle=%d\n", (int)GetLastError()));
- /* errorCode = USBOPEN_ERR_ACCESS; opening will always fail for mouse -- ignore */
- continue;
- }
- deviceAttributes.Size = sizeof(deviceAttributes);
- HidD_GetAttributes(handle, &deviceAttributes);
- DEBUG_PRINT(("device attributes: vid=%d pid=%d ver=%4.4X\n", deviceAttributes.VendorID, deviceAttributes.ProductID, deviceAttributes.VersionNumber));
- if(deviceAttributes.VendorID != vendor || deviceAttributes.ProductID != product)
- continue; /* skip this device */
-
- errorCode = 0;
- if ( 0 == usbhidEnumFunc((USBDEVHANDLE)handle, context) )
- {
- break; /* stop enumeration */
- }
-
- /* Now the handle is owned by the callback */
- handle = INVALID_HANDLE_VALUE;
- }
-
- SetupDiDestroyDeviceInfoList(deviceInfoList);
- if(deviceDetails != NULL)
- free(deviceDetails);
-
- return errorCode;
-}
-
-
-void usbhidCloseDevice(USBDEVHANDLE usbh)
-{
- CloseHandle((HANDLE)usbh);
-}
-
-
-int usbhidSetReport(USBDEVHANDLE usbh, char *buffer, int len)
-{
- BOOLEAN rval;
- rval = HidD_SetFeature((HANDLE)usbh, buffer, len);
- return rval == 0 ? USBHID_ERR_IO_HID : 0;
-}
-
-
-int usbhidGetReport(USBDEVHANDLE usbh, int reportNumber, char *buffer, int *len)
-{
- BOOLEAN rval = 0;
- buffer[0] = reportNumber;
- rval = HidD_GetFeature((HANDLE)usbh, buffer, *len);
- return rval == 0 ? USBHID_ERR_IO_HID : 0;
-}
-
-
-
-/* ######################################################################## */
-#else /* defined WIN32 #################################################### */
-/* ######################################################################## */
-
-// Using the old simple version of libusb (before 1.0)
-#include <usb.h>
// USBDEVHANDLE is same as struct usb_device *
// Open handles are stored in struct usb_device (HACK! revise for new libusb)
#define USBRQ_HID_GET_REPORT 0x01
#define USBRQ_HID_SET_REPORT 0x09
-
#define USB_HID_REPORT_TYPE_FEATURE 3
#define A_REPORT_REQUEST_TIMEOUT 5000 /* in milliseconds */
void usbhidSetUsesReportId(USBDEVHANDLE usbh)
{
//TODO Implement if some devices prepend report IDs
+ //This differs among several versions of Linux HID libraries...
}
-/* ######################################################################## */
-#endif /* WIN32 */
-/* ######################################################################## */
-
int usbhidStrerror_r( int err, char *buf, int len)
{
const char *s;
--- /dev/null
+/* hiddata_mswin.c
+ * Win32 variant
+ */
+
+/* Inspired by hiddata.c|h by Christian Starkjohann
+ * Copyright: (c) 2008 by OBJECTIVE DEVELOPMENT Software GmbH
+ */
+
+#if !defined(_WIN32)
+#error
+#endif
+
+#include "hiddata.h"
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+
+#if 0 //ifdef DEBUG
+#define DEBUG_PRINT(arg) printf arg
+#else
+#define DEBUG_PRINT(arg)
+#endif
+
+#include "targetver.h"
+#include <windows.h>
+#include <setupapi.h>
+#include <hidsdi.h>
+#include <hidpi.h>
+
+#ifdef _MSC_VER
+#pragma comment(lib, "setupapi")
+#pragma comment(lib, "hid")
+#define snprintf _snprintf
+#else /* GCC, Mingw... */
+#endif /*_MSC_VER*/
+
+/*
+ * Convert UTF-16 null term. string to single byte (ASCII or ISO Latin)
+ * change all weird characters to "?"
+ */
+static void usbstring_to_ascii(unsigned short *wp, char *cp, int size)
+{
+ unsigned short *wpend = wp + (size/sizeof(unsigned short));
+ for( ; wp < wpend; )
+ {
+ unsigned short h = *wp++;
+ *cp++ = (h < 0xFF) ? (char)h : '?';
+ if (h == 0)
+ break;
+ }
+}
+
+/*
+ * Read HID string for vendor and device, return as ASCII (or ISO Latin...)
+ */
+int usbhidGetVendorString(USBDEVHANDLE usbh, char *buffer, int len)
+{
+ /* HidD_GetManufacturerString returns zero terminated UTF-16 string */
+ /* Error if buffer is too short */
+ if ( !HidD_GetManufacturerString((HANDLE)usbh, (void*)buffer, len ) ) {
+ DEBUG_PRINT(("error obtaining vendor name\n"));
+ return USBHID_ERR_IO_HID;
+ }
+ usbstring_to_ascii((UINT16*)buffer, buffer, len);
+ return 0;
+}
+
+int usbhidGetProductString(USBDEVHANDLE usbh, char *buffer, int len)
+{
+ /* HidD_GetProductString returns zero terminated UTF-16 string */
+ /* Error if buffer is too short */
+ if (!HidD_GetProductString((HANDLE)usbh, (void*)buffer, len ) ) {
+ DEBUG_PRINT(("error obtaining product name\n"));
+ return USBHID_ERR_IO_HID;
+ }
+ usbstring_to_ascii((UINT16*)buffer, buffer, len);
+ return 0;
+}
+
+/*
+ * Enumerate HID USB devices.
+ * In Windows this will find also non-USB devices, but assume that
+ * filtering by PID & VID is enough.
+ * Some HID devices (mice, kbd) are locked by Windows and cannot be opened.
+ * If we cannot open a device for R&W, we skip it without error.
+ * Assume our devices are not of types reserved by Windows.
+ */
+int usbhidEnumDevices(int vendor, int product,
+ void *context,
+ int (*usbhidEnumFunc)(USBDEVHANDLE usbh, void *ctx))
+{
+ GUID hidGuid; /* GUID for HID class */
+ HDEVINFO deviceInfoList;
+ SP_DEVICE_INTERFACE_DATA deviceInfo;
+ SP_DEVICE_INTERFACE_DETAIL_DATA_W *deviceDetails = NULL;
+ DWORD size;
+ int i, openFlag = 0; /* may be FILE_FLAG_OVERLAPPED */
+ int errorCode = USBHID_ERR_NOTFOUND;
+ HANDLE handle = INVALID_HANDLE_VALUE;
+ HIDD_ATTRIBUTES deviceAttributes;
+
+ HidD_GetHidGuid(&hidGuid);
+ deviceInfoList = SetupDiGetClassDevsW(&hidGuid, NULL, NULL, DIGCF_PRESENT | DIGCF_INTERFACEDEVICE);
+ if (!deviceInfoList || deviceInfoList == INVALID_HANDLE_VALUE)
+ {
+ return USBHID_ERR_NOTFOUND;
+ }
+
+ deviceInfo.cbSize = sizeof(deviceInfo);
+ for (i=0; ; i++) {
+ if(handle != INVALID_HANDLE_VALUE){
+ CloseHandle(handle);
+ handle = INVALID_HANDLE_VALUE;
+ }
+ if( !SetupDiEnumDeviceInterfaces(deviceInfoList, 0, &hidGuid, i, &deviceInfo) )
+ break; /* no more entries */
+ /* first do a dummy call just to determine the actual size required */
+ SetupDiGetDeviceInterfaceDetailW(deviceInfoList, &deviceInfo, NULL, 0, &size, NULL);
+ if(deviceDetails != NULL)
+ free(deviceDetails);
+ deviceDetails = malloc(size);
+ deviceDetails->cbSize = sizeof(*deviceDetails);
+ /* this call is for real: */
+ SetupDiGetDeviceInterfaceDetailW(deviceInfoList, &deviceInfo, deviceDetails, size, &size, NULL);
+ DEBUG_PRINT(("checking HID path \"%s\"\n", deviceDetails->DevicePath));
+
+ handle = CreateFileW(deviceDetails->DevicePath,
+ GENERIC_READ|GENERIC_WRITE, FILE_SHARE_READ|FILE_SHARE_WRITE, NULL, OPEN_EXISTING, openFlag, NULL);
+ if(handle == INVALID_HANDLE_VALUE){
+ DEBUG_PRINT(("open USB device failed: gle=%d\n", (int)GetLastError()));
+ /* errorCode = USBOPEN_ERR_ACCESS; opening will always fail for mouse -- ignore */
+ continue;
+ }
+ deviceAttributes.Size = sizeof(deviceAttributes);
+ HidD_GetAttributes(handle, &deviceAttributes);
+ DEBUG_PRINT(("device attributes: vid=%d pid=%d ver=%4.4X\n", deviceAttributes.VendorID, deviceAttributes.ProductID, deviceAttributes.VersionNumber));
+ if(deviceAttributes.VendorID != vendor || deviceAttributes.ProductID != product)
+ continue; /* skip this device */
+
+ errorCode = 0;
+ if ( 0 == usbhidEnumFunc((USBDEVHANDLE)handle, context) )
+ {
+ break; /* stop enumeration */
+ }
+
+ /* Now the handle is owned by the callback */
+ handle = INVALID_HANDLE_VALUE;
+ }
+
+ SetupDiDestroyDeviceInfoList(deviceInfoList);
+ if(deviceDetails != NULL)
+ free(deviceDetails);
+
+ return errorCode;
+}
+
+
+void usbhidCloseDevice(USBDEVHANDLE usbh)
+{
+ CloseHandle((HANDLE)usbh);
+}
+
+
+int usbhidSetReport(USBDEVHANDLE usbh, char *buffer, int len)
+{
+ BOOLEAN rval;
+ rval = HidD_SetFeature((HANDLE)usbh, buffer, len);
+ return rval == 0 ? USBHID_ERR_IO_HID : 0;
+}
+
+
+int usbhidGetReport(USBDEVHANDLE usbh, int reportNumber, char *buffer, int *len)
+{
+ BOOLEAN rval = 0;
+ buffer[0] = reportNumber;
+ rval = HidD_GetFeature((HANDLE)usbh, buffer, *len);
+ return rval == 0 ? USBHID_ERR_IO_HID : 0;
+}
+
+
+int usbhidStrerror_r( int err, char *buf, int len)
+{
+ const char *s;
+ switch (err) {
+ case USBHID_ERR_ACCESS: s = "Access to device denied";
+ case USBHID_ERR_NOTFOUND: s = "The specified device was not found";
+ case USBHID_ERR_IO: s = "Communication error with device";
+ case USBHID_ERR_IO_HID: s = "HID I/O error with device";
+ default:
+ s = "";
+ }
+
+ return snprintf(buf, len, "%s", s);
+}
UniqueIdentifier="{4FC737F1-C7A5-4376-A066-2A32D752A2FF}"
>
<File
- RelativePath=".\hiddata.c"
+ RelativePath=".\hiddata_mswin.c"
>
</File>
<File
<ClInclude Include="targetver.h" />
</ItemGroup>
<ItemGroup>
- <ClCompile Include="hiddata.c" />
+ <ClCompile Include="hiddata_mswin.c" />
<ClCompile Include="usbrelay-cmd.c" />
</ItemGroup>
<ItemGroup>
-// Chinese USB/HID relay command line tool:
+// Command line tool for Chinese USB/HID relays
//
-// pa02 20-Nov-2014 supports 1,2,4 - relay devices
+// pa02 20-Nov-2014 supports 1,2,4,8 - relay devices
//
-// Build for Windows: using VC++ 2008 and WDK7.1
-//~~~~~~~~~~~~~~~~~~~~~~~~
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
/* Prototype: V-USB example: vusb-20121206/examples/hid-data/commandline/hidtool.c
* Author: Christian Starkjohann
* Copyright: (c) 2008 by OBJECTIVE DEVELOPMENT Software GmbH
*/
-#define A_VER_STR "r1.2"
+#define A_VER_STR "r1.4"
#define A_URL "http://vusb.wikidot.com/project:driver-less-usb-relays-hid-interface"
#include <stdio.h>
#define USB_RELAY_VENDOR_NAME "www.dcttech.com"
#define USB_RELAY_NAME_PREF "USBRelay" // + number
-
-static int g_max_relay_num = 0;
+#define USB_RELAY_ID_STR_LEN 5 /* length of "unique serial number" in the devices */
static int rel_read_status_raw(USBDEVHANDLE dev, void *raw_data);
+#define printerr(fmt, ...) fprintf(stderr, fmt, ## __VA_ARGS__);
+
/* ------------------------------------------------------------------------- */
static void usage(char *myName)
else p = strrchr(myName, '/'); /* whatever */
if (p) myName = p + 1;
- fprintf(stderr, "USBHID relay utility, " A_VER_STR " " A_URL "\n\n");
- fprintf(stderr, "Usage:\n");
- fprintf(stderr, " %s on <num> - turn relay <num> ON\n", myName);
- fprintf(stderr, " %s off <num> - turn relay <num> OFF\n", myName);
- fprintf(stderr, " %s state - print state of the relays\n", myName);
- fprintf(stderr, " %s enum - print state of all relay devices\n", myName);
- fprintf(stderr, "\nParameter ID=XXXXX selects one device if several are connected.\n");
- fprintf(stderr, "Example: %s ID=ABCDE on 1\n", myName);
+ printf("HID USB relay utility, " A_VER_STR " "
+ "For info: " A_URL "\n"
+ "Usage:\n");
+ printf(" %s on <num> - turn relay <num> ON\n", myName);
+ printf(" %s off <num> - turn relay <num> OFF\n", myName);
+ printf(" %s state - print states of the relays\n", myName);
+ printf(" %s enum - print state of all found relay devices\n", myName);
+ printf("\nParameter ID=XXXXX selects one device if several are connected.\n");
+ printf("Example: %s ID=ABCDE ON 2\n\n", myName);
}
static const char *usbErrorMessage(int errCode)
{
static char buffer[80];
-
- switch (errCode) {
- case USBOPEN_ERR_ACCESS: return "Access to device denied";
- case USBOPEN_ERR_NOTFOUND: return "The specified device was not found";
- case USBOPEN_ERR_IO: return "Communication error with device";
+ buffer[0] = 0;
+ if ( errCode != USBHID_ERR_UNKNOWN ) {
+ usbhidStrerror_r(errCode, buffer, sizeof(buffer));
+ }
+ if ( 0 == buffer[0] ) {
+ snprintf(buffer, sizeof(buffer), "Unknown error (%d)", errCode);
}
-
- snprintf(buffer, sizeof(buffer), "Unknown USB error %d", errCode);
return buffer;
}
static struct
{
USBDEVHANDLE mydev;
- char id[10];
+ char id[USB_RELAY_ID_STR_LEN * 2];
} g_enumCtx;
+static int g_max_relay_num = 0; // number of relays in the active device
+
+
static int enumFunc(USBDEVHANDLE dev, void *context)
{
static const char vendorName[] = USB_RELAY_VENDOR_NAME;
goto next;
}
- i = strlen(buffer);
+ i = (int)strlen(buffer);
if ( i != strlen(productName) + 1 )
{
goto next;
if ( num <= 0 || num > 8 )
{
+ printerr("Unknown relay device? num relays=%d\n", num);
goto next;
}
- /* Check the unique ID: 5 bytes at offs 0 */
+ /* Check the unique ID: USB_RELAY_ID_STR_LEN bytes at offset 1 (just after the report id) */
err = rel_read_status_raw(dev, buffer);
if( err < 0 )
{
- fprintf(stderr, "error reading report 0: %s\n", usbErrorMessage(err));
+ printerr("Error reading report 0: %s\n", usbErrorMessage(err));
goto next;
}
- //hexdump(buffer + 1, sizeof(buffer) - 1);
- for (i=1; i <=5; i++)
+ for (i = 1; i <= USB_RELAY_ID_STR_LEN; i++)
{
unsigned char x = (unsigned char)buffer[i];
if (x <= 0x20 || x >= 0x7F)
}
}
- if( buffer[6] != 0 )
+ if( buffer[USB_RELAY_ID_STR_LEN + 1] != 0 )
{
- fprintf(stderr, "Bad device ID!\n");
+ printerr("Bad device ID!\n");
goto next;
}
if ( g_enumCtx.id[0] != 0 )
{
- if ( 0 != memcmp(g_enumCtx.id, &buffer[1], 5) )
+ if ( 0 != memcmp(g_enumCtx.id, &buffer[1], USB_RELAY_ID_STR_LEN) )
goto next;
}
#if 0
if ( g_enumCtx.mydev )
{
- fprintf(stderr, "ERROR: More than one relay device found. ID must be specified\n");
+ printerr("ERROR: More than one relay device found. ID must be specified\n");
usbhidCloseDevice(dev);
usbhidCloseDevice(g_enumCtx.mydev);
return 0;
}
#endif
g_enumCtx.mydev = dev;
- return 0;
+ return 0; /* stop */
next:
/* Continue search */
{
int err;
- // TODO: enumerate all instances, then filter by unique ID
- //$$$ find any one device
-
err = usbhidEnumDevices(USB_CFG_VENDOR_ID, USB_CFG_DEVICE_ID, &g_enumCtx, enumFunc);
if ( err || !g_enumCtx.mydev )
{
- fprintf(stderr, "error finding USB relay: %s\n", usbErrorMessage(err));
+ printerr("error finding USB relay: %s\n", usbErrorMessage(err));
return NULL;
}
return g_enumCtx.mydev;
}
-/* ------------------------------------------------------------------------- */
-#if 0
-static void hexdump(char *buffer, int len)
-{
-int i;
-FILE *fp = stdout;
-
- for(i = 0; i < len; i++){
- if(i != 0){
- if(i % 16 == 0){
- fprintf(fp, "\n");
- }else{
- fprintf(fp, " ");
- }
- }
- fprintf(fp, "0x%02x", buffer[i] & 0xff);
- }
- if(i != 0)
- fprintf(fp, "\n");
-}
-#endif
-
-/* ------------------------------------------------------------------------- */
// Read state of all relays
// @return bit mask of all relays (R1->bit 0, R2->bit 1 ...) or -1 on error
err = usbhidGetReport(dev, reportnum, buffer, &len);
if ( err ) {
- fprintf(stderr, "error reading status: %s\n", usbErrorMessage(err));
+ printerr("error reading status: %s\n", usbErrorMessage(err));
return -1;
}
if ( len != 9 || buffer[0] != reportnum ) {
- fprintf(stderr, "ERROR: wrong HID report returned! %d\n", len);
+ printerr("ERROR: wrong HID report returned! %d\n", len);
return -2;
}
if (raw_data) {
/* copy raw report data */
- memcpy( raw_data, buffer, sizeof(buffer) );
+ memcpy( raw_data, buffer, len );
}
return (unsigned char)buffer[8]; /* byte of relay states */
}
if ( relaynum <= 0 || relaynum > g_max_relay_num ) {
- fprintf(stderr, "Invalid relay number. Must be 1-%d or ALL)\n", g_max_relay_num);
+ printerr("Invalid relay number. Must be 1-%d or ALL)\n", g_max_relay_num);
return 1;
}
memset(buffer, 0, sizeof(buffer));
buffer[0] = 0; /* report # */
buffer[1] = is_on ? 0xFF : 0xFD;
+ // ALL ON=0xFE, ALL OFF=0xFC
buffer[2] = (unsigned char)relaynum;
if((err = usbhidSetReport(dev, (void*)buffer, 9)) != 0) {
- fprintf(stderr, "Error writing data: %s\n", usbErrorMessage(err));
+ printerr("Error writing data: %s\n", usbErrorMessage(err));
return 1;
}
}
if ( err ) {
- fprintf(stderr, "Error: failed set %s relay %u\n", is_on ? "ON":"OFF", relaynum);
+ printerr("Error: failed to set relay %u %s\n", relaynum, is_on ? "ON":"OFF");
return 1;
}
#define onoff(n) on_off[!!(err & (1U << n))]
err = rel_read_status_raw(dev, buffer);
- if ( err < 0 ){
- fprintf(stderr, "error reading data: %s\n", usbErrorMessage(err));
+ if ( err < 0 ) {
+ printerr("Error reading data: %s\n", usbErrorMessage(err));
err = 1;
} else {
- //hexdump(buffer + 1, len - 1);
switch (g_max_relay_num) {
case 1:
printf("Board ID=[%5.5s] State: R1=%s\n", &buffer[1], onoff(0) );
printf("Board ID=[%5.5s] State: R1=%s R3=%s R1=%s R4=%s\n",
&buffer[1], onoff(0), onoff(1), onoff(2), onoff(3) );
break;
- default:
+ default: /* print as bit mask */
printf("Board ID=[%5.5s] State: %2.2X (hex)\n", &buffer[1], (unsigned char)err );
break;
}
err = usbhidEnumDevices(USB_CFG_VENDOR_ID, USB_CFG_DEVICE_ID, &g_enumCtx, showFunc);
if ( err )
{
- fprintf(stderr, "Error finding USB relay: %s\n", usbErrorMessage(err));
+ printerr("Error finding USB relays: %s\n", usbErrorMessage(err));
return 1;
}
}
if ( strncasecmp(arg1, "id=", 3) == 0 ) {
- if (strlen(&arg1[3]) != 5) {
- fprintf(stderr, "ERROR: ID must be 5 characters (%s)\n", arg1);
+ if (strlen(&arg1[3]) != USB_RELAY_ID_STR_LEN) {
+ printerr("ERROR: ID must be %d characters (%s)\n", USB_RELAY_ID_STR_LEN, arg1);
return 1;
}