2 * USB HID relays API library
5 * This is reconstruction of the original Windows DLL,
6 * as supplied by the USB relay vendors.
7 * It is binary compatible and works with their example programs.
8 * The original usb_relay_device.h file has been slightly hacked up.
10 * 12-jan-2015 pa01 Win32 version
13 #define MY_VERSION 0x02
15 #if defined (WIN32) || defined (_WIN32)
17 // Windows 32 or 64 bit
18 #include "targetver.h"
19 #define WIN32_EXTRALEAN
23 /* The original DLL has cdecl calling convention */
24 #define USBRL_CALL __cdecl
25 #define USBRL_API __declspec(dllexport) USBRL_CALL
27 #define snprintf _snprintf
31 #include "usb_relay_device.h"
33 #if USBRELAY_LIB_VER != MY_VERSION
34 #error "Oops. Wrong version of usb_relay_device.h"
37 #include "usb_relay_hw.h"
43 //#define dbgprintf(fmt, ...) fprintf(stderr, fmt, __VA_ARGS__)
44 //#define dbgprintf(fmt, ...) printf(fmt, __VA_ARGS__)
45 #define dbgprintf(fmt, ...) __noop(fmt, __VA_ARGS__)
46 //#define printerr(fmt, ...) fprintf(stderr, fmt, __VA_ARGS__)
47 //#define printerr(fmt, ...) printf(fmt, __VA_ARGS__)
48 #define printerr(fmt, ...) __noop(fmt, __VA_ARGS__)
54 struct usbrelay_internal_s
{
55 struct usb_relay_device_info urdi
; //public part
57 USBDEVHANDLE usbh
; // handle
61 // struct for enum context
63 struct usbrelay_internal_s
*head
, *tail
;
70 const char *g_dummyPath
= "NOTHING"; // passing dev.path to client not implemented, I return this as path.
72 static const char *usbErrorMessage(int errCode
)
74 static char buffer
[80];
76 if ( errCode
!= USBHID_ERR_UNKNOWN
) {
77 usbhidStrerror_r(errCode
, buffer
, sizeof(buffer
));
79 if ( 0 == buffer
[0] ) {
80 snprintf(buffer
, sizeof(buffer
), "Unknown error (%d)", errCode
);
85 // Read state of all relays
86 // @return bit mask of all relays (R1->bit 0, R2->bit 1 ...) or -1 on error
87 static int rel_read_status_raw(USBDEVHANDLE dev
, void *raw_data
)
92 int len
= 8 + 1; /* report id 1 byte + 8 bytes data */
93 memset(buffer
, 0, sizeof(buffer
));
95 err
= usbhidGetReport(dev
, reportnum
, buffer
, &len
);
97 printerr("error reading status: %s\n", usbErrorMessage(err
));
101 if ( len
!= 9 || buffer
[0] != reportnum
) {
102 printerr("ERROR: wrong HID report returned! %d\n", len
);
107 /* copy raw report data */
108 memcpy( raw_data
, buffer
, len
);
111 return (unsigned char)buffer
[8]; /* byte of relay states */
114 // Turn relay on/off.
115 // @param relaynum: positive (1-N): one relay index, negative: all, -num = number of relays
116 // @returns 0 ok, else error
117 static int rel_onoff( USBDEVHANDLE dev
, int is_on
, int relaynum
)
119 unsigned char buffer
[10];
121 unsigned char cmd1
, cmd2
, mask
, maskval
;
123 if ( relaynum
< 0 && (-relaynum
) <= 8 ) {
128 maskval
= (unsigned char)( (1U << (-relaynum
)) - 1 );
134 if ( relaynum
<= 0 || relaynum
> 8 ) {
135 printerr("Relay number must be 1-8\n");
138 mask
= (unsigned char)(1U << relaynum
);
139 cmd2
= (unsigned char)relaynum
;
149 memset(buffer
, 0, sizeof(buffer
));
150 buffer
[0] = 0; /* report # */
153 if((err
= usbhidSetReport(dev
, (void*)buffer
, 9)) != 0) {
154 printerr("Error writing data: %s\n", usbErrorMessage(err
));
158 // Read back & verify
159 err
= rel_read_status_raw(dev
, NULL
);
161 printerr("Error read back: %s\n", usbErrorMessage(err
));
166 if (err
!= maskval
) {
167 printerr("Error: failed to set relay %u %s\n", relaynum
, is_on
? "ON":"OFF");
177 /** Initialize the USB Relay Library
178 @returns: This function returns 0 on success and -1 on error.
180 int USBRL_API
usb_relay_init(void)
185 /** Finalize the USB Relay Library.
186 This function frees all of the static data associated with USB Relay Library.
187 It should be called at the end of execution to avoid memory leaks.
188 @returns: This function returns 0 on success and -1 on error.
190 int USBRL_API
usb_relay_exit(void)
195 // Enum function for building list of devices
197 int enumfunc(USBDEVHANDLE usbh
, void *context
)
199 // static const char vendorName[] = USB_RELAY_VENDOR_NAME;
200 static const char productName
[] = USB_RELAY_NAME_PREF
;
202 char buffer
[128*sizeof(short)]; // max USB string is 128 UTF-16 chars
205 struct usbrelay_internal_s
*q
;
206 struct enumctx_s
*ectx
= (struct enumctx_s
*)context
;
208 //NOTE: Ignore vendor string. This is against ObjDev rules, restore the check if needed!
210 err
= usbhidGetProductString(usbh
, buffer
, sizeof(buffer
));
216 i
= (int)strlen(buffer
);
217 if ( i
!= strlen(productName
) + 1 )
222 /* the last char of ProductString is number of relays */
223 num
= (int)(buffer
[i
- 1]) - (int)'0';
226 if ( 0 != strcmp( buffer
, productName
) )
231 if ( num
<= 0 || num
> 8 )
233 dbgprintf("Unknown relay device? num relays=%d\n", num
);
237 /* Check the unique ID: USB_RELAY_ID_STR_LEN bytes at offset 1 (just after the report id) */
238 err
= rel_read_status_raw(usbh
, buffer
);
241 dbgprintf("Error reading report 0: %s\n", usbErrorMessage(err
));
245 for (i
= 1; i
<= USB_RELAY_ID_STR_LEN
; i
++)
247 unsigned char x
= (unsigned char)buffer
[i
];
248 if (x
<= 0x20 || x
>= 0x7F)
250 dbgprintf("Bad usbrelay ID string!\n");
255 if( buffer
[USB_RELAY_ID_STR_LEN
+ 1] != 0 )
257 dbgprintf("Bad usbrelay ID string!\n");
261 dbgprintf("Device %s%d found: ID=[%5s]\n", productName
, num
, &buffer
[1]);
263 // allocate & save info
264 q
= (struct usbrelay_internal_s
*)calloc(1, sizeof(struct usbrelay_internal_s
));
266 dbgprintf("Malloc err\n");
267 goto next
; //$$$ revise
269 /* keep this device, continue */
271 memcpy(q
->idstr
, &buffer
[1], USB_RELAY_ID_STR_LEN
);
272 q
->urdi
.type
= num
; // enum = number of relays
273 q
->urdi
.serial_number
= &q
->idstr
[0];
274 q
->urdi
.device_path
= (char*)g_dummyPath
;
280 ectx
->tail
->urdi
.next
= (pusb_relay_device_info_t
)q
;
287 /* Continue search */
288 usbhidCloseDevice(usbh
);
292 // Enum function for open one device by ID
294 int enumOpenfunc(USBDEVHANDLE usbh
, void *context
)
296 // static const char vendorName[] = USB_RELAY_VENDOR_NAME;
297 static const char productName
[] = USB_RELAY_NAME_PREF
;
299 char buffer
[128*sizeof(short)]; // max USB string is 128 UTF-16 chars
302 struct enumctx_s
*ectx
= (struct enumctx_s
*)context
;
303 struct usbrelay_internal_s
*q
= ectx
->head
;
305 //NOTE: Ignore vendor string. This is against ObjDev rules, restore the check if needed!
307 err
= usbhidGetProductString(usbh
, buffer
, sizeof(buffer
));
313 i
= (int)strlen(buffer
);
314 if ( i
!= strlen(productName
) + 1 )
319 /* the last char of ProductString is number of relays */
320 num
= (int)(buffer
[i
- 1]) - (int)'0';
323 if ( 0 != strcmp( buffer
, productName
) )
328 if ( num
<= 0 || num
> 8 )
330 dbgprintf("Unknown relay device? num relays=%d\n", num
);
334 /* Check the unique ID: USB_RELAY_ID_STR_LEN bytes at offset 1 (just after the report id) */
335 err
= rel_read_status_raw(usbh
, buffer
);
338 dbgprintf("Error reading report 0: %s\n", usbErrorMessage(err
));
342 for (i
= 1; i
<= USB_RELAY_ID_STR_LEN
; i
++)
344 unsigned char x
= (unsigned char)buffer
[i
];
345 if (x
<= 0x20 || x
>= 0x7F)
347 dbgprintf("Bad usbrelay ID string!\n");
352 if( buffer
[USB_RELAY_ID_STR_LEN
+ 1] != 0 )
354 dbgprintf("Bad usbrelay ID string!\n");
358 dbgprintf("Device %s%d found: ID=[%5s]\n", productName
, num
, &buffer
[1]);
360 if ( 0 == memcmp( q
->idstr
, &buffer
[1], USB_RELAY_ID_STR_LEN
) ) {
362 q
->urdi
.type
= num
; // enum = number of relays
363 q
->urdi
.serial_number
= &q
->idstr
[0];
364 q
->urdi
.device_path
= (char*)g_dummyPath
;
370 /* Continue search */
371 usbhidCloseDevice(usbh
);
375 /** Enumerate the USB Relay Devices.*/
376 pusb_relay_device_info_t USBRL_API
usb_relay_device_enumerate(void)
378 struct enumctx_s ectx
;
380 memset(&ectx
, 0, sizeof(ectx
));
381 ret
= usbhidEnumDevices(USB_CFG_VENDOR_ID
, USB_CFG_DEVICE_ID
,
385 return (pusb_relay_device_info_t
)ectx
.head
;
389 /** Free an enumeration Linked List*/
390 void USBRL_API
usb_relay_device_free_enumerate(struct usb_relay_device_info
*dilist
)
392 struct usbrelay_internal_s
*p
= (struct usbrelay_internal_s
*)dilist
;
395 struct usbrelay_internal_s
*q
= (struct usbrelay_internal_s
*)((pusb_relay_device_info_t
)p
)->next
;
396 if (p
->usbh
&& ((USBDEVHANDLE
)(-1)) != p
->usbh
) {
397 usbhidCloseDevice(p
->usbh
);
407 /** Open device by serial number
408 serial_number == NULL is valid and means any one device.
409 @return: This function returns a valid handle to the device on success or NULL on failure.
410 Example: usb_relay_device_open_with_serial_number("abcde", 5) */
411 intptr_t USBRL_API
usb_relay_device_open_with_serial_number(const char *serial_number
, unsigned len
)
413 struct enumctx_s ectx
;
415 struct usbrelay_internal_s
*q
;
416 memset(&ectx
, 0, sizeof(ectx
));
418 if (serial_number
&& len
!= USB_RELAY_ID_STR_LEN
) {
419 printerr("Specified invalid str id length: %u", len
);
423 q
= ectx
.head
= calloc(1, sizeof(*ectx
.head
));
427 memcpy(q
->idstr
, serial_number
, len
);
429 ret
= usbhidEnumDevices(USB_CFG_VENDOR_ID
, USB_CFG_DEVICE_ID
,
433 goto ret_err
; // error during enum
435 if (ectx
.numdevs
== 0 || q
->usbh
== 0) {
436 goto ret_err
; // not found
439 q
->urdi
.next
= (void*)q
; // mark this element as standalone
447 /** Open a USB relay device
448 @return: This function returns a valid handle to the device on success or NULL on failure.
450 intptr_t USBRL_API
usb_relay_device_open(struct usb_relay_device_info
*device_info
)
452 struct usbrelay_internal_s
*p
= (struct usbrelay_internal_s
*)device_info
;
455 if ( (uintptr_t)p
->usbh
== 0 || (uintptr_t)p
->usbh
== (uintptr_t)-1 )
458 return (uintptr_t)device_info
;
461 /** Close a USB relay device*/
462 void USBRL_API
usb_relay_device_close(intptr_t hHandle
)
464 struct usbrelay_internal_s
*p
= (struct usbrelay_internal_s
*)hHandle
;
465 if ( 0 == hHandle
|| ((intptr_t)-1) == hHandle
)
468 if ( (void*)(p
->urdi
.next
) == (void*)p
) {
469 // This was made by usb_relay_device_open_with_serial_number() so free it now:
470 if ( p
->usbh
&& ((intptr_t)-1) != (intptr_t)(p
->usbh
)) {
471 usbhidCloseDevice(p
->usbh
);
477 // Else this can be in the list, don't do anything.
480 /** Turn ON a relay channel on the USB-Relay-Device
481 @param index -- which channel your want to open
482 @param hHandle -- which usb relay device your want to operate
483 @returns: 0 -- success; 1 -- error; 2 -- index is outnumber the number of the usb relay device
485 int USBRL_API
usb_relay_device_open_one_relay_channel(intptr_t hHandle
, int index
)
487 struct usbrelay_internal_s
*p
= (struct usbrelay_internal_s
*)hHandle
;
490 if ( index
<= 0 || index
> (int)p
->urdi
.type
)
492 return rel_onoff( p
->usbh
, 1, index
);
495 /** Turn ON all relay channels on the USB-Relay-Device
496 @param hHandle -- which usb relay device your want to operate
497 @returns: 0 -- success; 1 -- error
499 int USBRL_API
usb_relay_device_open_all_relay_channel(intptr_t hHandle
)
501 struct usbrelay_internal_s
*p
= (struct usbrelay_internal_s
*)hHandle
;
504 return rel_onoff( p
->usbh
, 1, -(int)p
->urdi
.type
);
507 /** Turn OFF a relay channel on the USB-Relay-Device
508 @param index -- which channel your want to close
509 @param hHandle -- which usb relay device your want to operate
510 @returns: 0 -- success; 1 -- error; 2 -- index is outnumber the number of the usb relay device
512 int USBRL_API
usb_relay_device_close_one_relay_channel(intptr_t hHandle
, int index
)
514 struct usbrelay_internal_s
*p
= (struct usbrelay_internal_s
*)hHandle
;
517 if ( index
<= 0 || index
> (int)p
->urdi
.type
)
519 return rel_onoff( p
->usbh
, 0, index
);
522 /** Turn OFF all relay channels on the USB-Relay-Device
523 @param hHandle -- which usb relay device your want to operate
524 @returns 0 -- success; 1 -- error
526 int USBRL_API
usb_relay_device_close_all_relay_channel(intptr_t hHandle
)
528 struct usbrelay_internal_s
*p
= (struct usbrelay_internal_s
*)hHandle
;
531 return rel_onoff( p
->usbh
, 0, -(int)p
->urdi
.type
);
534 /** Get status of all relays on the device
535 Status bits: one bit indicate a relay status.
536 bit 0/1/2/3/4/5/6/7/8 indicate channel 1/2/3/4/5/6/7/8 status
537 1 -- means ON, 0 -- means OFF.
538 @returns: 0 -- success; 1 -- error
540 int USBRL_API
usb_relay_device_get_status(intptr_t hHandle
, unsigned int *status
)
542 struct usbrelay_internal_s
*p
= (struct usbrelay_internal_s
*)hHandle
;
546 err
= rel_read_status_raw(p
->usbh
, NULL
);
548 printerr("Error reading data: %s\n", usbErrorMessage(err
));
552 *status
= (unsigned char)err
;
556 /************ Added ****************/
558 /** Get the library (dll) version
559 @return Lower 16 bits: the library version. Higher bits: undefined, ignore.
560 @note The original DLL does not have this function!
562 int USBRL_API
usb_relay_device_lib_version(void)
564 return (int)(MY_VERSION
);
568 The following functions are for non-native callers, to avoid fumbling with C structs.
569 Native C/C++ callers do not need to use these.
570 The ptr_usb_relay_device_info arg is pointer to struct usb_relay_device_info, cast to intptr_t, void*, etc.
573 /* Return next info struct pointer in the list returned by usb_relay_device_enumerate() */
574 intptr_t USBRL_API
usb_relay_device_next_dev(intptr_t ptr_usb_relay_device_info
)
576 if (!ptr_usb_relay_device_info
)
578 return (intptr_t)(void*)((pusb_relay_device_info_t
)ptr_usb_relay_device_info
)->next
;
581 /* Get number of relay channels on the device */
582 int USBRL_API
usb_relay_device_get_num_relays(intptr_t ptr_usb_relay_device_info
)
584 if (!ptr_usb_relay_device_info
)
586 return (int)((pusb_relay_device_info_t
)ptr_usb_relay_device_info
)->type
;
589 /* Get the ID string of the device. Returns pointer to const C string (1-byte, 0-terminated) */
590 intptr_t USBRL_API
usb_relay_device_get_id_string(intptr_t ptr_usb_relay_device_info
)
592 if (!ptr_usb_relay_device_info
)
594 return (intptr_t)(void const *)((pusb_relay_device_info_t
)ptr_usb_relay_device_info
)->serial_number
;
597 /* Get status of all relays on the device.
598 * @return Bitmask of all relay channels state, if the value > 0. Negative values mean error.
599 bit 0/1/2/3/4/5/6/7/8 indicate channel 1/2/3/4/5/6/7/8 status
600 Each bit value 1 means ON, 0 means OFF.
601 * @note This is same as usb_relay_device_get_status, but without dereferencing pointers.
603 int USBRL_API
usb_relay_device_get_status_bitmap(intptr_t hHandle
)
606 int err
= usb_relay_device_get_status(hHandle
, &st
);
609 return (err
> 0) ? (-err
) : err
;