Linux version with old libusb done
authorpavel <pavel@aaa>
Sun, 23 Nov 2014 00:19:27 +0000 (02:19 +0200)
committerpavel <pavel@aaa>
Sun, 23 Nov 2014 00:19:27 +0000 (02:19 +0200)
commandline/Makefile
commandline/README-hidusb-relay-cmd.txt
commandline/hiddata.c
commandline/usbrelay-cmd.c

index 53a3e024a1bd7b318b418388fa6693aa30ffeb9c..8c1b5d251aef33af308185d51be753ef3144668a 100644 (file)
@@ -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)
 
index 617c708b5cf755c17c7f4a42b921eb7ee945d83a..c32a43a24e7c50c52aae15a67593a375453b65cc 100644 (file)
@@ -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 <num>   -- turn the relay ON or OFF
   
-      where <num> is the relay number: 1-2 or "*" for all
+      where <num> is the relay number: 1-2 or "ALL"
 
-  usbrelay-cmd STAT         -- 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 <num>
-  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]);
index 7965352b6cfca5153083af3c68da8af08d9c0802..79c71bbfcdd20940bb0ebf261a16b7d038d74370 100644 (file)
@@ -9,6 +9,7 @@
 #include "hiddata.h"
 #include <stdio.h>
 #include <string.h>
+#include <errno.h>
 
 #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 <usb.h>
 
-#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<rval;i++){
-        if(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 */
 /* ######################################################################## */
index ac15d520ad09d6099186f394684d6f1edbf7169d..a5366dedb730efbbd5e8ecbcb2990eb5e39d4398 100644 (file)
@@ -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;
     }