From b60fbf7e6f805345a71212dfbd23653036710749 Mon Sep 17 00:00:00 2001 From: pavel Date: Fri, 21 Nov 2014 04:23:41 +0200 Subject: [PATCH] Added enumeration function (enum). Undone returning strings as unicode: this will encumber port to linux. --- commandline/hiddata.c | 175 +++++++++++++++++--------------- commandline/hiddata.h | 39 +++++--- commandline/hidusb-tool.h | 3 - commandline/usbrelay-cmd.c | 199 +++++++++++++++++++++++++------------ 4 files changed, 257 insertions(+), 159 deletions(-) diff --git a/commandline/hiddata.c b/commandline/hiddata.c index 2874c33..ffa8824 100644 --- a/commandline/hiddata.c +++ b/commandline/hiddata.c @@ -6,8 +6,9 @@ * License: GNU GPL v2 (see License.txt), GNU GPL v3 or proprietary (CommercialLicense.txt) */ -#include #include "hiddata.h" +#include +#include #if 0 //ifdef DEBUG #define DEBUG_PRINT(arg) printf arg @@ -15,8 +16,11 @@ #define DEBUG_PRINT(arg) #endif +#define A_MAX_USB_STRING_LEN 126 + + /* ######################################################################## */ -#if defined(_WIN32) /* ##################################################### */ +#if defined(_WIN32) /* ######################################################################## */ #include "targetver.h" @@ -30,30 +34,64 @@ #pragma comment(lib, "hid") #endif /*_MSC_VER*/ +/* + * Convert UTF-16 null term. string to single byte + * (ASCII or ISO Latin); change weird characters to "?" + */ +static void usbstring_to_ascii(wchar_t *wp, char *cp) +{ + for(;;) + { + 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 USBOPEN_ERR_IO; + } + usbstring_to_ascii((wchar_t*)buffer, buffer); + return 0; +} -static void convertUniToAscii(char *buffer) +int usbhidGetProductString(USBDEVHANDLE usbh, char *buffer, int len) { -unsigned short *uni = (void *)buffer; -char *ascii = buffer; - - while(*uni != 0){ - if(*uni >= 256){ - *ascii++ = '?'; - }else{ - *ascii++ = (char)(*uni++); - } + /* 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 USBOPEN_ERR_IO; } - *ascii++ = 0; + usbstring_to_ascii((wchar_t*)buffer, buffer); + return 0; } -int usbhidOpenDevice(usbDevice_t **device, int vendor, char *vendorName, int product, char *productName, int usesReportIDs) +/* + * 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 driver */ + GUID hidGuid; /* GUID for HID class */ HDEVINFO deviceInfoList; SP_DEVICE_INTERFACE_DATA deviceInfo; - SP_DEVICE_INTERFACE_DETAIL_DATA *deviceDetails = NULL; + SP_DEVICE_INTERFACE_DETAIL_DATA_W *deviceDetails = NULL; DWORD size; int i, openFlag = 0; /* may be FILE_FLAG_OVERLAPPED */ int errorCode = USBOPEN_ERR_NOTFOUND; @@ -61,35 +99,34 @@ int usbhidOpenDevice(usbDevice_t **device, int vendor, char *vendorName, int pro HIDD_ATTRIBUTES deviceAttributes; HidD_GetHidGuid(&hidGuid); - deviceInfoList = SetupDiGetClassDevs(&hidGuid, NULL, NULL, DIGCF_PRESENT | DIGCF_INTERFACEDEVICE); + deviceInfoList = SetupDiGetClassDevsW(&hidGuid, NULL, NULL, DIGCF_PRESENT | DIGCF_INTERFACEDEVICE); + if (!deviceInfoList || deviceInfoList == INVALID_HANDLE_VALUE) + { + return USBOPEN_ERR_NOTFOUND; + } + deviceInfo.cbSize = sizeof(deviceInfo); - for(i=0;;i++){ + for (i=0; ; i++) { if(handle != INVALID_HANDLE_VALUE){ CloseHandle(handle); handle = INVALID_HANDLE_VALUE; } - if(!SetupDiEnumDeviceInterfaces(deviceInfoList, 0, &hidGuid, i, &deviceInfo)) + if( !SetupDiEnumDeviceInterfaces(deviceInfoList, 0, &hidGuid, i, &deviceInfo) ) break; /* no more entries */ /* first do a dummy call just to determine the actual size required */ - SetupDiGetDeviceInterfaceDetail(deviceInfoList, &deviceInfo, NULL, 0, &size, NULL); + SetupDiGetDeviceInterfaceDetailW(deviceInfoList, &deviceInfo, NULL, 0, &size, NULL); if(deviceDetails != NULL) free(deviceDetails); deviceDetails = malloc(size); deviceDetails->cbSize = sizeof(*deviceDetails); /* this call is for real: */ - SetupDiGetDeviceInterfaceDetail(deviceInfoList, &deviceInfo, deviceDetails, size, &size, NULL); + SetupDiGetDeviceInterfaceDetailW(deviceInfoList, &deviceInfo, deviceDetails, size, &size, NULL); DEBUG_PRINT(("checking HID path \"%s\"\n", deviceDetails->DevicePath)); -#if 0 - /* If we want to access a mouse or keyboard, we can only use feature - * requests as the device is locked by Windows. It must be opened - * with ACCESS_TYPE_NONE. - */ - handle = CreateFile(deviceDetails->DevicePath, ACCESS_TYPE_NONE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, openFlag, NULL); -#endif - /* attempt opening for R/W -- we don't care about devices which can't be accessed */ - handle = CreateFile(deviceDetails->DevicePath, GENERIC_READ|GENERIC_WRITE, FILE_SHARE_READ|FILE_SHARE_WRITE, NULL, OPEN_EXISTING, openFlag, NULL); + + 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(("opening failed: %d\n", (int)GetLastError())); + DEBUG_PRINT(("open USB device failed: gle=%d\n", (int)GetLastError())); /* errorCode = USBOPEN_ERR_ACCESS; opening will always fail for mouse -- ignore */ continue; } @@ -97,78 +134,56 @@ int usbhidOpenDevice(usbDevice_t **device, int vendor, char *vendorName, int pro HidD_GetAttributes(handle, &deviceAttributes); DEBUG_PRINT(("device attributes: vid=%d pid=%d\n", deviceAttributes.VendorID, deviceAttributes.ProductID)); if(deviceAttributes.VendorID != vendor || deviceAttributes.ProductID != product) - continue; /* ignore this device */ - errorCode = USBOPEN_ERR_NOTFOUND; - if(vendorName != NULL && productName != NULL){ - char buffer[512]; - memset(buffer, 0xFF, sizeof(buffer)); - if(!HidD_GetManufacturerString(handle, buffer, sizeof(buffer))){ - DEBUG_PRINT(("error obtaining vendor name\n")); - errorCode = USBOPEN_ERR_IO; - continue; - } - convertUniToAscii(buffer); - DEBUG_PRINT(("vendorName = \"%s\"\n", buffer)); - if(strcmp(vendorName, buffer) != 0) - continue; - memset(buffer, 0xFF, sizeof(buffer)); - if(!HidD_GetProductString(handle, buffer, sizeof(buffer))){ - DEBUG_PRINT(("error obtaining product name\n")); - errorCode = USBOPEN_ERR_IO; - continue; - } - convertUniToAscii(buffer); - DEBUG_PRINT(("productName = \"%s\"\n", buffer)); - if(strcmp(productName, buffer) != 0) - continue; - } - break; /* we have found the device we are looking for! */ + 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); - if(handle != INVALID_HANDLE_VALUE){ - *device = (usbDevice_t *)handle; - errorCode = 0; - } - return errorCode; + + return errorCode; } -/* ------------------------------------------------------------------------ */ -void usbhidCloseDevice(usbDevice_t *device) + +void usbhidCloseDevice(USBDEVHANDLE usbh) { - CloseHandle((HANDLE)device); + CloseHandle((HANDLE)usbh); } -/* ------------------------------------------------------------------------ */ -int usbhidSetReport(usbDevice_t *device, char *buffer, int len) -{ -BOOLEAN rval; - rval = HidD_SetFeature((HANDLE)device, buffer, len); +int usbhidSetReport(USBDEVHANDLE usbh, char *buffer, int len) +{ + BOOLEAN rval; + rval = HidD_SetFeature((HANDLE)usbh, buffer, len); return rval == 0 ? USBOPEN_ERR_IO : 0; } -/* ------------------------------------------------------------------------ */ -int usbhidGetReport(usbDevice_t *device, int reportNumber, char *buffer, int *len) +int usbhidGetReport(USBDEVHANDLE usbh, int reportNumber, char *buffer, int *len) { -BOOLEAN rval = 0; - + BOOLEAN rval = 0; buffer[0] = reportNumber; - rval = HidD_GetFeature((HANDLE)device, buffer, *len); + rval = HidD_GetFeature((HANDLE)usbh, buffer, *len); return rval == 0 ? USBOPEN_ERR_IO : 0; } -/* ------------------------------------------------------------------------ */ + /* ######################################################################## */ #else /* defined WIN32 #################################################### */ /* ######################################################################## */ -#include #include #define usbDevice usb_dev_handle /* use libusb's device structure */ @@ -181,7 +196,7 @@ BOOLEAN rval = 0; #define USB_HID_REPORT_TYPE_FEATURE 3 -static int usesReportIDs; +static int usesReportIDs = 0; /* 1 => 1st byte of get/set report buffer is report ID */ /* ------------------------------------------------------------------------- */ @@ -328,5 +343,5 @@ int bytesReceived, maxLen = *len; } /* ######################################################################## */ -#endif /* defined WIN32 ################################################### */ +#endif /* WIN32 */ /* ######################################################################## */ diff --git a/commandline/hiddata.h b/commandline/hiddata.h index 245453d..cddb897 100644 --- a/commandline/hiddata.h +++ b/commandline/hiddata.h @@ -1,4 +1,4 @@ -/* Name: hiddata.h +/* Name: hiddata.h $$$$$$$$$$$$$$$$$$$$$$$$$ * Author: Christian Starkjohann * Creation Date: 2008-04-11 * Tabsize: 4 @@ -27,27 +27,39 @@ appropriate libraries in either case: "-lhid -lusb -lsetupapi" on Windows and /* ------------------------------------------------------------------------ */ +struct usbDevice; typedef struct usbDevice usbDevice_t; +typedef usbDevice_t *USBDEVHANDLE; + /* Opaque data type representing the USB device. This can be a Windows handle * or a libusb handle, depending on the backend implementation. */ /* ------------------------------------------------------------------------ */ -int usbhidOpenDevice(usbDevice_t **device, int vendorID, char *vendorName, int productID, char *productName, int usesReportIDs); -/* This function opens a USB device. 'vendorID' and 'productID' are the numeric - * Vendor-ID and Product-ID of the device we want to open. If 'vendorName' and - * 'productName' are both not NULL, only devices with matching manufacturer- - * and product name strings are accepted. If the device uses report IDs, - * 'usesReportIDs' must be set to a non-zero value. - * Returns: If a matching device has been found, USBOPEN_SUCCESS is returned - * and '*device' is set to an opaque pointer representing the device. The - * device must be closed with usbhidCloseDevice(). If the device has not been - * found or opening failed, an error code is returned. +/* @func usbhidEnumDevices + * Enumerates USB HID devices, filtered by USB vendorID and productID. + * Each found device is opened and the callback will be called. + * The callback can probe the device and close it or keep open. + * If the callback returns 0, enumeration stops, else it goes on. + * @returns 0 if some devices were found or error code otherwise. */ -void usbhidCloseDevice(usbDevice_t *device); -/* Every device opened with usbhidOpenDevice() must be closed with this function. +int usbhidEnumDevices(int vendorID, int productID, void *context, + int (*usbhidEnumFunc)(USBDEVHANDLE usbh, void *ctx)); + +/* + * Close USB device handle opened with usbhidEnumDevices */ +void usbhidCloseDevice(USBDEVHANDLE usbh); + +/* + * Read HID vendor and product strings as ASCII null terminated strings + * Returns 0 on success, error code if error occured or the buffer is too short + * Any non-ascii characters in the strings will be replaced to '?' + */ +int usbhidGetVendorString(USBDEVHANDLE usbh, char *buffer, int len); +int usbhidGetProductString(USBDEVHANDLE usbh, char *buffer, int len); + int usbhidSetReport(usbDevice_t *device, char *buffer, int len); /* This function sends a feature report to the device. The report ID must be * in the first byte of buffer and the length 'len' of the report is specified @@ -55,6 +67,7 @@ int usbhidSetReport(usbDevice_t *device, char *buffer, int len); * to 0 (dummy report ID). * Returns: 0 on success, an error code otherwise. */ + int usbhidGetReport(usbDevice_t *device, int reportID, char *buffer, int *len); /* This function obtains a feature report from the device. The requested * report-ID is passed in 'reportID'. The caller must pass a buffer of the size diff --git a/commandline/hidusb-tool.h b/commandline/hidusb-tool.h index d095c0f..036c5f1 100644 --- a/commandline/hidusb-tool.h +++ b/commandline/hidusb-tool.h @@ -7,9 +7,6 @@ #define USB_CFG_VENDOR_ID 0x16c0 /* 5824 = voti.nl */ #define USB_CFG_DEVICE_ID 0x05DF /* obdev's shared PID for HIDs */ - -typedef usbDevice_t *USBDEVHANDLE; /* I prefer handles to libusb 'opaque pointer' */ - #ifndef DEBUG_PRINT #if 0 //ifdef DEBUG # define DEBUG_PRINT(arg) printf arg diff --git a/commandline/usbrelay-cmd.c b/commandline/usbrelay-cmd.c index bd7f4cc..edb1be6 100644 --- a/commandline/usbrelay-cmd.c +++ b/commandline/usbrelay-cmd.c @@ -13,7 +13,7 @@ * Copyright: (c) 2008 by OBJECTIVE DEVELOPMENT Software GmbH */ -#define A_VER_STR "r1.1x (1 device only)" +#define A_VER_STR "r1.1y (1 device only)" #include #include @@ -21,9 +21,7 @@ #include "hidusb-tool.h" #define USB_RELAY_VENDOR_NAME "www.dcttech.com" -#define USB_RELAY1_NAME "USBRelay1" // 1 relay -#define USB_RELAY2_NAME "USBRelay2" // 2 relays -#define USB_RELAY4_NAME "USBRelay4" // 4 relays +#define USB_RELAY_NAME_PREF "USBRelay" // + number static int g_max_relay_num = 0; @@ -46,70 +44,108 @@ static const char *usbErrorMessage(int errCode) return buffer; } -static USBDEVHANDLE openDevice(void) +// Data for enumeration func: +static struct { - USBDEVHANDLE dev = 0; - char vendorName[] = USB_RELAY_VENDOR_NAME, - productName[] = USB_RELAY1_NAME; - int vid = USB_CFG_VENDOR_ID; - int pid = USB_CFG_DEVICE_ID; - int err; - int num = 0; + USBDEVHANDLE mydev; + char id[10]; +} g_enumCtx; - // TODO: enumerate all instances, then filter by unique ID - //$$$ find any one device - strcpy(productName, USB_RELAY2_NAME); - if((err = usbhidOpenDevice(&dev, vid, vendorName, pid, productName, 0)) == 0) { - num = 2; - goto check1; - } - strcpy(productName, USB_RELAY1_NAME); - if((err = usbhidOpenDevice(&dev, vid, vendorName, pid, productName, 0)) == 0) { - num = 1; - goto check1; +static int enumFunc(USBDEVHANDLE dev, void *context) +{ + static const char vendorName[] = USB_RELAY_VENDOR_NAME; + static const char productName[] = USB_RELAY_NAME_PREF; + int err; + char buffer[128*sizeof(short)]; // max USB string is 128 UTF-16 chars + int num = 0; + int i; + + err = usbhidGetVendorString(dev, buffer, sizeof(buffer)); + if ( err || 0 != strcmp( buffer, vendorName) ) + { + goto next; + } + + err = usbhidGetProductString(dev, buffer, sizeof(buffer)); + if (err) + { + goto next; + } + + i = strlen(buffer); + if ( i != strlen(productName) + 1 ) + { + goto next; + } + + /* the last char of ProductString is number of relays */ + num = (int)(buffer[i - 1]) - (int)'0'; + buffer[i - 1] = 0; + + if ( 0 != strcmp( buffer, productName) ) + { + goto next; + } + + if ( num <= 0 || num > 8 ) + { + goto next; + } + + /* Check the unique ID: 5 bytes at offs 0 */ + err = rel_read_status_raw(dev, buffer); + if( err < 0 ) + { + fprintf(stderr, "error reading report 0: %s\n", usbErrorMessage(err)); + goto next; } - - strcpy(productName, USB_RELAY4_NAME); - if((err = usbhidOpenDevice(&dev, vid, vendorName, pid, productName, 0)) == 0) { - num = 4; - goto check1; + + //hexdump(buffer + 1, sizeof(buffer) - 1); + for (i=1; i <=5; i++) + { + unsigned char x = (unsigned char)buffer[i]; + if (x <= 0x20 || x >= 0x7F) + { + fprintf(stderr, "Bad device ID!\n"); + goto next; + } } - fprintf(stderr, "error finding USB relay: %s\n", usbErrorMessage(err)); - return NULL; - - check1: - { /* Check the unique ID: 5 bytes at offs 0 */ - char buffer[16]; - err = rel_read_status_raw(dev, buffer); - if( err < 0 ){ - fprintf(stderr, "error reading report 0: %s\n", usbErrorMessage(err)); - usbhidCloseDevice(dev); - return NULL; - }else{ - int i; - //hexdump(buffer + 1, sizeof(buffer) - 1); - for (i=1; i <=5; i++) { - unsigned char x = (unsigned char)buffer[i]; - if (x <= 0x20 || x >= 0x7F) { - fprintf(stderr, "Bad device ID!\n"); - usbhidCloseDevice(dev); - return NULL; - } - } - if( buffer[6] != 0 ) { - fprintf(stderr, "Bad device ID!\n"); - usbhidCloseDevice(dev); - return NULL; - } - - DEBUG_PRINT(("Device %s found: ID=[%5s]\n", USB_CFG_DEVICE_NAME, &buffer[1])); - g_max_relay_num = num; - } + if( buffer[6] != 0 ) + { + fprintf(stderr, "Bad device ID!\n"); + goto next; } - return dev; + DEBUG_PRINT(("Device %s%d found: ID=[%5s]\n", productName, num, &buffer[1])); + strcpy( g_enumCtx.id, (char*)&buffer[1] ); + g_max_relay_num = num; + g_enumCtx.mydev = dev; + return 0; + + next: + /* Continue search */ + usbhidCloseDevice(dev); + return 1; +} + +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)); + return NULL; + } + + return g_enumCtx.mydev; } /* ------------------------------------------------------------------------- */ @@ -148,6 +184,7 @@ static void usage(char *myName) 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); } @@ -177,7 +214,7 @@ static int rel_read_status_raw(USBDEVHANDLE dev, void *raw_data) memcpy( raw_data, buffer, sizeof(buffer) ); } - return (int)(unsigned char)buffer[8]; /* byte of relay states */ + return (unsigned char)buffer[8]; /* byte of relay states */ } @@ -264,6 +301,37 @@ static int show_status(USBDEVHANDLE dev) #undef onoff } +// Enumerate available relay devices + +static int showFunc(USBDEVHANDLE dev, void *context) +{ + int err = enumFunc( dev, context ); + if (err != 0 || g_enumCtx.mydev == 0) // not my device, continue + return err; + + show_status(g_enumCtx.mydev); + usbhidCloseDevice(g_enumCtx.mydev); + g_enumCtx.mydev = 0; + + return 1; // continue +} + +static int show_relays(void) +{ + int err; + g_enumCtx.mydev = 0; + + 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)); + return 1; + } + + return 0; +} + + int main(int argc, char **argv) { USBDEVHANDLE dev = 0; @@ -273,12 +341,17 @@ int main(int argc, char **argv) if ( !arg1 ) { usage(argv[0]); - exit(1); + return 1; } + if ( strcasecmp(arg1, "enum") == 0 ) { + err = show_relays(); + return err; + } + dev = openDevice(); if ( !dev ) - exit(1); + return 1; if ( strncasecmp(arg1, "stat", 4) == 0 ) { // stat|state|status err = show_status(dev); -- 2.25.1