From 28f22397b38276022144da8e65cbb64d6d07effb Mon Sep 17 00:00:00 2001 From: pavel Date: Sun, 23 Nov 2014 02:19:27 +0200 Subject: [PATCH] Linux version with old libusb done --- commandline/Makefile | 10 +- commandline/README-hidusb-relay-cmd.txt | 62 ++++---- commandline/hiddata.c | 198 +++++++++++++----------- commandline/usbrelay-cmd.c | 8 +- 4 files changed, 156 insertions(+), 122 deletions(-) diff --git a/commandline/Makefile b/commandline/Makefile index 53a3e02..8c1b5d2 100644 --- a/commandline/Makefile +++ b/commandline/Makefile @@ -1,4 +1,4 @@ -# Makefile for HID USB relay utility, usbrelay-cmd +# Makefile for HID USB relay utility, hidusbrelay-cmd # pa01 16-apr-2014 # # prototype: @@ -9,7 +9,10 @@ # License: GNU GPL v2 (see License.txt), GNU GPL v3 or proprietary (CommercialLicense.txt) +CMD_UTILITY=hidusb-relay-cmd + # For Unix/Linux, Mac OS X: +# Using old simple version of libusb USBFLAGS= `libusb-config --cflags` USBLIBS= `libusb-config --libs` EXE_SUFFIX= @@ -23,11 +26,12 @@ EXE_SUFFIX= #USBFLAGS+=-I/usr/include CC= gcc -CFLAGS= -O -Wall $(USBFLAGS) +DEBUGFLAGS= +CFLAGS= -O -Wall $(USBFLAGS) $(DEBUGFLAGS) LIBS= $(USBLIBS) OBJ= usbrelay-cmd.o hiddata.o -PROGRAM= usbrelay-cmd$(EXE_SUFFIX) +PROGRAM= $(CMD_UTILITY)$(EXE_SUFFIX) all: $(PROGRAM) diff --git a/commandline/README-hidusb-relay-cmd.txt b/commandline/README-hidusb-relay-cmd.txt index 617c708..c32a43a 100644 --- a/commandline/README-hidusb-relay-cmd.txt +++ b/commandline/README-hidusb-relay-cmd.txt @@ -1,50 +1,63 @@ Command line utility for Chinese USB HID relays (USB 1.1 HID devices, VEN=5824 (0x16c0) DEV=1503 (0x05DF), based on the V-USB project) -Rev. 1.2 (20-Nov-2014) +Rev. 1.2 (22-Nov-2014) Usage: usbrelay-cmd [ID=XXXXX] ON|OFF -- turn the relay ON or OFF - where is the relay number: 1-2 or "*" for all + where is the relay number: 1-2 or "ALL" - usbrelay-cmd STATE -- print state of one relay device with its "unique ID" + usbrelay-cmd STATUS -- print state of one relay device with its "unique ID" usbrelay-cmd ENUM -- print state of all devices and their "unique IDs" -Example: +Parameter ID=XXXXX specifies one relay device if several are connected. +Without the ID= parameter, if several devices are connected, the program uses any one of them. +The ID string is case sensitive. + +Examples: + usbrelay-cmd id=ABCDE on 1 + usbrelay-cmd id=ABCDE OFF ALL + usbrelay-cmd id=ABCDE STATUS + + usbrelay-cmd ON ALL -- uses first found relay device + usbrelay-cmd STATUS -- uses first found relay device -Note: Parameter ID=XXXXX specifies one relay device if several are connected. -The ID string is case sensitive. -The Enum command lists IDs of all connected devices. -Without the ID= parameter, if several devices are connected, the program uses any one of them. +REMARKS +The Enum command lists the IDs of all available relay devices. +The ID strings are case sensitive and should be exactly as output by the "enum" command. Each relay has two contact pairs: Normally Open and Normally Closed. The OFF state is the "normal" state, when the red LED is off, and the Normally Open contacts are disconnected. The ON state is when the red LED is on, and the Normally Open contacts are connected. Note: In the original s/w readme, "Open" means ON, "Close" means OFF. - -Currently tested with 1 and 2-relay devices. -SOURCE based on a V-USB HID example. -LICENSE: TBD! - Should be non-GPL; rewrite all prototype code! +This program has been tested with 1- and 2-relay devices. +On Linux, this program requires root access by default! +To grant access to non-root users, define udev rule for the devices: +Example: + SUBSYSTEMS=="usb", ATTRS{idVendor}=="16c0", ATTRS{idProduct}=="05df", MODE:="0666" -For Windows XP and later: - - using the in-box hid.dll and WDK 7.1. - - build with VC++ 2008 -For Linux (PC, x86): - - using the old libusb (v 0.1; usb.h) - - tested on Centos 5, Ubuntu 12.04 - - requires root! - To grant access to users, define udev rule for this device (or all HID). Example: - SUBSYSTEMS=="usb", ATTRS{idVendor}=="16c0", ATTRS{idProduct}=="05df", MODE:="0666" +BUILDING + +For Windows XP and later: + - Using the in-box hid.dll + - Build with VC++ 2008 and WDK 7.1, or newer VC++ with the suitable WDK +For Linux (PC, x86): + - using the old libusb (v. 0.1; usb.h) + - tested on Centos 5, Ubuntu 12.04, Mint + +SOURCE based on a V-USB HID example. +LICENSE: TBD! + Should be non-GPL; rewrite all prototype code! + >>> Target for 1st public release: ================================ 1. Remove GPL encumberment @@ -52,11 +65,6 @@ For Linux (PC, x86): Other TODO's ============ -TODO: support multiple devices, support other existing variants - usbrelay-cmd -ID=XXXXX ON|OFF - usbrelay-cmd -ID=XXXXX STATE - or -s XXXXX or --SN - -ID=* --ANY any one? *** Check that these devices indeed have unique IDs! The orig. h file mentions function: usb_relay_device_set_serial(int hHandle, char serial[5]); diff --git a/commandline/hiddata.c b/commandline/hiddata.c index 7965352..79c71bb 100644 --- a/commandline/hiddata.c +++ b/commandline/hiddata.c @@ -9,6 +9,7 @@ #include "hiddata.h" #include #include +#include #if 0 //ifdef DEBUG #define DEBUG_PRINT(arg) printf arg @@ -155,14 +156,12 @@ int usbhidEnumDevices(int vendor, int product, } - void usbhidCloseDevice(USBDEVHANDLE usbh) { CloseHandle((HANDLE)usbh); } - int usbhidSetReport(USBDEVHANDLE usbh, char *buffer, int len) { BOOLEAN rval; @@ -185,9 +184,25 @@ int usbhidGetReport(USBDEVHANDLE usbh, int reportNumber, char *buffer, int *len) #else /* defined WIN32 #################################################### */ /* ######################################################################## */ +// Using the old simple version of libusb (before 1.0) #include -#define usbDevice usb_dev_handle /* use libusb's device structure */ +// USBDEVHANDLE is same as struct usb_device * +// Open handles are stored in struct usb_device (HACK! revise for new libusb) + +static inline struct usb_device * usbDevStruct(USBDEVHANDLE usbh) { + return (struct usb_device *)usbh; +} + +static inline usb_dev_handle * usbDevHandle(USBDEVHANDLE usbh) { + return (usb_dev_handle *)(usbDevStruct(usbh)->children); +} + +// TO DO: for devices that prepend report ID, implement this somehow +static inline int usesReportIDs(USBDEVHANDLE usbh) { + /* return 1 if 1st byte of get/set report buffer is report ID */ + return 0; +} /* ------------------------------------------------------------------------- */ @@ -196,46 +211,20 @@ int usbhidGetReport(USBDEVHANDLE usbh, int reportNumber, char *buffer, int *len) #define USB_HID_REPORT_TYPE_FEATURE 3 - -static int usesReportIDs = 0; /* 1 => 1st byte of get/set report buffer is report ID */ +#define A_REPORT_REQUEST_TIMEOUT 5000 /* in milliseconds */ /* ------------------------------------------------------------------------- */ -static int usbhidGetStringAscii(usb_dev_handle *dev, int index, char *buf, int buflen) -{ -char buffer[256]; -int rval, i; - - if((rval = usb_get_string_simple(dev, index, buf, buflen)) >= 0) /* use libusb version if it works */ - return rval; - if((rval = usb_control_msg(dev, USB_ENDPOINT_IN, USB_REQ_GET_DESCRIPTOR, (USB_DT_STRING << 8) + index, 0x0409, buffer, sizeof(buffer), 5000)) < 0) - return rval; - if(buffer[1] != USB_DT_STRING){ - *buf = 0; - return 0; - } - if((unsigned char)buffer[0] < rval) - rval = (unsigned char)buffer[0]; - rval /= 2; - /* lossy conversion to ISO Latin1: */ - for(i=1;i buflen) /* destination buffer overflow */ - break; - buf[i-1] = buffer[2 * i]; - if(buffer[2 * i + 1] != 0) /* outside of ISO Latin1 range */ - buf[i-1] = '?'; - } - buf[i-1] = 0; - return i-1; -} +int usbhidEnumDevices(int vendor, int product, + void *context, + int (*usbhidEnumFunc)(USBDEVHANDLE usbh, void *ctx)) -int usbhidOpenDevice(usbDevice_t **device, int vendor, char *vendorName, int product, char *productName, int _usesReportIDs) { -struct usb_bus *bus; -struct usb_device *dev; -usb_dev_handle *handle = NULL; -int errorCode = USBOPEN_ERR_NOTFOUND; -static int didUsbInit = 0; + struct usb_bus *bus; + struct usb_device *dev; + usb_dev_handle *handle = NULL; + int errorCode = USBOPEN_ERR_NOTFOUND; + static int didUsbInit = 0; if(!didUsbInit){ usb_init(); @@ -243,77 +232,102 @@ static int didUsbInit = 0; } usb_find_busses(); usb_find_devices(); - for(bus=usb_get_busses(); bus; bus=bus->next){ - for(dev=bus->devices; dev; dev=dev->next){ - if(dev->descriptor.idVendor == vendor && dev->descriptor.idProduct == product){ - char string[256]; - int len; + for (bus=usb_get_busses(); bus; bus=bus->next) { + for (dev=bus->devices; dev; dev=dev->next) { + if (dev->descriptor.idVendor == vendor && dev->descriptor.idProduct == product) { handle = usb_open(dev); /* we need to open the device in order to query strings */ - if(!handle){ + if ( !handle ) { errorCode = USBOPEN_ERR_ACCESS; fprintf(stderr, "Warning: cannot open USB device: %s\n", usb_strerror()); continue; } - if(vendorName == NULL && productName == NULL){ /* name does not matter */ - break; + + errorCode = 0; + +// printf("Probing: [%s] nc=%u %p\n", dev->filename, dev->num_children, dev->children); + // Assume our devices are leaf, so we can use dev->children to store the handle + if ( dev->children ) { + fprintf(stderr, "ERROR: assertion failed for usb dev %p\n", dev); + usb_close(handle); + handle = NULL; + continue; } - /* now check whether the names match: */ - len = usbhidGetStringAscii(handle, dev->descriptor.iManufacturer, string, sizeof(string)); - if(len < 0){ - errorCode = USBOPEN_ERR_IO; - fprintf(stderr, "Warning: cannot query manufacturer for device: %s\n", usb_strerror()); - }else{ - errorCode = USBOPEN_ERR_NOTFOUND; - /* fprintf(stderr, "seen device from vendor ->%s<-\n", string); */ - if(strcmp(string, vendorName) == 0){ - len = usbhidGetStringAscii(handle, dev->descriptor.iProduct, string, sizeof(string)); - if(len < 0){ - errorCode = USBOPEN_ERR_IO; - fprintf(stderr, "Warning: cannot query product for device: %s\n", usb_strerror()); - }else{ - errorCode = USBOPEN_ERR_NOTFOUND; - /* fprintf(stderr, "seen product ->%s<-\n", string); */ - if(strcmp(string, productName) == 0) - break; - } - } + + dev->children = (void*)handle; + if ( 0 == usbhidEnumFunc((void*)dev, context) ) + { + break; /* stop enumeration */ } - usb_close(handle); - handle = NULL; + + /* Now the handle is owned by the callback */ + handle = 0; } } - if(handle) - break; - } - if(handle != NULL){ - errorCode = 0; - *device = (void *)handle; - usesReportIDs = _usesReportIDs; } + return errorCode; } /* ------------------------------------------------------------------------- */ -void usbhidCloseDevice(usbDevice_t *device) +void usbhidCloseDevice(USBDEVHANDLE usbh) { - if(device != NULL) - usb_close((void *)device); + if (usbh != NULL) { + usb_close(usbDevHandle(usbh)); + usbDevStruct(usbh)->children = NULL; + } } /* ------------------------------------------------------------------------- */ -int usbhidSetReport(usbDevice_t *device, char *buffer, int len) +static int usbhidGetStringAscii(struct usb_dev_handle *dev, int index, char *buf, int buflen) +{ + int rval; + if((rval = usb_get_string_simple(dev, index, buf, buflen)) >= 0) /* use libusb version if it works */ + return rval; + if (errno == EPERM) + fprintf(stderr, "usbhid: Access denied to USB device. Run as root or adjust device permissions.\n"); + else + fprintf(stderr, "usbhid: %s error %s\n", __FUNCTION__, usb_strerror()); + return -1; +} + +int usbhidGetVendorString(USBDEVHANDLE usbh, char *buffer, int len) +{ + int len2 = usbhidGetStringAscii(usbDevHandle(usbh), usbDevStruct(usbh)->descriptor.iManufacturer, buffer, len); + if (len2 < 0) { + fprintf(stderr, "Warning: cannot query vendor for device\n"); + return USBOPEN_ERR_IO; + } + return 0; +} + +int usbhidGetProductString(USBDEVHANDLE usbh, char *buffer, int len) +{ + int len2 = usbhidGetStringAscii(usbDevHandle(usbh), usbDevStruct(usbh)->descriptor.iProduct, buffer, len); + if (len2 < 0) { + fprintf(stderr, "Warning: cannot query product for device\n"); + return USBOPEN_ERR_IO; + } + return 0; +} + + + + +int usbhidSetReport(USBDEVHANDLE usbh, char *buffer, int len) { -int bytesSent, reportId = buffer[0]; + int bytesSent, reportId = buffer[0]; - if(!usesReportIDs){ + if ( !usesReportIDs(usbh) ) { buffer++; /* skip dummy report ID */ len--; } - bytesSent = usb_control_msg((void *)device, USB_TYPE_CLASS | USB_RECIP_DEVICE | USB_ENDPOINT_OUT, USBRQ_HID_SET_REPORT, USB_HID_REPORT_TYPE_FEATURE << 8 | (reportId & 0xff), 0, buffer, len, 5000); - if(bytesSent != len){ - if(bytesSent < 0) + bytesSent = usb_control_msg(usbDevHandle(usbh), + USB_TYPE_CLASS | USB_RECIP_DEVICE | USB_ENDPOINT_OUT, USBRQ_HID_SET_REPORT, USB_HID_REPORT_TYPE_FEATURE << 8 | (reportId & 0xff), + 0, buffer, len, A_REPORT_REQUEST_TIMEOUT); + if (bytesSent != len) { + if (bytesSent < 0) fprintf(stderr, "Error sending message: %s\n", usb_strerror()); return USBOPEN_ERR_IO; } @@ -322,27 +336,35 @@ int bytesSent, reportId = buffer[0]; /* ------------------------------------------------------------------------- */ -int usbhidGetReport(usbDevice_t *device, int reportNumber, char *buffer, int *len) +int usbhidGetReport(USBDEVHANDLE usbh, int reportNumber, char *buffer, int *len) { int bytesReceived, maxLen = *len; - if(!usesReportIDs){ + if (!usesReportIDs(usbh)) { buffer++; /* make room for dummy report ID */ maxLen--; } - bytesReceived = usb_control_msg((void *)device, USB_TYPE_CLASS | USB_RECIP_DEVICE | USB_ENDPOINT_IN, USBRQ_HID_GET_REPORT, USB_HID_REPORT_TYPE_FEATURE << 8 | reportNumber, 0, buffer, maxLen, 5000); + bytesReceived = usb_control_msg(usbDevHandle(usbh), + USB_TYPE_CLASS | USB_RECIP_DEVICE | USB_ENDPOINT_IN, USBRQ_HID_GET_REPORT, USB_HID_REPORT_TYPE_FEATURE << 8 | reportNumber, + 0, buffer, maxLen, A_REPORT_REQUEST_TIMEOUT); if(bytesReceived < 0){ fprintf(stderr, "Error sending message: %s\n", usb_strerror()); return USBOPEN_ERR_IO; } *len = bytesReceived; - if(!usesReportIDs){ + if (!usesReportIDs(usbh)) { buffer[-1] = reportNumber; /* add dummy report ID */ (*len)++; } return 0; } + +void usbhidSetUsesReportId(USBDEVHANDLE usbh) +{ + //TODO Implement if some devices prepend report IDs +} + /* ######################################################################## */ #endif /* WIN32 */ /* ######################################################################## */ diff --git a/commandline/usbrelay-cmd.c b/commandline/usbrelay-cmd.c index ac15d52..a5366de 100644 --- a/commandline/usbrelay-cmd.c +++ b/commandline/usbrelay-cmd.c @@ -220,7 +220,7 @@ static int rel_read_status_raw(USBDEVHANDLE dev, void *raw_data) } if ( len != 9 || buffer[0] != reportnum ) { - fprintf(stderr, "ERROR: wrong HID report returned!\n", len); + fprintf(stderr, "ERROR: wrong HID report returned! %d\n", len); return -2; } @@ -239,7 +239,7 @@ static int rel_onoff( USBDEVHANDLE dev, int is_on, char const *numstr ) int err = -1; int relaynum = numstr ? atoi(numstr) : 0; - if ( numstr && (0 == strcmp(numstr,"*")) ) { + if ( numstr && (0 == strcasecmp(numstr,"all")) ) { char x[2] = {'1', 0}; int i; for (i = 1; i <= g_max_relay_num; i++) { @@ -251,7 +251,7 @@ 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); + fprintf(stderr, "Invalid relay number. Must be 1-%d or ALL)\n", g_max_relay_num); return 1; } @@ -259,7 +259,7 @@ static int rel_onoff( USBDEVHANDLE dev, int is_on, char const *numstr ) buffer[0] = 0; /* report # */ buffer[1] = is_on ? 0xFF : 0xFD; buffer[2] = (unsigned char)relaynum; - if((err = usbhidSetReport(dev, buffer, 9)) != 0) { + if((err = usbhidSetReport(dev, (void*)buffer, 9)) != 0) { fprintf(stderr, "Error writing data: %s\n", usbErrorMessage(err)); return 1; } -- 2.25.1