7 #include <libusb-1.0/libusb.h>
9 #define USB_TIMEOUT 10000
11 #define ID_VENDOR 0x18ef
12 #define ID_PRODUCT 0xe015
14 #define EXT_TO_SEC(ext) ((1 << ((ext & 0xf0) >> 4)) * (ext & 0x0f) * 0.25)
18 /* Not in all libusb-1.0 versions, so we have to roll our own :-( */
19 static char * usb_strerror(int e
)
21 static char unknerr
[256];
27 return "Input/output error";
28 case LIBUSB_ERROR_INVALID_PARAM
:
29 return "Invalid parameter";
30 case LIBUSB_ERROR_ACCESS
:
31 return "Access denied (insufficient permissions)";
32 case LIBUSB_ERROR_NO_DEVICE
:
33 return "No such device (it may have been disconnected)";
34 case LIBUSB_ERROR_NOT_FOUND
:
35 return "Entity not found";
36 case LIBUSB_ERROR_BUSY
:
37 return "Resource busy";
38 case LIBUSB_ERROR_TIMEOUT
:
39 return "Operation timed out";
40 case LIBUSB_ERROR_OVERFLOW
:
42 case LIBUSB_ERROR_PIPE
:
44 case LIBUSB_ERROR_INTERRUPTED
:
45 return "System call interrupted (perhaps due to signal)";
46 case LIBUSB_ERROR_NO_MEM
:
47 return "Insufficient memory";
48 case LIBUSB_ERROR_NOT_SUPPORTED
:
49 return "Operation not supported or unimplemented on this platform";
50 case LIBUSB_ERROR_OTHER
:
53 snprintf(unknerr
, sizeof(unknerr
), "Unknown error code %d / 0x%02x", e
, e
);
57 libusb_device_handle
*fs20pcs_find() {
58 libusb_device_handle
*devh
= NULL
;
64 cnt
= libusb_get_device_list(NULL
, &list
);
66 fprintf(stderr
, "Can't get USB device list: %d\n", (int)cnt
);
70 for (i
= 0; i
< cnt
; i
++){
71 struct libusb_device_descriptor desc
;
73 err
= libusb_get_device_descriptor(list
[i
], &desc
);
77 if ((desc
.idVendor
== ID_VENDOR
) && (desc
.idProduct
== ID_PRODUCT
)) {
78 libusb_device
*dev
= list
[i
];
80 err
= libusb_open(dev
, &devh
);
82 fprintf(stderr
, "Can't open device: %s\n", usb_strerror(err
));
86 err
= libusb_detach_kernel_driver(devh
, 0);
87 if ((err
!= 0) && (err
!= LIBUSB_ERROR_NOT_FOUND
)) {
88 fprintf(stderr
, "Can't detach kernel driver: %s\n", usb_strerror(err
));
92 err
= libusb_claim_interface(devh
, 0);
94 fprintf(stderr
, "Can't claim interface: %s\n", usb_strerror(err
));
106 int fs20pcs_send(libusb_device_handle
*devh
, unsigned char* send_data
)
108 unsigned char recv_data
[5] = {0x00, 0x00, 0x00, 0x00, 0x00};
114 err
= libusb_interrupt_transfer(devh
, 0x01, send_data
, 11, &cnt
, USB_TIMEOUT
);
116 fprintf(stderr
, "Can't send data: %s\n", usb_strerror(err
));
120 err
= libusb_interrupt_transfer(devh
, 0x81, recv_data
, sizeof(recv_data
), &cnt
, USB_TIMEOUT
);
122 fprintf(stderr
, "Can't receive data: %s\n", usb_strerror(err
));
126 if ((recv_data
[0] != 0x02) ||
127 (recv_data
[1] != 0x03) ||
128 (recv_data
[2] != 0xa0)) {
129 fprintf(stderr
, "Unexpected response: ");
130 for (i
= 0; i
< cnt
; i
++) {
131 fprintf(stderr
, "0x%02x ", recv_data
[i
]);
133 fprintf(stderr
, "\n");
140 switch(recv_data
[3]) {
145 printf("Firmware: V%d.%d", ((recv_data
[4] & 0xf0) >> 4) & 0x0f, recv_data
[4] & 0x0f);
148 printf("Unknown command");
152 printf("Wrong length");
156 printf("Aborted sending long press");
159 printf("Nothing to stop");
162 printf("Unknown response: 0x%02x 0x%02x", recv_data
[3], recv_data
[4]);
172 unsigned char *parse_housecode(char *hc
)
174 static unsigned char housecode
[2];
183 memset(housecode
, 0, sizeof(housecode
));
187 if (strncmp(hc
, "0x", 2)) {
188 fprintf(stderr
, "Not a 2 byte hexstring: %s\n", hc
);
192 val
= strtoul(hc
, &ep
, 16);
194 fprintf(stderr
, "Not a 2 byte hexstring: %s\n", hc
);
199 memset(hc_fixed
, 0, sizeof(hc_fixed
));
200 for (i
= 0; i
< 8; i
++) {
201 if ((hc
[i
] < '1') || (hc
[i
] > '4')) {
202 fprintf(stderr
, "Not a valid ELV housecode: %s\n", hc
);
205 hc_fixed
[i
] = hc
[i
] - 1;
206 val
= strtoul(hc_fixed
, &ep
, 4);
209 fprintf(stderr
, "Can't parse fixed ELV housecode: %s\n", hc_fixed
);
215 fprintf(stderr
, "Housecode has to be in hex (1234, 0x1234) or ELV (12341234) format!\n");
220 housecode
[0] = (val
& 0xff00) >> 8;
221 housecode
[1] = (val
& 0xff);
226 int parse_addr(char *ad
)
239 if (strncmp(ad
, "0x", 2)) {
240 memset(ad_fixed
, 0, sizeof(ad_fixed
));
241 for (i
= 0; i
< 4; i
++) {
242 if ((ad
[i
] < '1') || (ad
[i
] > '4')) {
243 fprintf(stderr
, "Not a valid ELV address: %s\n", ad
);
246 ad_fixed
[i
] = ad
[i
] - 1;
247 val
= strtoul(ad_fixed
, &ep
, 4);
250 fprintf(stderr
, "Can't parse fixed ELV housecode: %s\n", ad_fixed
);
258 val
= strtoul(ad
, &ep
, 16);
260 fprintf(stderr
, "Not a 1 byte hexstring: %s\n", ad
);
265 fprintf(stderr
, "Address has to be in hex (01, 0x01) or ELV (1112) format!\n");
275 int parse_command(char *cmd
)
284 if (!strcasecmp(cmd
, "off")) {
286 } else if (!strcasecmp(cmd
, "on")) {
288 } else if (!strcasecmp(cmd
, "toggle")) {
290 } else if (!strcasecmp(cmd
, "dimup")) {
292 } else if (!strcasecmp(cmd
, "dimdown")) {
294 } else if (!strcasecmp(cmd
, "on-for-timer")) {
297 val
= strtoul(cmd
, &ep
, 16);
299 fprintf(stderr
, "Not a 1 byte hexstring or alias: %s\n", cmd
);
303 command
= val
& 0xff;
309 int parse_extension(char *ext
)
317 if ((ext
[strlen(ext
)-1] == 's') ||
318 (ext
[strlen(ext
)-1] == 'S')) {
325 t
= strtod(ext
, &ep
);
326 if ((*ep
!= 's') && (*ep
!= 'S')) {
327 fprintf(stderr
, "Not a valid time in seconds: %s\n", ext
);
331 /* time = 2^(high nibble) * (low nibble) * 0.25 , high may only be 12 */
334 printf("t: %f\n", t
);
337 for(i
= 1; i
<= 0xf; i
++) {
344 printf("i: 0x%01x\n", i
);
349 printf("h: %f\n", h
);
354 printf("l: %f\n", l
);
364 printf("d: %f\n", d
);
369 printf("Current best match!\n");
378 printf("Got: %f, high: %01x, low: %01x\n", t
, high
, low
);
381 if ((high
== 0) && (low
== 0)) {
382 fprintf(stderr
, "Can't find extension byte for %s!\n", ext
);
386 extension
= ((high
& 0xf) << 4) | (low
& 0xf);
390 val
= strtoul(ext
, &ep
, 16);
392 fprintf(stderr
, "Not a 1 byte hexstring or time: %s\n", ext
);
396 extension
= val
& 0xff;
402 void syntax(char *prog
)
404 fprintf(stderr
, "Syntax: %s options\n\n", prog
);
405 fprintf(stderr
, "Possible options:\n");
406 fprintf(stderr
, "\t-V\t\trequest firmware-version\n");
407 fprintf(stderr
, "\t-x\t\tabort sending long press\n");
408 fprintf(stderr
, "\t-h [ELV|hex]\thousecode in ELV- or hex-notation\n");
409 fprintf(stderr
, "The following options need an housecode:\n");
410 fprintf(stderr
, "\t-a [ELV|hex]\tdestination address\n");
411 fprintf(stderr
, "\t-s [hex|alias]\tsend command byte\n");
412 fprintf(stderr
, "\t-e [hex|time]\textension byte for command or time in seconds (e.g. 10s)\n");
413 fprintf(stderr
, "\t-r n\t\trepeat sending n times\n");
414 fprintf(stderr
, "\nCommand bytes (without extension byte):\n");
415 fprintf(stderr
, "hex\t\talias\tdescription\n");
416 fprintf(stderr
, "0x00\t\toff\tswitch off\n");
417 fprintf(stderr
, "0x01-0x10\t\tswitch on dimmed (0x01: 6.25%%, 0x02: 12.5%%, ..., 0x10: 100%%)\n");
418 fprintf(stderr
, "0x11\t\ton\tswitch on\n");
419 fprintf(stderr
, "0x12\t\ttoggle\ttoggle\n");
420 fprintf(stderr
, "0x13\t\tdimup\tdimup\n");
421 fprintf(stderr
, "0x14\t\tdimdown\tdimdown\n");
422 fprintf(stderr
, "0x15\t\t\tdimup, pause, dimdown, pause, ...\n");
423 fprintf(stderr
, "0x16\t\t\tstart/stop programming internal timer\n");
424 fprintf(stderr
, "0x17\t\t\tlearn housecode/address\n");
425 fprintf(stderr
, "0x18\t\t\toff for internal timer, then back on with current level\n");
426 fprintf(stderr
, "0x19\t\t\ton (100%%) for internal timer, then off\n");
427 fprintf(stderr
, "0x1a\t\t\ton (old level) for internal timer, then off\n");
428 fprintf(stderr
, "0x1b\t\t\tfactory reset\n");
429 fprintf(stderr
, "0x1e\t\t\ton (100%%) for internal timer, then old level\n");
430 fprintf(stderr
, "0x1f\t\t\ton (old level) for internal timer, then old state\n");
431 fprintf(stderr
, "\nCommand bytes (with timer as extension byte):\n");
432 fprintf(stderr
, "hex\t\talias\tdescription\n");
433 fprintf(stderr
, "0x20\t\t\tdim down to off while timer is running\n");
434 fprintf(stderr
, "0x21-0x30\t\tdim to (0x01: 6.25%%, 0x02: 12.5%%, ..., 0x10: 100%%) while timer is running\n");
435 fprintf(stderr
, "0x31\t\t\tdim to last level while timer is running\n");
436 fprintf(stderr
, "0x32\t\t\tdim to off, then after timer dim to off\n");
437 fprintf(stderr
, "0x33\t\t\tdimup now and switch off after timer\n");
438 fprintf(stderr
, "0x34\t\t\tdimdown now and switch off after timer\n");
439 fprintf(stderr
, "0x35\t\t\tdimup or dimdown (toggle) and switch off after timer\n");
440 fprintf(stderr
, "0x36\t\t\tprogram internal timer\n");
441 fprintf(stderr
, "0x38\t\t\toff for timer, then back on with current level\n");
442 fprintf(stderr
, "0x39\ton-for-timer\ton (100%%) for timer, then off\n");
443 fprintf(stderr
, "0x3a\t\t\ton (old level) for timer, then off\n");
444 fprintf(stderr
, "0x3c\t\t\tprogram internal ramp-up-time\n");
445 fprintf(stderr
, "0x3d\t\t\tprogram internal ramp-down-time\n");
446 fprintf(stderr
, "0x3e\t\t\ton (100%%) for timer, then old level\n");
447 fprintf(stderr
, "0x3f\t\t\ton (old level) for timer, then old state\n");
457 #define DUPLICATE_ACTION if (action != NO_ACTION) { \
458 fprintf(stderr, "duplicate action specified!\n"); \
459 exit(EXIT_FAILURE); }
461 int main(int argc
, char **argv
)
463 unsigned char send_data
[11];
464 unsigned char *housecode
= NULL
;
465 libusb_device_handle
*devh
= NULL
;
469 int action
= NO_ACTION
;
475 memset(send_data
, 0, sizeof(send_data
));
479 while ((opt
= getopt(argc
, argv
, "Vxs:h:a:r:e:")) != -1) {
483 action
= REQUEST_FIRMWARE
;
487 action
= ABORT_LONG_PRESS
;
491 action
= SEND_COMMAND
;
493 command
= parse_command(optarg
);
495 fprintf(stderr
, "Can't parse command!\n");
499 printf("Got command: %d\n", command
);
503 housecode
= parse_housecode(optarg
);
505 fprintf(stderr
, "Can't parse housecode!\n");
509 printf("Got housecode: 0x%02x%02x\n", housecode
[0], housecode
[1]);
513 addr
= parse_addr(optarg
);
515 fprintf(stderr
, "Can't parse address!\n");
519 printf("Got address: 0x%02x\n", addr
);
523 repeat
= strtoul(optarg
, &ep
, 10);
525 fprintf(stderr
, "Can't parse repeat!\n");
529 printf("Got repeat: %d\n", repeat
);
533 extension
= parse_extension(optarg
);
534 if (extension
== -1) {
535 fprintf(stderr
, "Can't parse extension!\n");
539 printf("Got extension: %d\n", extension
);
552 case REQUEST_FIRMWARE
:
555 printf("Requesting firmware version...\n");
558 case ABORT_LONG_PRESS
:
561 printf("Aborting long press...\n");
565 if (housecode
== NULL
) {
566 fprintf(stderr
, "housecode needed!\n");
570 fprintf(stderr
, "address needed!\n");
577 send_data
[8] = repeat
;
578 printf("Sending 0x%02x 0x%02x (%.2fs) to address %d (hc: 0x%02x%02x) (repeated %d times)\n",
579 command
, extension
, EXT_TO_SEC(extension
), addr
,
580 housecode
[0], housecode
[1],
585 printf("Sending 0x%02x 0x%02x (%.2fs) to address %d (hc: 0x%02x%02x)\n",
586 command
, extension
, EXT_TO_SEC(extension
), addr
,
587 housecode
[0], housecode
[1]);
589 send_data
[3] = housecode
[0];
590 send_data
[4] = housecode
[1];
592 send_data
[6] = command
;
593 send_data
[7] = extension
;
598 fprintf(stderr
, "No action specified!\n\n");
604 err
= libusb_init(NULL
);
606 fprintf(stderr
, "Can't initialize libusb: %s\n", usb_strerror(err
));
610 devh
= fs20pcs_find();
612 fprintf(stderr
, "Can't find/open fs20pcs!\n");
616 if (fs20pcs_send(devh
, send_data
) == 0) {
617 fprintf(stderr
, "Can't communicate with fs20pcs!\n");