]> git.zerfleddert.de Git - fs20pcs/blob - fs20pcs.c
d74d5b0976ae32b62b6e2d60d750f51f1aa851b8
[fs20pcs] / fs20pcs.c
1 #include <string.h>
2 #include <stdio.h>
3 #include <stdint.h>
4 #include <unistd.h>
5 #include <stdlib.h>
6 #include <libusb-1.0/libusb.h>
7
8 #define USB_TIMEOUT 10000
9
10 #define ID_VENDOR 0x18ef
11 #define ID_PRODUCT 0xe015
12
13 extern char *optarg;
14
15 /* Not in all libusb-1.0 versions, so we have to roll our own :-( */
16 static char * usb_strerror(int e)
17 {
18 static char unknerr[256];
19
20 switch (e) {
21 case LIBUSB_SUCCESS:
22 return "Success";
23 case LIBUSB_ERROR_IO:
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:
38 return "Overflow";
39 case LIBUSB_ERROR_PIPE:
40 return "Pipe error";
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:
48 return "Other error";
49 };
50 snprintf(unknerr, sizeof(unknerr), "Unknown error code %d / 0x%02x", e, e);
51 return unknerr;
52 }
53
54 libusb_device_handle *fs20pcs_find() {
55 libusb_device_handle *devh = NULL;
56 libusb_device **list;
57 ssize_t cnt;
58 ssize_t i;
59 int err;
60
61 cnt = libusb_get_device_list(NULL, &list);
62 if (cnt < 0) {
63 fprintf(stderr, "Can't get USB device list: %d\n", (int)cnt);
64 return NULL;
65 }
66
67 for (i = 0; i < cnt; i++){
68 struct libusb_device_descriptor desc;
69
70 err = libusb_get_device_descriptor(list[i], &desc);
71 if (err)
72 continue;
73
74 if ((desc.idVendor == ID_VENDOR) && (desc.idProduct == ID_PRODUCT)) {
75 libusb_device *dev = list[i];
76
77 err = libusb_open(dev, &devh);
78 if (err) {
79 fprintf(stderr, "Can't open device: %s\n", usb_strerror(err));
80 return NULL;
81 }
82
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));
86 return NULL;
87 }
88
89 err = libusb_claim_interface(devh, 0);
90 if ((err != 0)) {
91 fprintf(stderr, "Can't claim interface: %s\n", usb_strerror(err));
92 return NULL;
93 }
94
95 return devh;
96 }
97
98 }
99
100 return NULL;
101 }
102
103 int fs20pcs_send(libusb_device_handle *devh, unsigned char* send_data)
104 {
105 unsigned char recv_data[5] = {0x00, 0x00, 0x00, 0x00, 0x00};
106 int err;
107 int cnt;
108 int i;
109 int ret;
110
111 err = libusb_interrupt_transfer(devh, 0x01, send_data, 11, &cnt, 5000);
112 if (err) {
113 fprintf(stderr, "Can't send data: %s\n", usb_strerror(err));
114 return 0;
115 }
116
117 err = libusb_interrupt_transfer(devh, 0x81, recv_data, sizeof(recv_data), &cnt, 5000);
118 if (err) {
119 fprintf(stderr, "Can't receive data: %s\n", usb_strerror(err));
120 return 0;
121 }
122
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]);
129 }
130 fprintf(stderr, "\n");
131
132 return 0;
133 }
134
135 ret = 1;
136
137 switch(recv_data[3]) {
138 case 0x00:
139 printf("Success");
140 break;
141 case 0x01:
142 printf("Firmware: V%d.%d", ((recv_data[4] & 0xf0) >> 4) & 0x0f, recv_data[4] & 0x0f);
143 break;
144 case 0x02:
145 printf("Unknown command");
146 ret = 0;
147 break;
148 case 0x03:
149 printf("Wrong length");
150 ret = 0;
151 break;
152 case 0x04:
153 printf("Aborted sending long press");
154 break;
155 case 0x05:
156 printf("Nothing to stop");
157 break;
158 default:
159 printf("Unknown response: 0x%02x 0x%02x", recv_data[3], recv_data[4]);
160 ret = 0;
161 break;
162 }
163
164 printf("\n");
165
166 return ret;
167 }
168
169 unsigned char *parse_housecode(char *hc)
170 {
171 static unsigned char housecode[2];
172 char hc_fixed[9];
173 char *ep;
174 unsigned long val;
175 int i;
176
177 if (hc == NULL)
178 return NULL;
179
180 memset(housecode, 0, sizeof(housecode));
181
182 switch(strlen(hc)) {
183 case 6:
184 if (strncmp(hc, "0x", 2)) {
185 fprintf(stderr, "Not a 2 byte hexstring: %s\n", hc);
186 return NULL;
187 }
188 case 4:
189 val = strtoul(hc, &ep, 16);
190 if (*ep != '\0') {
191 fprintf(stderr, "Not a 2 byte hexstring: %s\n", hc);
192 return NULL;
193 }
194 break;
195 case 8:
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);
200 return NULL;
201 }
202 hc_fixed[i] = hc[i] - 1;
203 val = strtoul(hc_fixed, &ep, 4);
204
205 if (*ep != '\0') {
206 fprintf(stderr, "Can't parse fixed ELV housecode: %s\n", hc_fixed);
207 return NULL;
208 }
209 }
210 break;
211 default:
212 fprintf(stderr, "Housecode has to be in hex (1234, 0x1234) or ELV (12341234) format!\n");
213 return NULL;
214 break;
215 }
216
217 housecode[0] = (val & 0xff00) >> 8;
218 housecode[1] = (val & 0xff);
219
220 return housecode;
221 }
222
223 int parse_addr(char *ad)
224 {
225 int addr = -1;
226 char ad_fixed[5];
227 char *ep;
228 unsigned long val;
229 int i;
230
231 if (ad == NULL)
232 return -1;
233
234 switch(strlen(ad)) {
235 case 4:
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);
241 return -1;
242 }
243 ad_fixed[i] = ad[i] - 1;
244 val = strtoul(ad_fixed, &ep, 4);
245
246 if (*ep != '\0') {
247 fprintf(stderr, "Can't parse fixed ELV housecode: %s\n", ad_fixed);
248 return -1;
249 }
250 }
251
252 break;
253 }
254 case 2:
255 val = strtoul(ad, &ep, 16);
256 if (*ep != '\0') {
257 fprintf(stderr, "Not a 1 byte hexstring: %s\n", ad);
258 return -1;
259 }
260 break;
261 default:
262 fprintf(stderr, "Address has to be in hex (01, 0x01) or ELV (1112) format!\n");
263 return -1;
264 break;
265 }
266
267 addr = val & 0xff;
268
269 return addr;
270 }
271
272 int parse_command(char *cmd)
273 {
274 int command = -1;
275 char *ep;
276 unsigned long val;
277
278 if (cmd == NULL)
279 return -1;
280
281 if (!strcasecmp(cmd, "off")) {
282 command = 0x00;
283 } else if (!strcasecmp(cmd, "on")) {
284 command = 0x11;
285 } else if (!strcasecmp(cmd, "toggle")) {
286 command = 0x12;
287 } else if (!strcasecmp(cmd, "dimup")) {
288 command = 0x13;
289 } else if (!strcasecmp(cmd, "dimdown")) {
290 command = 0x14;
291 } else if (!strcasecmp(cmd, "on-for-timer")) {
292 command = 0x39;
293 } else {
294 val = strtoul(cmd, &ep, 16);
295 if (*ep != '\0') {
296 fprintf(stderr, "Not a 1 byte hexstring or alias: %s\n", cmd);
297 return -1;
298 }
299
300 command = val & 0xff;
301 }
302
303 return command;
304 }
305
306 void syntax(char *prog)
307 {
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");
352 };
353
354 enum {
355 NO_ACTION,
356 REQUEST_FIRMWARE,
357 SEND_COMMAND,
358 ABORT_LONG_PRESS
359 };
360
361 #define DUPLICATE_ACTION if (action != NO_ACTION) { \
362 fprintf(stderr, "duplicate action specified!\n"); \
363 exit(EXIT_FAILURE); }
364
365 int main(int argc, char **argv)
366 {
367 unsigned char send_data[11];
368 unsigned char *housecode = NULL;
369 libusb_device_handle *devh = NULL;
370 char *ep;
371 int err;
372 int opt;
373 int action = NO_ACTION;
374 int command = -1;
375 uint8_t extension = 0x00;
376 uint8_t repeat = 0;
377 int addr = -1;
378
379 memset(send_data, 0, sizeof(send_data));
380
381 send_data[0] = 0x01;
382
383 while ((opt = getopt(argc, argv, "Vxs:h:a:r:e:")) != -1) {
384 switch(opt) {
385 case 'V':
386 DUPLICATE_ACTION;
387 action = REQUEST_FIRMWARE;
388 break;
389 case 'x':
390 DUPLICATE_ACTION;
391 action = ABORT_LONG_PRESS;
392 break;
393 case 's':
394 DUPLICATE_ACTION;
395 action = SEND_COMMAND;
396
397 command = parse_command(optarg);
398 if (command == -1) {
399 fprintf(stderr, "Can't parse command!\n");
400 exit(EXIT_FAILURE);
401 }
402 #ifdef DEBUG
403 printf("Got command: %d\n", command);
404 #endif
405 break;
406 case 'h':
407 housecode = parse_housecode(optarg);
408 if (!housecode) {
409 fprintf(stderr, "Can't parse housecode!\n");
410 exit(EXIT_FAILURE);
411 }
412 #ifdef DEBUG
413 printf("Got housecode: 0x%02x%02x\n", housecode[0], housecode[1]);
414 #endif
415 break;
416 case 'a':
417 addr = parse_addr(optarg);
418 if (addr == -1) {
419 fprintf(stderr, "Can't parse address!\n");
420 exit(EXIT_FAILURE);
421 }
422 #ifdef DEBUG
423 printf("Got address: 0x%02x\n", addr);
424 #endif
425 break;
426 case 'r':
427 repeat = strtoul(optarg, &ep, 10);
428 if (*ep != '\0') {
429 fprintf(stderr, "Can't parse repeat!\n");
430 exit(EXIT_FAILURE);
431 }
432 #ifdef DEBUG
433 printf("Got repeat: %d\n", repeat);
434 #endif
435 break;
436 case 'e':
437 extension = strtoul(optarg, &ep, 16);
438 if (*ep != '\0') {
439 fprintf(stderr, "Can't parse extension!\n");
440 exit(EXIT_FAILURE);
441 }
442 #ifdef DEBUG
443 printf("Got extension: %d\n", extension);
444 #endif
445 break;
446 case ':':
447 case '?':
448 default:
449 syntax(argv[0]);
450 exit(EXIT_FAILURE);
451 break;
452 }
453 }
454
455 switch(action) {
456 case REQUEST_FIRMWARE:
457 send_data[1] = 0x01;
458 send_data[2] = 0xf0;
459 printf("Requesting firmware version...\n");
460
461 break;
462 case ABORT_LONG_PRESS:
463 send_data[1] = 0x01;
464 send_data[2] = 0xf3;
465 printf("Aborting long press...\n");
466
467 break;
468 case SEND_COMMAND:
469 if (housecode == NULL) {
470 fprintf(stderr, "housecode needed!\n");
471 exit(EXIT_FAILURE);
472 }
473 if (addr == -1) {
474 fprintf(stderr, "address needed!\n");
475 exit(EXIT_FAILURE);
476 }
477
478 if (repeat) {
479 send_data[1] = 0x07;
480 send_data[2] = 0xf2;
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],
485 repeat);
486 } else {
487 send_data[1] = 0x06;
488 send_data[2] = 0xf1;
489 printf("Sending 0x%02x 0x%02x to address %d (hc: 0x%02x%02x)\n",
490 command, extension, addr,
491 housecode[0], housecode[1]);
492 }
493 send_data[3] = housecode[0];
494 send_data[4] = housecode[1];
495 send_data[5] = addr;
496 send_data[6] = command;
497 send_data[7] = extension;
498
499 break;
500 case NO_ACTION:
501 default:
502 fprintf(stderr, "No action specified!\n\n");
503 syntax(argv[0]);
504 exit(EXIT_FAILURE);
505 break;
506 }
507
508 err = libusb_init(NULL);
509 if (err != 0) {
510 fprintf(stderr, "Can't initialize libusb: %s\n", usb_strerror(err));
511 return EXIT_FAILURE;
512 }
513
514 devh = fs20pcs_find();
515 if (!devh) {
516 fprintf(stderr, "Can't find/open fs20pcs!\n");
517 return EXIT_FAILURE;
518 }
519
520 if (fs20pcs_send(devh, send_data) == 0) {
521 fprintf(stderr, "Can't communicate with fs20pcs!\n");
522 return EXIT_FAILURE;
523 }
524
525 libusb_close(NULL);
526
527 return EXIT_SUCCESS;
528 }
Impressum, Datenschutz