Windows DLL src added, vc2008
[usb-relay-hid.git] / lib / usb_relay_lib.c
1 /**
2 * USB HID relays API library
3 * http://git.io/bGcxrQ
4 *
5 * 12-jan-2015 pa01 Win32 version
6 */
7
8 #define MY_VERSION 0x02
9
10 #if defined (WIN32) || defined (_WIN32)
11
12 // Windows 32 or 64 bit
13 #include "targetver.h"
14 #define WIN32_EXTRALEAN // Exclude rarely-used stuff from Windows headers
15 #include <windows.h>
16
17 #define USBRL_API __declspec(dllexport)
18 #define USBRL_CALL _cdecl
19 #define snprintf _snprintf
20
21 #endif //WIN32
22
23 #include "usb_relay_device.h"
24
25 #if USBRELAY_LIB_VER != MY_VERSION
26 #error "Oops. Wrong version of usb_relay_device.h"
27 #endif
28
29 #include "usb_relay_hw.h"
30 #include "hiddata.h"
31 #include <stdio.h>
32 #include <string.h>
33 #include <stdlib.h>
34
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__)
41
42 #ifdef __cplusplus
43 extern "C" {
44 #endif
45
46 struct usbrelay_internal_s {
47 struct usb_relay_device_info urdi; //public part
48 // Private part:
49 USBDEVHANDLE usbh; // handle
50 char idstr[8];
51 };
52
53 // struct for enum context
54 struct enumctx_s {
55 struct usbrelay_internal_s *head, *tail;
56 int numdevs;
57 int status;
58 };
59
60 // Globals
61
62 const char *g_dummyPath = "NOTHING"; // passing dev.path to client not implemented, I return this as path.
63
64 static const char *usbErrorMessage(int errCode)
65 {
66 static char buffer[80];
67 buffer[0] = 0;
68 if ( errCode != USBHID_ERR_UNKNOWN ) {
69 usbhidStrerror_r(errCode, buffer, sizeof(buffer));
70 }
71 if ( 0 == buffer[0] ) {
72 snprintf(buffer, sizeof(buffer), "Unknown error (%d)", errCode);
73 }
74 return buffer;
75 }
76
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)
80 {
81 char buffer[10];
82 int err;
83 int reportnum = 0;
84 int len = 8 + 1; /* report id 1 byte + 8 bytes data */
85 memset(buffer, 0, sizeof(buffer));
86
87 err = usbhidGetReport(dev, reportnum, buffer, &len);
88 if ( err ) {
89 printerr("error reading status: %s\n", usbErrorMessage(err));
90 return -1;
91 }
92
93 if ( len != 9 || buffer[0] != reportnum ) {
94 printerr("ERROR: wrong HID report returned! %d\n", len);
95 return -2;
96 }
97
98 if (raw_data) {
99 /* copy raw report data */
100 memcpy( raw_data, buffer, len );
101 }
102
103 return (unsigned char)buffer[8]; /* byte of relay states */
104 }
105
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 )
110 {
111 unsigned char buffer[10];
112 int err = -1;
113 unsigned char cmd1, cmd2, mask, maskval;
114
115 if ( relaynum < 0 && (-relaynum) <= 8 ) {
116 mask = 0xFF;
117 cmd2 = 0;
118 if (is_on) {
119 cmd1 = 0xFE;
120 maskval = (unsigned char)( (1U << (-relaynum)) - 1 );
121 } else {
122 cmd1 = 0xFC;
123 maskval = 0;
124 }
125 } else {
126 if ( relaynum <= 0 || relaynum > 8 ) {
127 printerr("Relay number must be 1-8\n");
128 return 1;
129 }
130 mask = (unsigned char)(1U << relaynum);
131 cmd2 = (unsigned char)relaynum;
132 if (is_on) {
133 cmd1 = 0xFF;
134 maskval = mask;
135 } else {
136 cmd1 = 0xFD;
137 maskval = 0;
138 }
139 }
140
141 memset(buffer, 0, sizeof(buffer));
142 buffer[0] = 0; /* report # */
143 buffer[1] = cmd1;
144 buffer[2] = cmd2;
145 if((err = usbhidSetReport(dev, (void*)buffer, 9)) != 0) {
146 printerr("Error writing data: %s\n", usbErrorMessage(err));
147 return 1;
148 }
149
150 // Read back & verify
151 err = rel_read_status_raw(dev, NULL);
152 if ( err < 0 ) {
153 printerr("Error read back: %s\n", usbErrorMessage(err));
154 return 1;
155 }
156
157 err = err & mask;
158 if (err != maskval) {
159 printerr("Error: failed to set relay %u %s\n", relaynum, is_on ? "ON":"OFF");
160 return 1;
161 }
162
163 return 0;
164 }
165
166
167 // Public functions:
168
169 /** Initialize the USB Relay Library
170 @returns: This function returns 0 on success and -1 on error.
171 */
172 int USBRL_API usb_relay_init(void)
173 {
174 return 0;
175 }
176
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.
181 */
182 int USBRL_API usb_relay_exit(void)
183 {
184 return 0;
185 }
186
187 // Enum function for building list of devices
188 static
189 int enumfunc(USBDEVHANDLE usbh, void *context)
190 {
191 // static const char vendorName[] = USB_RELAY_VENDOR_NAME;
192 static const char productName[] = USB_RELAY_NAME_PREF;
193 int err;
194 char buffer[128*sizeof(short)]; // max USB string is 128 UTF-16 chars
195 int num = 0;
196 int i;
197 struct usbrelay_internal_s *q;
198 struct enumctx_s *ectx = (struct enumctx_s *)context;
199
200 //NOTE: Ignore vendor string. This is against ObjDev rules, restore the check if needed!
201
202 err = usbhidGetProductString(usbh, buffer, sizeof(buffer));
203 if (err)
204 {
205 goto next;
206 }
207
208 i = (int)strlen(buffer);
209 if ( i != strlen(productName) + 1 )
210 {
211 goto next;
212 }
213
214 /* the last char of ProductString is number of relays */
215 num = (int)(buffer[i - 1]) - (int)'0';
216 buffer[i - 1] = 0;
217
218 if ( 0 != strcmp( buffer, productName) )
219 {
220 goto next;
221 }
222
223 if ( num <= 0 || num > 8 )
224 {
225 dbgprintf("Unknown relay device? num relays=%d\n", num);
226 goto next;
227 }
228
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);
231 if( err < 0 )
232 {
233 dbgprintf("Error reading report 0: %s\n", usbErrorMessage(err));
234 goto next;
235 }
236
237 for (i = 1; i <= USB_RELAY_ID_STR_LEN; i++)
238 {
239 unsigned char x = (unsigned char)buffer[i];
240 if (x <= 0x20 || x >= 0x7F)
241 {
242 dbgprintf("Bad usbrelay ID string!\n");
243 goto next;
244 }
245 }
246
247 if( buffer[USB_RELAY_ID_STR_LEN + 1] != 0 )
248 {
249 dbgprintf("Bad usbrelay ID string!\n");
250 goto next;
251 }
252
253 dbgprintf("Device %s%d found: ID=[%5s]\n", productName, num, &buffer[1]);
254
255 // allocate & save info
256 q = (struct usbrelay_internal_s *)calloc(1, sizeof(struct usbrelay_internal_s));
257 if (!q) {
258 dbgprintf("Malloc err\n");
259 goto next; //$$$ revise
260 }
261 /* keep this device, continue */
262 q->usbh = usbh;
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;
267
268 if (!ectx->head) {
269 ectx->head = q;
270 ectx->tail =q;
271 } else {
272 ectx->tail->urdi.next = (pusb_relay_device_info_t)q;
273 }
274
275 ++ectx->numdevs;
276 return 1;
277
278 next:
279 /* Continue search */
280 usbhidCloseDevice(usbh);
281 return 1;
282 }
283
284 // Enum function for open one device by ID
285 static
286 int enumOpenfunc(USBDEVHANDLE usbh, void *context)
287 {
288 // static const char vendorName[] = USB_RELAY_VENDOR_NAME;
289 static const char productName[] = USB_RELAY_NAME_PREF;
290 int err;
291 char buffer[128*sizeof(short)]; // max USB string is 128 UTF-16 chars
292 int num = 0;
293 int i;
294 struct enumctx_s *ectx = (struct enumctx_s *)context;
295 struct usbrelay_internal_s *q = ectx->head;
296
297 //NOTE: Ignore vendor string. This is against ObjDev rules, restore the check if needed!
298
299 err = usbhidGetProductString(usbh, buffer, sizeof(buffer));
300 if (err)
301 {
302 goto next;
303 }
304
305 i = (int)strlen(buffer);
306 if ( i != strlen(productName) + 1 )
307 {
308 goto next;
309 }
310
311 /* the last char of ProductString is number of relays */
312 num = (int)(buffer[i - 1]) - (int)'0';
313 buffer[i - 1] = 0;
314
315 if ( 0 != strcmp( buffer, productName) )
316 {
317 goto next;
318 }
319
320 if ( num <= 0 || num > 8 )
321 {
322 dbgprintf("Unknown relay device? num relays=%d\n", num);
323 goto next;
324 }
325
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);
328 if( err < 0 )
329 {
330 dbgprintf("Error reading report 0: %s\n", usbErrorMessage(err));
331 goto next;
332 }
333
334 for (i = 1; i <= USB_RELAY_ID_STR_LEN; i++)
335 {
336 unsigned char x = (unsigned char)buffer[i];
337 if (x <= 0x20 || x >= 0x7F)
338 {
339 dbgprintf("Bad usbrelay ID string!\n");
340 goto next;
341 }
342 }
343
344 if( buffer[USB_RELAY_ID_STR_LEN + 1] != 0 )
345 {
346 dbgprintf("Bad usbrelay ID string!\n");
347 goto next;
348 }
349
350 dbgprintf("Device %s%d found: ID=[%5s]\n", productName, num, &buffer[1]);
351
352 if ( 0 == memcmp( q->idstr, &buffer[1], USB_RELAY_ID_STR_LEN) ) {
353 q->usbh = usbh;
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;
357 ++ectx->numdevs;
358 return 0;
359 }
360
361 next:
362 /* Continue search */
363 usbhidCloseDevice(usbh);
364 return 1;
365 }
366
367 /** Enumerate the USB Relay Devices.*/
368 pusb_relay_device_info_t USBRL_API usb_relay_device_enumerate(void)
369 {
370 struct enumctx_s ectx;
371 int ret;
372 memset(&ectx, 0, sizeof(ectx));
373 ret = usbhidEnumDevices(USB_CFG_VENDOR_ID, USB_CFG_DEVICE_ID,
374 (void*)&ectx,
375 enumfunc);
376
377 return (pusb_relay_device_info_t)ectx.head;
378 }
379
380
381 /** Free an enumeration Linked List*/
382 void USBRL_API usb_relay_device_free_enumerate(struct usb_relay_device_info *dilist)
383 {
384 struct usbrelay_internal_s *p = (struct usbrelay_internal_s *)dilist;
385
386 while (p) {
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);
390 p->usbh = 0;
391 }
392 free(p);
393 p = q;
394 }
395
396 return;
397 }
398
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)
404 {
405 struct enumctx_s ectx;
406 int ret;
407 struct usbrelay_internal_s *q;
408 memset(&ectx, 0, sizeof(ectx));
409
410 if (serial_number && len != USB_RELAY_ID_STR_LEN) {
411 printerr("Specified invalid str id length: %u", len);
412 return (intptr_t)0;
413 }
414
415 q = ectx.head = calloc(1, sizeof(*ectx.head));
416 if (!q)
417 return (intptr_t)0;
418
419 memcpy(q->idstr, serial_number, len);
420
421 ret = usbhidEnumDevices(USB_CFG_VENDOR_ID, USB_CFG_DEVICE_ID,
422 (void*)&ectx,
423 enumfunc);
424 if (ret != 0)
425 goto ret_err; // error during enum
426
427 if (ectx.numdevs == 0 || q->usbh == 0) {
428 goto ret_err; // not found
429 }
430
431 q->urdi.next = (void*)q; // mark this element as standalone
432 return (intptr_t)q;
433
434 ret_err:
435 free(q);
436 return (intptr_t)0;
437 }
438
439 /** Open a USB relay device
440 @return: This function returns a valid handle to the device on success or NULL on failure.
441 */
442 intptr_t USBRL_API usb_relay_device_open(struct usb_relay_device_info *device_info)
443 {
444 struct usbrelay_internal_s *p = (struct usbrelay_internal_s *)device_info;
445 if (!device_info)
446 return 0;
447 if ( (uintptr_t)p->usbh == 0 || (uintptr_t)p->usbh == (uintptr_t)-1 )
448 return 0;
449 //$$$ validate more
450 return (uintptr_t)device_info;
451 }
452
453 /** Close a USB relay device*/
454 void USBRL_API usb_relay_device_close(intptr_t hHandle)
455 {
456 struct usbrelay_internal_s *p = (struct usbrelay_internal_s *)hHandle;
457 if ( 0 == hHandle || ((intptr_t)-1) == hHandle )
458 return;
459
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);
464 p->usbh = 0;
465 }
466 p->urdi.next = NULL;
467 free( (void*)p );
468 }
469 // Else this can be in the list, don't do anything.
470 }
471
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
476 */
477 int USBRL_API usb_relay_device_open_one_relay_channel(intptr_t hHandle, int index)
478 {
479 struct usbrelay_internal_s *p = (struct usbrelay_internal_s *)hHandle;
480 if (!p)
481 return 1;
482 if ( index <= 0 || index > (int)p->urdi.type )
483 return 2;
484 return rel_onoff( p->usbh, 1, index);
485 }
486
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
490 */
491 int USBRL_API usb_relay_device_open_all_relay_channel(intptr_t hHandle)
492 {
493 struct usbrelay_internal_s *p = (struct usbrelay_internal_s *)hHandle;
494 if (!p)
495 return 1;
496 return rel_onoff( p->usbh, 1, -(int)p->urdi.type );
497 }
498
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
503 */
504 int USBRL_API usb_relay_device_close_one_relay_channel(intptr_t hHandle, int index)
505 {
506 struct usbrelay_internal_s *p = (struct usbrelay_internal_s *)hHandle;
507 if (!p)
508 return 1;
509 if ( index <= 0 || index > (int)p->urdi.type )
510 return 2;
511 return rel_onoff( p->usbh, 0, index);
512 }
513
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
517 */
518 int USBRL_API usb_relay_device_close_all_relay_channel(intptr_t hHandle)
519 {
520 struct usbrelay_internal_s *p = (struct usbrelay_internal_s *)hHandle;
521 if (!p)
522 return 1;
523 return rel_onoff( p->usbh, 0, -(int)p->urdi.type );
524 }
525
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
531 */
532 int USBRL_API usb_relay_device_get_status(intptr_t hHandle, unsigned int *status)
533 {
534 struct usbrelay_internal_s *p = (struct usbrelay_internal_s *)hHandle;
535 int err;
536 if (!p)
537 return 1;
538 err = rel_read_status_raw(p->usbh, NULL);
539 if ( err < 0 ) {
540 printerr("Error reading data: %s\n", usbErrorMessage(err));
541 return 1;
542 }
543
544 *status = (unsigned char)err;
545 return 0;
546 }
547
548 /** Return lib version
549 */
550 int USBRL_API usb_relay_device_lib_version(void)
551 {
552 return (int)(MY_VERSION);
553 }
554
555 #ifdef __cplusplus
556 }
557 #endif
558