From: pavel Date: Sun, 11 Jan 2015 18:28:58 +0000 (+0200) Subject: Added OS X, split hiddata.c files X-Git-Url: https://vcs.fsf.org/?a=commitdiff_plain;h=fe63975da710f2cb0b445315a7b95efb9af4e20c;p=usb-relay-hid.git Added OS X, split hiddata.c files --- diff --git a/commandline/Makefile b/commandline/Makefile index c3e5b0f..0b4d898 100644 --- a/commandline/Makefile +++ b/commandline/Makefile @@ -11,26 +11,28 @@ 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 index 0000000..803961b --- /dev/null +++ b/commandline/hiddata-osx.c @@ -0,0 +1,268 @@ +/** + * File: hiddata-OSX.c + * Version for OS/X 10.9+ + * 05-jan-2014 pa + * License: ? + */ + +#include "hiddata.h" +#include +#include +#include +#include + +// OS X: Using IOKit HID functionality +#include +#include + + +#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); +} diff --git a/commandline/hiddata.c b/commandline/hiddata_libusb01.c similarity index 50% rename from commandline/hiddata.c rename to commandline/hiddata_libusb01.c index 432533a..4ed5ed8 100644 --- a/commandline/hiddata.c +++ b/commandline/hiddata_libusb01.c @@ -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 #include #include +#include #if 0 //ifdef DEBUG #define DEBUG_PRINT(arg) printf arg @@ -17,177 +19,6 @@ #define DEBUG_PRINT(arg) #endif -#define A_MAX_USB_STRING_LEN 126 - - -/* ######################################################################## */ -#if defined(_WIN32) -/* ######################################################################## */ - -#include "targetver.h" -#include -#include -#include -#include - -#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 // 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 index 0000000..d556f8b --- /dev/null +++ b/commandline/hiddata_mswin.c @@ -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 +#include +#include + +#if 0 //ifdef DEBUG +#define DEBUG_PRINT(arg) printf arg +#else +#define DEBUG_PRINT(arg) +#endif + +#include "targetver.h" +#include +#include +#include +#include + +#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); +} diff --git a/commandline/hidusb-relay-cmd(vc2008).vcproj b/commandline/hidusb-relay-cmd(vc2008).vcproj index fecca5b..8c9b7d3 100644 --- a/commandline/hidusb-relay-cmd(vc2008).vcproj +++ b/commandline/hidusb-relay-cmd(vc2008).vcproj @@ -193,7 +193,7 @@ UniqueIdentifier="{4FC737F1-C7A5-4376-A066-2A32D752A2FF}" > - + diff --git a/commandline/usbrelay-cmd.c b/commandline/usbrelay-cmd.c index a5366de..421997b 100644 --- a/commandline/usbrelay-cmd.c +++ b/commandline/usbrelay-cmd.c @@ -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 @@ -22,11 +21,12 @@ #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 - turn relay ON\n", myName); - fprintf(stderr, " %s off - turn relay 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 - turn relay ON\n", myName); + printf(" %s off - turn relay 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; }