6 #include <libusb-1.0/libusb.h>
8 #define USB_TIMEOUT 10000
10 #define ID_VENDOR 0x18ef
11 #define ID_PRODUCT 0xe015
15 /* Not in all libusb-1.0 versions, so we have to roll our own :-( */
16 static char * usb_strerror(int e
)
18 static char unknerr
[256];
24 return "Input/output error";
25 case LIBUSB_ERROR_INVALID_PARAM
:
26 return "Invalid parameter";
27 case LIBUSB_ERROR_ACCESS
:
28 return "Access denied (insufficient permissions)";
29 case LIBUSB_ERROR_NO_DEVICE
:
30 return "No such device (it may have been disconnected)";
31 case LIBUSB_ERROR_NOT_FOUND
:
32 return "Entity not found";
33 case LIBUSB_ERROR_BUSY
:
34 return "Resource busy";
35 case LIBUSB_ERROR_TIMEOUT
:
36 return "Operation timed out";
37 case LIBUSB_ERROR_OVERFLOW
:
39 case LIBUSB_ERROR_PIPE
:
41 case LIBUSB_ERROR_INTERRUPTED
:
42 return "System call interrupted (perhaps due to signal)";
43 case LIBUSB_ERROR_NO_MEM
:
44 return "Insufficient memory";
45 case LIBUSB_ERROR_NOT_SUPPORTED
:
46 return "Operation not supported or unimplemented on this platform";
47 case LIBUSB_ERROR_OTHER
:
50 snprintf(unknerr
, sizeof(unknerr
), "Unknown error code %d / 0x%02x", e
, e
);
54 libusb_device_handle
*fs20pcs_find() {
55 libusb_device_handle
*devh
= NULL
;
61 cnt
= libusb_get_device_list(NULL
, &list
);
63 fprintf(stderr
, "Can't get USB device list: %d\n", (int)cnt
);
67 for (i
= 0; i
< cnt
; i
++){
68 struct libusb_device_descriptor desc
;
70 err
= libusb_get_device_descriptor(list
[i
], &desc
);
74 if ((desc
.idVendor
== ID_VENDOR
) && (desc
.idProduct
== ID_PRODUCT
)) {
75 libusb_device
*dev
= list
[i
];
77 err
= libusb_open(dev
, &devh
);
79 fprintf(stderr
, "Can't open device: %s\n", usb_strerror(err
));
83 err
= libusb_detach_kernel_driver(devh
, 0);
84 if ((err
!= 0) && (err
!= LIBUSB_ERROR_NOT_FOUND
)) {
85 fprintf(stderr
, "Can't detach kernel driver: %s\n", usb_strerror(err
));
89 err
= libusb_claim_interface(devh
, 0);
91 fprintf(stderr
, "Can't claim interface: %s\n", usb_strerror(err
));
103 int fs20pcs_send(libusb_device_handle
*devh
, unsigned char* send_data
)
105 unsigned char recv_data
[5] = {0x00, 0x00, 0x00, 0x00, 0x00};
111 err
= libusb_interrupt_transfer(devh
, 0x01, send_data
, 11, &cnt
, 5000);
113 fprintf(stderr
, "Can't send data: %s\n", usb_strerror(err
));
117 err
= libusb_interrupt_transfer(devh
, 0x81, recv_data
, sizeof(recv_data
), &cnt
, 5000);
119 fprintf(stderr
, "Can't receive data: %s\n", usb_strerror(err
));
123 if ((recv_data
[0] != 0x02) ||
124 (recv_data
[1] != 0x03) ||
125 (recv_data
[2] != 0xa0)) {
126 fprintf(stderr
, "Unexpected response: ");
127 for (i
= 0; i
< cnt
; i
++) {
128 fprintf(stderr
, "0x%02x ", recv_data
[i
]);
130 fprintf(stderr
, "\n");
137 switch(recv_data
[3]) {
142 printf("Firmware: V%d.%d", ((recv_data
[4] & 0xf0) >> 4) & 0x0f, recv_data
[4] & 0x0f);
145 printf("Unknown command");
149 printf("Wrong length");
153 printf("Aborted sending long press");
156 printf("Nothing to stop");
159 printf("Unknown response: 0x%02x 0x%02x", recv_data
[3], recv_data
[4]);
169 unsigned char *parse_housecode(char *hc
)
171 static unsigned char housecode
[2];
180 memset(housecode
, 0, sizeof(housecode
));
184 if (strncmp(hc
, "0x", 2)) {
185 fprintf(stderr
, "Not a 2 byte hexstring: %s\n", hc
);
189 val
= strtoul(hc
, &ep
, 16);
191 fprintf(stderr
, "Not a 2 byte hexstring: %s\n", hc
);
196 memset(hc_fixed
, 0, sizeof(hc_fixed
));
197 for (i
= 0; i
< 8; i
++) {
198 if ((hc
[i
] < '1') || (hc
[i
] > '4')) {
199 fprintf(stderr
, "Not a valid ELV housecode: %s\n", hc
);
202 hc_fixed
[i
] = hc
[i
] - 1;
203 val
= strtoul(hc_fixed
, &ep
, 4);
206 fprintf(stderr
, "Can't parse fixed ELV housecode: %s\n", hc_fixed
);
212 fprintf(stderr
, "Housecode has to be in hex (1234, 0x1234) or ELV (12341234) format!\n");
217 housecode
[0] = (val
& 0xff00) >> 8;
218 housecode
[1] = (val
& 0xff);
223 int parse_addr(char *ad
)
236 if (strncmp(ad
, "0x", 2)) {
237 memset(ad_fixed
, 0, sizeof(ad_fixed
));
238 for (i
= 0; i
< 4; i
++) {
239 if ((ad
[i
] < '1') || (ad
[i
] > '4')) {
240 fprintf(stderr
, "Not a valid ELV address: %s\n", ad
);
243 ad_fixed
[i
] = ad
[i
] - 1;
244 val
= strtoul(ad_fixed
, &ep
, 4);
247 fprintf(stderr
, "Can't parse fixed ELV housecode: %s\n", ad_fixed
);
255 val
= strtoul(ad
, &ep
, 16);
257 fprintf(stderr
, "Not a 1 byte hexstring: %s\n", ad
);
262 fprintf(stderr
, "Address has to be in hex (01, 0x01) or ELV (1112) format!\n");
272 int parse_command(char *cmd
)
281 if (!strcasecmp(cmd
, "off")) {
283 } else if (!strcasecmp(cmd
, "on")) {
285 } else if (!strcasecmp(cmd
, "toggle")) {
287 } else if (!strcasecmp(cmd
, "dimup")) {
289 } else if (!strcasecmp(cmd
, "dimdown")) {
291 } else if (!strcasecmp(cmd
, "on-for-timer")) {
294 val
= strtoul(cmd
, &ep
, 16);
296 fprintf(stderr
, "Not a 1 byte hexstring or alias: %s\n", cmd
);
300 command
= val
& 0xff;
306 void syntax(char *prog
)
308 fprintf(stderr
, "Syntax: %s options\n\n", prog
);
309 fprintf(stderr
, "Possible options:\n");
310 fprintf(stderr
, "\t-V\t\trequest firmware-version\n");
311 fprintf(stderr
, "\t-x\t\tabort sending long press\n");
312 fprintf(stderr
, "\t-h [ELV|hex]\thousecode in ELV- or hex-notation\n");
313 fprintf(stderr
, "The following options need an housecode:\n");
314 fprintf(stderr
, "\t-a [ELV|hex]\tdestination address\n");
315 fprintf(stderr
, "\t-s [hex|alias]\tsend command byte\n");
316 fprintf(stderr
, "\t-e hex\t\textension byte for command\n");
317 fprintf(stderr
, "\t-r n\t\trepeat sending n times\n");
318 fprintf(stderr
, "\nCommand bytes (without extension byte):\n");
319 fprintf(stderr
, "hex\t\talias\tdescription\n");
320 fprintf(stderr
, "0x00\t\toff\tswitch off\n");
321 fprintf(stderr
, "0x01-0x10\t\tswitch on dimmed (0x01: 6.25%%, 0x02: 12.5%%, ..., 0x10: 100%%)\n");
322 fprintf(stderr
, "0x11\t\ton\tswitch on\n");
323 fprintf(stderr
, "0x12\t\ttoggle\ttoggle\n");
324 fprintf(stderr
, "0x13\t\tdimup\tdimup\n");
325 fprintf(stderr
, "0x14\t\tdimdown\tdimdown\n");
326 fprintf(stderr
, "0x15\t\t\tdimup, pause, dimdown, pause, ...\n");
327 fprintf(stderr
, "0x16\t\t\tstart/stop programming internal timer\n");
328 fprintf(stderr
, "0x17\t\t\tlearn housecode/address\n");
329 fprintf(stderr
, "0x18\t\t\toff for internal timer, then back on with current level\n");
330 fprintf(stderr
, "0x19\t\t\ton (100%%) for internal timer, then off\n");
331 fprintf(stderr
, "0x1a\t\t\ton (old level) for internal timer, then off\n");
332 fprintf(stderr
, "0x1b\t\t\tfactory reset\n");
333 fprintf(stderr
, "0x1e\t\t\ton (100%%) for internal timer, then old level\n");
334 fprintf(stderr
, "0x1f\t\t\ton (old level) for internal timer, then old state\n");
335 fprintf(stderr
, "\nCommand bytes (with timer as extension byte):\n");
336 fprintf(stderr
, "hex\t\talias\tdescription\n");
337 fprintf(stderr
, "0x20\t\t\tdim down to off while timer is running\n");
338 fprintf(stderr
, "0x21-0x30\t\tdim to (0x01: 6.25%%, 0x02: 12.5%%, ..., 0x10: 100%%) while timer is running\n");
339 fprintf(stderr
, "0x31\t\t\tdim to last level while timer is running\n");
340 fprintf(stderr
, "0x32\t\t\tdim to off, then after timer dim to off\n");
341 fprintf(stderr
, "0x33\t\t\tdimup now and switch off after timer\n");
342 fprintf(stderr
, "0x34\t\t\tdimdown now and switch off after timer\n");
343 fprintf(stderr
, "0x35\t\t\tdimup or dimdown (toggle) and switch off after timer\n");
344 fprintf(stderr
, "0x36\t\t\tprogram internal timer\n");
345 fprintf(stderr
, "0x38\t\t\toff for timer, then back on with current level\n");
346 fprintf(stderr
, "0x39\ton-for-timer\ton (100%%) for timer, then off\n");
347 fprintf(stderr
, "0x3a\t\t\ton (old level) for timer, then off\n");
348 fprintf(stderr
, "0x3c\t\t\tprogram internal ramp-up-time\n");
349 fprintf(stderr
, "0x3d\t\t\tprogram internal ramp-down-time\n");
350 fprintf(stderr
, "0x3e\t\t\ton (100%%) for timer, then old level\n");
351 fprintf(stderr
, "0x3f\t\t\ton (old level) for timer, then old state\n");
361 #define DUPLICATE_ACTION if (action != NO_ACTION) { \
362 fprintf(stderr, "duplicate action specified!\n"); \
363 exit(EXIT_FAILURE); }
365 int main(int argc
, char **argv
)
367 unsigned char send_data
[11];
368 unsigned char *housecode
= NULL
;
369 libusb_device_handle
*devh
= NULL
;
373 int action
= NO_ACTION
;
375 uint8_t extension
= 0x00;
379 memset(send_data
, 0, sizeof(send_data
));
383 while ((opt
= getopt(argc
, argv
, "Vxs:h:a:r:e:")) != -1) {
387 action
= REQUEST_FIRMWARE
;
391 action
= ABORT_LONG_PRESS
;
395 action
= SEND_COMMAND
;
397 command
= parse_command(optarg
);
399 fprintf(stderr
, "Can't parse command!\n");
403 printf("Got command: %d\n", command
);
407 housecode
= parse_housecode(optarg
);
409 fprintf(stderr
, "Can't parse housecode!\n");
413 printf("Got housecode: 0x%02x%02x\n", housecode
[0], housecode
[1]);
417 addr
= parse_addr(optarg
);
419 fprintf(stderr
, "Can't parse address!\n");
423 printf("Got address: 0x%02x\n", addr
);
427 repeat
= strtoul(optarg
, &ep
, 10);
429 fprintf(stderr
, "Can't parse repeat!\n");
433 printf("Got repeat: %d\n", repeat
);
437 extension
= strtoul(optarg
, &ep
, 16);
439 fprintf(stderr
, "Can't parse extension!\n");
443 printf("Got extension: %d\n", extension
);
456 case REQUEST_FIRMWARE
:
459 printf("Requesting firmware version...\n");
462 case ABORT_LONG_PRESS
:
465 printf("Aborting long press...\n");
469 if (housecode
== NULL
) {
470 fprintf(stderr
, "housecode needed!\n");
474 fprintf(stderr
, "address needed!\n");
481 send_data
[8] = repeat
;
482 printf("Sending 0x%02x 0x%02x to address %d (hc: 0x%02x%02x) (repeated %d times)\n",
483 command
, extension
, addr
,
484 housecode
[0], housecode
[1],
489 printf("Sending 0x%02x 0x%02x to address %d (hc: 0x%02x%02x)\n",
490 command
, extension
, addr
,
491 housecode
[0], housecode
[1]);
493 send_data
[3] = housecode
[0];
494 send_data
[4] = housecode
[1];
496 send_data
[6] = command
;
497 send_data
[7] = extension
;
502 fprintf(stderr
, "No action specified!\n\n");
508 err
= libusb_init(NULL
);
510 fprintf(stderr
, "Can't initialize libusb: %s\n", usb_strerror(err
));
514 devh
= fs20pcs_find();
516 fprintf(stderr
, "Can't find/open fs20pcs!\n");
520 if (fs20pcs_send(devh
, send_data
) == 0) {
521 fprintf(stderr
, "Can't communicate with fs20pcs!\n");