2 * USB HID relays API library
5 * 12-jan-2015 pa01 Win32 version
8 #define MY_VERSION 0x02
10 #if defined (WIN32) || defined (_WIN32)
12 // Windows 32 or 64 bit
13 #include "targetver.h"
14 #define WIN32_EXTRALEAN // Exclude rarely-used stuff from Windows headers
17 #define USBRL_API __declspec(dllexport)
18 #define USBRL_CALL _cdecl
19 #define snprintf _snprintf
23 #include "usb_relay_device.h"
25 #if USBRELAY_LIB_VER != MY_VERSION
26 #error "Oops. Wrong version of usb_relay_device.h"
29 #include "usb_relay_hw.h"
35 //#define dbgprintf(fmt, ...) fprintf(stderr, fmt, __VA_ARGS__)
36 //#define dbgprintf(fmt, ...) printf(fmt, __VA_ARGS__)
37 #define dbgprintf(fmt, ...) __noop(fmt, __VA_ARGS__)
38 //#define printerr(fmt, ...) fprintf(stderr, fmt, __VA_ARGS__)
39 //#define printerr(fmt, ...) printf(fmt, __VA_ARGS__)
40 #define printerr(fmt, ...) __noop(fmt, __VA_ARGS__)
46 struct usbrelay_internal_s
{
47 struct usb_relay_device_info urdi
; //public part
49 USBDEVHANDLE usbh
; // handle
53 // struct for enum context
55 struct usbrelay_internal_s
*head
, *tail
;
62 const char *g_dummyPath
= "NOTHING"; // passing dev.path to client not implemented, I return this as path.
64 static const char *usbErrorMessage(int errCode
)
66 static char buffer
[80];
68 if ( errCode
!= USBHID_ERR_UNKNOWN
) {
69 usbhidStrerror_r(errCode
, buffer
, sizeof(buffer
));
71 if ( 0 == buffer
[0] ) {
72 snprintf(buffer
, sizeof(buffer
), "Unknown error (%d)", errCode
);
77 // Read state of all relays
78 // @return bit mask of all relays (R1->bit 0, R2->bit 1 ...) or -1 on error
79 static int rel_read_status_raw(USBDEVHANDLE dev
, void *raw_data
)
84 int len
= 8 + 1; /* report id 1 byte + 8 bytes data */
85 memset(buffer
, 0, sizeof(buffer
));
87 err
= usbhidGetReport(dev
, reportnum
, buffer
, &len
);
89 printerr("error reading status: %s\n", usbErrorMessage(err
));
93 if ( len
!= 9 || buffer
[0] != reportnum
) {
94 printerr("ERROR: wrong HID report returned! %d\n", len
);
99 /* copy raw report data */
100 memcpy( raw_data
, buffer
, len
);
103 return (unsigned char)buffer
[8]; /* byte of relay states */
106 // Turn relay on/off.
107 // @param relaynum: positive (1-N): one relay index, negative: all, -num = number of relays
108 // @returns 0 ok, else error
109 static int rel_onoff( USBDEVHANDLE dev
, int is_on
, int relaynum
)
111 unsigned char buffer
[10];
113 unsigned char cmd1
, cmd2
, mask
, maskval
;
115 if ( relaynum
< 0 && (-relaynum
) <= 8 ) {
120 maskval
= (unsigned char)( (1U << (-relaynum
)) - 1 );
126 if ( relaynum
<= 0 || relaynum
> 8 ) {
127 printerr("Relay number must be 1-8\n");
130 mask
= (unsigned char)(1U << relaynum
);
131 cmd2
= (unsigned char)relaynum
;
141 memset(buffer
, 0, sizeof(buffer
));
142 buffer
[0] = 0; /* report # */
145 if((err
= usbhidSetReport(dev
, (void*)buffer
, 9)) != 0) {
146 printerr("Error writing data: %s\n", usbErrorMessage(err
));
150 // Read back & verify
151 err
= rel_read_status_raw(dev
, NULL
);
153 printerr("Error read back: %s\n", usbErrorMessage(err
));
158 if (err
!= maskval
) {
159 printerr("Error: failed to set relay %u %s\n", relaynum
, is_on
? "ON":"OFF");
169 /** Initialize the USB Relay Library
170 @returns: This function returns 0 on success and -1 on error.
172 int USBRL_API
usb_relay_init(void)
177 /** Finalize the USB Relay Library.
178 This function frees all of the static data associated with USB Relay Library.
179 It should be called at the end of execution to avoid memory leaks.
180 @returns: This function returns 0 on success and -1 on error.
182 int USBRL_API
usb_relay_exit(void)
187 // Enum function for building list of devices
189 int enumfunc(USBDEVHANDLE usbh
, void *context
)
191 // static const char vendorName[] = USB_RELAY_VENDOR_NAME;
192 static const char productName
[] = USB_RELAY_NAME_PREF
;
194 char buffer
[128*sizeof(short)]; // max USB string is 128 UTF-16 chars
197 struct usbrelay_internal_s
*q
;
198 struct enumctx_s
*ectx
= (struct enumctx_s
*)context
;
200 //NOTE: Ignore vendor string. This is against ObjDev rules, restore the check if needed!
202 err
= usbhidGetProductString(usbh
, buffer
, sizeof(buffer
));
208 i
= (int)strlen(buffer
);
209 if ( i
!= strlen(productName
) + 1 )
214 /* the last char of ProductString is number of relays */
215 num
= (int)(buffer
[i
- 1]) - (int)'0';
218 if ( 0 != strcmp( buffer
, productName
) )
223 if ( num
<= 0 || num
> 8 )
225 dbgprintf("Unknown relay device? num relays=%d\n", num
);
229 /* Check the unique ID: USB_RELAY_ID_STR_LEN bytes at offset 1 (just after the report id) */
230 err
= rel_read_status_raw(usbh
, buffer
);
233 dbgprintf("Error reading report 0: %s\n", usbErrorMessage(err
));
237 for (i
= 1; i
<= USB_RELAY_ID_STR_LEN
; i
++)
239 unsigned char x
= (unsigned char)buffer
[i
];
240 if (x
<= 0x20 || x
>= 0x7F)
242 dbgprintf("Bad usbrelay ID string!\n");
247 if( buffer
[USB_RELAY_ID_STR_LEN
+ 1] != 0 )
249 dbgprintf("Bad usbrelay ID string!\n");
253 dbgprintf("Device %s%d found: ID=[%5s]\n", productName
, num
, &buffer
[1]);
255 // allocate & save info
256 q
= (struct usbrelay_internal_s
*)calloc(1, sizeof(struct usbrelay_internal_s
));
258 dbgprintf("Malloc err\n");
259 goto next
; //$$$ revise
261 /* keep this device, continue */
263 memcpy(q
->idstr
, &buffer
[1], USB_RELAY_ID_STR_LEN
);
264 q
->urdi
.type
= num
; // enum = number of relays
265 q
->urdi
.serial_number
= &q
->idstr
[0];
266 q
->urdi
.device_path
= (char*)g_dummyPath
;
272 ectx
->tail
->urdi
.next
= (pusb_relay_device_info_t
)q
;
279 /* Continue search */
280 usbhidCloseDevice(usbh
);
284 // Enum function for open one device by ID
286 int enumOpenfunc(USBDEVHANDLE usbh
, void *context
)
288 // static const char vendorName[] = USB_RELAY_VENDOR_NAME;
289 static const char productName
[] = USB_RELAY_NAME_PREF
;
291 char buffer
[128*sizeof(short)]; // max USB string is 128 UTF-16 chars
294 struct enumctx_s
*ectx
= (struct enumctx_s
*)context
;
295 struct usbrelay_internal_s
*q
= ectx
->head
;
297 //NOTE: Ignore vendor string. This is against ObjDev rules, restore the check if needed!
299 err
= usbhidGetProductString(usbh
, buffer
, sizeof(buffer
));
305 i
= (int)strlen(buffer
);
306 if ( i
!= strlen(productName
) + 1 )
311 /* the last char of ProductString is number of relays */
312 num
= (int)(buffer
[i
- 1]) - (int)'0';
315 if ( 0 != strcmp( buffer
, productName
) )
320 if ( num
<= 0 || num
> 8 )
322 dbgprintf("Unknown relay device? num relays=%d\n", num
);
326 /* Check the unique ID: USB_RELAY_ID_STR_LEN bytes at offset 1 (just after the report id) */
327 err
= rel_read_status_raw(usbh
, buffer
);
330 dbgprintf("Error reading report 0: %s\n", usbErrorMessage(err
));
334 for (i
= 1; i
<= USB_RELAY_ID_STR_LEN
; i
++)
336 unsigned char x
= (unsigned char)buffer
[i
];
337 if (x
<= 0x20 || x
>= 0x7F)
339 dbgprintf("Bad usbrelay ID string!\n");
344 if( buffer
[USB_RELAY_ID_STR_LEN
+ 1] != 0 )
346 dbgprintf("Bad usbrelay ID string!\n");
350 dbgprintf("Device %s%d found: ID=[%5s]\n", productName
, num
, &buffer
[1]);
352 if ( 0 == memcmp( q
->idstr
, &buffer
[1], USB_RELAY_ID_STR_LEN
) ) {
354 q
->urdi
.type
= num
; // enum = number of relays
355 q
->urdi
.serial_number
= &q
->idstr
[0];
356 q
->urdi
.device_path
= (char*)g_dummyPath
;
362 /* Continue search */
363 usbhidCloseDevice(usbh
);
367 /** Enumerate the USB Relay Devices.*/
368 pusb_relay_device_info_t USBRL_API
usb_relay_device_enumerate(void)
370 struct enumctx_s ectx
;
372 memset(&ectx
, 0, sizeof(ectx
));
373 ret
= usbhidEnumDevices(USB_CFG_VENDOR_ID
, USB_CFG_DEVICE_ID
,
377 return (pusb_relay_device_info_t
)ectx
.head
;
381 /** Free an enumeration Linked List*/
382 void USBRL_API
usb_relay_device_free_enumerate(struct usb_relay_device_info
*dilist
)
384 struct usbrelay_internal_s
*p
= (struct usbrelay_internal_s
*)dilist
;
387 struct usbrelay_internal_s
*q
= (struct usbrelay_internal_s
*)((pusb_relay_device_info_t
)p
)->next
;
388 if (p
->usbh
&& ((USBDEVHANDLE
)(-1)) != p
->usbh
) {
389 usbhidCloseDevice(p
->usbh
);
399 /** Open device by serial number
400 serial_number == NULL is valid and means any one device.
401 @return: This function returns a valid handle to the device on success or NULL on failure.
402 Example: usb_relay_device_open_with_serial_number("abcde", 5) */
403 intptr_t USBRL_API
usb_relay_device_open_with_serial_number(const char *serial_number
, unsigned len
)
405 struct enumctx_s ectx
;
407 struct usbrelay_internal_s
*q
;
408 memset(&ectx
, 0, sizeof(ectx
));
410 if (serial_number
&& len
!= USB_RELAY_ID_STR_LEN
) {
411 printerr("Specified invalid str id length: %u", len
);
415 q
= ectx
.head
= calloc(1, sizeof(*ectx
.head
));
419 memcpy(q
->idstr
, serial_number
, len
);
421 ret
= usbhidEnumDevices(USB_CFG_VENDOR_ID
, USB_CFG_DEVICE_ID
,
425 goto ret_err
; // error during enum
427 if (ectx
.numdevs
== 0 || q
->usbh
== 0) {
428 goto ret_err
; // not found
431 q
->urdi
.next
= (void*)q
; // mark this element as standalone
439 /** Open a USB relay device
440 @return: This function returns a valid handle to the device on success or NULL on failure.
442 intptr_t USBRL_API
usb_relay_device_open(struct usb_relay_device_info
*device_info
)
444 struct usbrelay_internal_s
*p
= (struct usbrelay_internal_s
*)device_info
;
447 if ( (uintptr_t)p
->usbh
== 0 || (uintptr_t)p
->usbh
== (uintptr_t)-1 )
450 return (uintptr_t)device_info
;
453 /** Close a USB relay device*/
454 void USBRL_API
usb_relay_device_close(intptr_t hHandle
)
456 struct usbrelay_internal_s
*p
= (struct usbrelay_internal_s
*)hHandle
;
457 if ( 0 == hHandle
|| ((intptr_t)-1) == hHandle
)
460 if ( (void*)(p
->urdi
.next
) == (void*)p
) {
461 // This was made by usb_relay_device_open_with_serial_number() so free it now:
462 if ( p
->usbh
&& ((intptr_t)-1) != (intptr_t)(p
->usbh
)) {
463 usbhidCloseDevice(p
->usbh
);
469 // Else this can be in the list, don't do anything.
472 /** Turn ON a relay channel on the USB-Relay-Device
473 @param index -- which channel your want to open
474 @param hHandle -- which usb relay device your want to operate
475 @returns: 0 -- success; 1 -- error; 2 -- index is outnumber the number of the usb relay device
477 int USBRL_API
usb_relay_device_open_one_relay_channel(intptr_t hHandle
, int index
)
479 struct usbrelay_internal_s
*p
= (struct usbrelay_internal_s
*)hHandle
;
482 if ( index
<= 0 || index
> (int)p
->urdi
.type
)
484 return rel_onoff( p
->usbh
, 1, index
);
487 /** Turn ON all relay channels on the USB-Relay-Device
488 @param hHandle -- which usb relay device your want to operate
489 @returns: 0 -- success; 1 -- error
491 int USBRL_API
usb_relay_device_open_all_relay_channel(intptr_t hHandle
)
493 struct usbrelay_internal_s
*p
= (struct usbrelay_internal_s
*)hHandle
;
496 return rel_onoff( p
->usbh
, 1, -(int)p
->urdi
.type
);
499 /** Turn OFF a relay channel on the USB-Relay-Device
500 @param index -- which channel your want to close
501 @param hHandle -- which usb relay device your want to operate
502 @returns: 0 -- success; 1 -- error; 2 -- index is outnumber the number of the usb relay device
504 int USBRL_API
usb_relay_device_close_one_relay_channel(intptr_t hHandle
, int index
)
506 struct usbrelay_internal_s
*p
= (struct usbrelay_internal_s
*)hHandle
;
509 if ( index
<= 0 || index
> (int)p
->urdi
.type
)
511 return rel_onoff( p
->usbh
, 0, index
);
514 /** Turn OFF all relay channels on the USB-Relay-Device
515 @param hHandle -- which usb relay device your want to operate
516 @returns 0 -- success; 1 -- error
518 int USBRL_API
usb_relay_device_close_all_relay_channel(intptr_t hHandle
)
520 struct usbrelay_internal_s
*p
= (struct usbrelay_internal_s
*)hHandle
;
523 return rel_onoff( p
->usbh
, 0, -(int)p
->urdi
.type
);
526 /** Get status of all relays on the device
527 Status bits: one bit indicate a relay status.
528 bit 0/1/2/3/4/5/6/7/8 indicate channel 1/2/3/4/5/6/7/8 status
529 1 -- means ON, 0 -- means OFF.
530 @returns: 0 -- success; 1 -- error
532 int USBRL_API
usb_relay_device_get_status(intptr_t hHandle
, unsigned int *status
)
534 struct usbrelay_internal_s
*p
= (struct usbrelay_internal_s
*)hHandle
;
538 err
= rel_read_status_raw(p
->usbh
, NULL
);
540 printerr("Error reading data: %s\n", usbErrorMessage(err
));
544 *status
= (unsigned char)err
;
548 /** Return lib version
550 int USBRL_API
usb_relay_device_lib_version(void)
552 return (int)(MY_VERSION
);