Added OS X, split hiddata.c files
authorpavel <pavel@aaa>
Sun, 11 Jan 2015 18:28:58 +0000 (20:28 +0200)
committerpavel <pavel@aaa>
Sun, 11 Jan 2015 18:45:15 +0000 (20:45 +0200)
commandline/Makefile
commandline/hiddata-osx.c [new file with mode: 0644]
commandline/hiddata_libusb01.c [moved from commandline/hiddata.c with 50% similarity]
commandline/hiddata_mswin.c [new file with mode: 0644]
commandline/hidusb-relay-cmd(vc2008).vcproj
commandline/hidusb-relay-cmd(vc2013).vcxproj
commandline/usbrelay-cmd.c

index c3e5b0f39565886da7c2657e9e74ea865515091e..0b4d898da0c11e6b2e8f6245cd5c40e1133ca1f1 100644 (file)
 
 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)
diff --git a/commandline/hiddata-osx.c b/commandline/hiddata-osx.c
new file mode 100644 (file)
index 0000000..803961b
--- /dev/null
@@ -0,0 +1,268 @@
+/**
+ * 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);
+}
similarity index 50%
rename from commandline/hiddata.c
rename to commandline/hiddata_libusb01.c
index 432533a76f39745721d057b095aeb14eb0fe4a2e..4ed5ed8e9d0fd62a5ffc468ee4c1af86dcdcd8b3 100644 (file)
@@ -1,15 +1,17 @@
-/* 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)
@@ -210,7 +41,6 @@ static inline int usesReportIDs(USBDEVHANDLE usbh) {
 
 #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 */
@@ -365,12 +195,9 @@ int bytesReceived, maxLen = *len;
 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;
diff --git a/commandline/hiddata_mswin.c b/commandline/hiddata_mswin.c
new file mode 100644 (file)
index 0000000..d556f8b
--- /dev/null
@@ -0,0 +1,194 @@
+/* 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);
+}
index fecca5b22e96d7c4fd1d722bfa2f7ef25af6f61d..8c9b7d33f359d619c21eb502c5bfaa59578f9b7c 100644 (file)
                        UniqueIdentifier="{4FC737F1-C7A5-4376-A066-2A32D752A2FF}"
                        >
                        <File
-                               RelativePath=".\hiddata.c"
+                               RelativePath=".\hiddata_mswin.c"
                                >
                        </File>
                        <File
index 4b79664240ab135914e0fe73b67e6e0640f7b863..e639136247a74490ff85d6bb68dc52bfd3f00c48 100644 (file)
     <ClInclude Include="targetver.h" />
   </ItemGroup>
   <ItemGroup>
-    <ClCompile Include="hiddata.c" />
+    <ClCompile Include="hiddata_mswin.c" />
     <ClCompile Include="usbrelay-cmd.c" />
   </ItemGroup>
   <ItemGroup>
index a5366dedb730efbbd5e8ecbcb2990eb5e39d4398..421997bc01bf69d01bb291c2fdd45171ec4f48a8 100644 (file)
@@ -1,9 +1,8 @@
-// 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
@@ -11,7 +10,7 @@
  * 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)
@@ -36,28 +36,28 @@ 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;
 }
 
@@ -65,10 +65,13 @@ static const char *usbErrorMessage(int errCode)
 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; 
@@ -90,7 +93,7 @@ static int enumFunc(USBDEVHANDLE dev, void *context)
         goto next;
     }
 
-    i = strlen(buffer);
+    i = (int)strlen(buffer);
     if ( i != strlen(productName) + 1 )
     {
         goto next;
@@ -107,19 +110,19 @@ static int enumFunc(USBDEVHANDLE dev, void *context)
 
     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)
@@ -129,9 +132,9 @@ static int enumFunc(USBDEVHANDLE dev, void *context)
         }
     }
 
-    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;
     }
 
@@ -140,20 +143,20 @@ static int enumFunc(USBDEVHANDLE dev, void *context)
 
     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 */
@@ -165,43 +168,17 @@ static USBDEVHANDLE openDevice(void)
 {
     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
@@ -215,18 +192,18 @@ static int rel_read_status_raw(USBDEVHANDLE dev, void *raw_data)
 
     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 */
@@ -251,16 +228,17 @@ static int rel_onoff( USBDEVHANDLE dev, int is_on, char const *numstr )
     }
 
     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;
     }
     
@@ -272,7 +250,7 @@ static int rel_onoff( USBDEVHANDLE dev, int is_on, char const *numstr )
     }
 
     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;
     }
 
@@ -289,11 +267,10 @@ static int show_status(USBDEVHANDLE dev)
 #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) );
@@ -306,7 +283,7 @@ static int show_status(USBDEVHANDLE dev)
                 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;
         }
@@ -339,7 +316,7 @@ static int show_relays(void)
     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;
     }
 
@@ -365,8 +342,8 @@ int main(int argc, char **argv)
     }
 
     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;
         }