]> cvs.zerfleddert.de Git - hmcfgusb/blob - hmcfgusb.c
Cleanup: Use := for assignment
[hmcfgusb] / hmcfgusb.c
1 /* HM-CFG-USB libusb-driver
2 *
3 * Copyright (c) 2013-16 Michael Gernoth <michael@gernoth.net>
4 *
5 * Permission is hereby granted, free of charge, to any person obtaining a copy
6 * of this software and associated documentation files (the "Software"), to
7 * deal in the Software without restriction, including without limitation the
8 * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
9 * sell copies of the Software, and to permit persons to whom the Software is
10 * furnished to do so, subject to the following conditions:
11 *
12 * The above copyright notice and this permission notice shall be included in
13 * all copies or substantial portions of the Software.
14 *
15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
20 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
21 * IN THE SOFTWARE.
22 */
23
24 #include <string.h>
25 #include <stdio.h>
26 #include <stdint.h>
27 #include <unistd.h>
28 #include <stdlib.h>
29 #include <math.h>
30 #include <poll.h>
31 #include <errno.h>
32 #include <sys/time.h>
33 #include <libusb-1.0/libusb.h>
34
35 /* Workaround for old libusb-1.0 */
36 #ifndef LIBUSB_CALL
37 #define LIBUSB_CALL
38 #define libusb_handle_events_timeout_completed(ctx, tv, x) libusb_handle_events_timeout(ctx, tv)
39 #endif
40
41 #include "hexdump.h"
42 #include "hmcfgusb.h"
43
44 #define USB_TIMEOUT 10000
45
46 #define ID_VENDOR 0x1b1f
47 #define ID_PRODUCT 0xc00f
48 #define ID_PRODUCT_BL 0xc010
49
50 /* TODO: dynamic */
51 #define ASYNC_SIZE 0x0040
52 #define ASYNC_INTERVAL 32
53
54 #define EP_OUT 0x02
55 #define EP_IN 0x83
56
57 #define INTERFACE 0
58
59 static int quit = 0;
60 static int debug = 0;
61 static int libusb_initialized = 0;
62
63 /* Not in all libusb-1.0 versions, so we have to roll our own :-( */
64 static const char* usb_strerror(int e)
65 {
66 static char unknerr[256];
67
68 switch (e) {
69 case LIBUSB_SUCCESS:
70 return "Success";
71 case LIBUSB_ERROR_IO:
72 return "Input/output error";
73 case LIBUSB_ERROR_INVALID_PARAM:
74 return "Invalid parameter";
75 case LIBUSB_ERROR_ACCESS:
76 return "Access denied (insufficient permissions)";
77 case LIBUSB_ERROR_NO_DEVICE:
78 return "No such device (it may have been disconnected)";
79 case LIBUSB_ERROR_NOT_FOUND:
80 return "Entity not found";
81 case LIBUSB_ERROR_BUSY:
82 return "Resource busy";
83 case LIBUSB_ERROR_TIMEOUT:
84 return "Operation timed out";
85 case LIBUSB_ERROR_OVERFLOW:
86 return "Overflow";
87 case LIBUSB_ERROR_PIPE:
88 return "Pipe error";
89 case LIBUSB_ERROR_INTERRUPTED:
90 return "System call interrupted (perhaps due to signal)";
91 case LIBUSB_ERROR_NO_MEM:
92 return "Insufficient memory";
93 case LIBUSB_ERROR_NOT_SUPPORTED:
94 return "Operation not supported or unimplemented on this platform";
95 case LIBUSB_ERROR_OTHER:
96 return "Other error";
97 };
98 snprintf(unknerr, sizeof(unknerr), "Unknown error code %d / 0x%02x", e, e);
99 return unknerr;
100 }
101
102 static const char* usb_str_transfer_status(int e)
103 {
104 static char unknerr[256];
105
106 switch (e) {
107 case LIBUSB_TRANSFER_COMPLETED:
108 return "Transfer completed";
109 case LIBUSB_TRANSFER_ERROR:
110 return "Transfer error";
111 case LIBUSB_TRANSFER_TIMED_OUT:
112 return "Transfer timed out";
113 case LIBUSB_TRANSFER_CANCELLED:
114 return "Transfer cancelled";
115 case LIBUSB_TRANSFER_STALL:
116 return "For bulk/interrupt endpoints: endpoint stalled. For control endpoints: control request not supported.";
117 case LIBUSB_TRANSFER_NO_DEVICE:
118 return "No device";
119 case LIBUSB_TRANSFER_OVERFLOW:
120 return "Transfer overflow";
121 };
122 snprintf(unknerr, sizeof(unknerr), "Unknown transfer status %d / 0x%02x", e, e);
123 return unknerr;
124 }
125
126 static libusb_device_handle *hmcfgusb_find(int vid, int pid, char *serial) {
127 libusb_device_handle *devh = NULL;
128 libusb_device **list;
129 ssize_t cnt;
130 ssize_t i;
131 int err;
132
133 cnt = libusb_get_device_list(NULL, &list);
134 if (cnt < 0) {
135 fprintf(stderr, "Can't get USB device list: %d\n", (int)cnt);
136 return NULL;
137 }
138
139 for (i = 0; i < cnt; i++){
140 struct libusb_device_descriptor desc;
141
142 err = libusb_get_device_descriptor(list[i], &desc);
143 if (err)
144 continue;
145
146 if ((desc.idVendor == vid) && (desc.idProduct == pid)) {
147 libusb_device *dev = list[i];
148
149 err = libusb_open(dev, &devh);
150 if (err) {
151 fprintf(stderr, "Can't open device: %s\n", usb_strerror(err));
152 libusb_free_device_list(list, 1);
153 return NULL;
154 }
155
156 if (serial) {
157 if (desc.iSerialNumber > 0) {
158 uint8_t devSerial[256];
159 err = libusb_get_string_descriptor_ascii(devh, desc.iSerialNumber, devSerial, sizeof(devSerial));
160 if (err < 0) {
161 fprintf(stderr, "Can't read serial-number: %s\n", usb_strerror(err));
162 libusb_close(devh);
163 libusb_free_device_list(list, 1);
164 return NULL;
165 }
166 if (strcmp((char*)devSerial, (char*)serial)) {
167 libusb_close(devh);
168 continue;
169 }
170 } else {
171 libusb_close(devh);
172 continue;
173 }
174 }
175
176 err = libusb_detach_kernel_driver(devh, INTERFACE);
177 if ((err != 0) && (err != LIBUSB_ERROR_NOT_FOUND)) {
178 fprintf(stderr, "Can't detach kernel driver: %s\n", usb_strerror(err));
179 libusb_close(devh);
180 libusb_free_device_list(list, 1);
181 return NULL;
182 }
183
184 err = libusb_claim_interface(devh, INTERFACE);
185 if ((err != 0)) {
186 fprintf(stderr, "Can't claim interface: %s\n", usb_strerror(err));
187 libusb_close(devh);
188 libusb_free_device_list(list, 1);
189 return NULL;
190 }
191
192 libusb_free_device_list(list, 0);
193 return devh;
194 }
195
196 }
197
198 libusb_free_device_list(list, 1);
199 return NULL;
200 }
201
202 int hmcfgusb_send_null_frame(struct hmcfgusb_dev *usbdev, int silent)
203 {
204 int err;
205 int cnt;
206 unsigned char out[0x40];
207
208 memset(out, 0, sizeof(out));
209
210 err = libusb_interrupt_transfer(usbdev->usb_devh, EP_OUT, out, 0, &cnt, USB_TIMEOUT);
211 if (err && (!silent)) {
212 fprintf(stderr, "Can't send null frame: %s\n", usb_strerror(err));
213 return 0;
214 }
215
216 return 1;
217 }
218
219 int hmcfgusb_send(struct hmcfgusb_dev *usbdev, unsigned char* send_data, int len, int done)
220 {
221 int err;
222 int cnt;
223 struct timeval tv_start, tv_end;
224 int msec;
225
226 if (debug) {
227 hexdump(send_data, len, "USB < ");
228 }
229
230 gettimeofday(&tv_start, NULL);
231
232 err = libusb_interrupt_transfer(usbdev->usb_devh, EP_OUT, send_data, len, &cnt, USB_TIMEOUT);
233 if (err) {
234 fprintf(stderr, "Can't send data: %s\n", usb_strerror(err));
235 return 0;
236 }
237
238 if (done) {
239 if (!hmcfgusb_send_null_frame(usbdev, 0)) {
240 return 0;
241 }
242 }
243
244 gettimeofday(&tv_end, NULL);
245 msec = ((tv_end.tv_sec-tv_start.tv_sec)*1000)+((tv_end.tv_usec-tv_start.tv_usec)/1000);
246
247 if (msec > 100) {
248 fprintf(stderr, "usb-transfer took more than 100ms (%dms), this may lead to timing problems!\n", msec);
249 } else if (debug) {
250 fprintf(stderr, "usb-transfer took %dms!\n", msec);
251 }
252
253 return 1;
254 }
255
256 static struct libusb_transfer *hmcfgusb_prepare_int(libusb_device_handle *devh, libusb_transfer_cb_fn cb, void *data, int in_size)
257 {
258 unsigned char *data_buf;
259 struct libusb_transfer *transfer;
260 int err;
261
262 data_buf = malloc(in_size);
263 if (!data_buf) {
264 fprintf(stderr, "Can't allocate memory for data-buffer!\n");
265 return NULL;
266 }
267
268 transfer = libusb_alloc_transfer(0);
269 if (!transfer) {
270 fprintf(stderr, "Can't allocate memory for usb-transfer!\n");
271 free(data_buf);
272 return NULL;
273 }
274
275 libusb_fill_interrupt_transfer(transfer, devh, EP_IN,
276 data_buf, in_size, cb, data, USB_TIMEOUT);
277
278 transfer->flags = LIBUSB_TRANSFER_FREE_BUFFER;
279
280 err = libusb_submit_transfer(transfer);
281 if (err != 0) {
282 fprintf(stderr, "Can't submit transfer: %s\n", usb_strerror(err));
283 libusb_free_transfer(transfer);
284 return NULL;
285 }
286
287 return transfer;
288 }
289
290 struct hmcfgusb_cb_data {
291 struct hmcfgusb_dev *dev;
292 hmcfgusb_cb_fn cb;
293 void *data;
294 };
295
296 static void LIBUSB_CALL hmcfgusb_interrupt(struct libusb_transfer *transfer)
297 {
298 int err;
299 struct hmcfgusb_cb_data *cb_data;
300
301 cb_data = transfer->user_data;
302
303 if (transfer->status != LIBUSB_TRANSFER_COMPLETED) {
304 if (transfer->status != LIBUSB_TRANSFER_TIMED_OUT) {
305 if (transfer->status != LIBUSB_TRANSFER_CANCELLED)
306 fprintf(stderr, "Interrupt transfer not completed: %s!\n", usb_str_transfer_status(transfer->status));
307
308 quit = EIO;
309 goto out;
310 }
311 } else {
312 if (cb_data && cb_data->cb) {
313 if (debug)
314 hexdump(transfer->buffer, transfer->actual_length, "USB > ");
315
316 if (!cb_data->cb(transfer->buffer, transfer->actual_length, cb_data->data)) {
317 quit = EIO;
318 goto out;
319 }
320 } else {
321 hexdump(transfer->buffer, transfer->actual_length, "> ");
322 }
323 }
324
325 err = libusb_submit_transfer(transfer);
326 if (err != 0) {
327 fprintf(stderr, "Can't re-submit transfer: %s\n", usb_strerror(err));
328 goto out;
329 }
330
331 return;
332
333 out:
334 libusb_free_transfer(transfer);
335 if (cb_data) {
336 if (cb_data->dev && cb_data->dev->transfer) {
337 cb_data->dev->transfer = NULL;
338 }
339 free(cb_data);
340 }
341 }
342
343 struct hmcfgusb_dev *hmcfgusb_init(hmcfgusb_cb_fn cb, void *data, char *serial)
344 {
345 libusb_device_handle *devh = NULL;
346 const struct libusb_pollfd **usb_pfd = NULL;
347 struct hmcfgusb_dev *dev = NULL;
348 struct hmcfgusb_cb_data *cb_data = NULL;
349 int bootloader = 0;
350 int err;
351 int i;
352
353 if (!libusb_initialized) {
354 err = libusb_init(NULL);
355 if (err != 0) {
356 fprintf(stderr, "Can't initialize libusb: %s\n", usb_strerror(err));
357 return NULL;
358 }
359 }
360 libusb_initialized = 1;
361
362 devh = hmcfgusb_find(ID_VENDOR, ID_PRODUCT, serial);
363 if (!devh) {
364 devh = hmcfgusb_find(ID_VENDOR, ID_PRODUCT_BL, serial);
365 if (!devh) {
366 if (serial) {
367 fprintf(stderr, "Can't find/open HM-CFG-USB with serial %s!\n", serial);
368 } else {
369 fprintf(stderr, "Can't find/open HM-CFG-USB!\n");
370 }
371 #ifdef NEED_LIBUSB_EXIT
372 hmcfgusb_exit();
373 #endif
374 return NULL;
375 }
376 bootloader = 1;
377 }
378
379 dev = malloc(sizeof(struct hmcfgusb_dev));
380 if (!dev) {
381 perror("Can't allocate memory for hmcfgusb_dev");
382 libusb_close(devh);
383 #ifdef NEED_LIBUSB_EXIT
384 hmcfgusb_exit();
385 #endif
386 return NULL;
387 }
388
389 memset(dev, 0, sizeof(struct hmcfgusb_dev));
390 dev->usb_devh = devh;
391 dev->bootloader = bootloader;
392 dev->opened_at = time(NULL);
393
394 cb_data = malloc(sizeof(struct hmcfgusb_cb_data));
395 if (!cb_data) {
396 perror("Can't allocate memory for hmcfgusb_cb_data");
397 free(dev);
398 libusb_close(devh);
399 #ifdef NEED_LIBUSB_EXIT
400 hmcfgusb_exit();
401 #endif
402 return NULL;
403 }
404
405 memset(cb_data, 0, sizeof(struct hmcfgusb_cb_data));
406
407 cb_data->dev = dev;
408 cb_data->cb = cb;
409 cb_data->data = data;
410
411 dev->transfer = hmcfgusb_prepare_int(devh, hmcfgusb_interrupt, cb_data, ASYNC_SIZE);
412
413 if (!dev->transfer) {
414 fprintf(stderr, "Can't prepare async device io!\n");
415 free(dev);
416 free(cb_data);
417 libusb_close(devh);
418 #ifdef NEED_LIBUSB_EXIT
419 hmcfgusb_exit();
420 #endif
421 return NULL;
422 }
423
424 usb_pfd = libusb_get_pollfds(NULL);
425 if (!usb_pfd) {
426 fprintf(stderr, "Can't get FDset from libusb!\n");
427 libusb_cancel_transfer(dev->transfer);
428 libusb_handle_events(NULL);
429 free(dev);
430 free(cb_data);
431 libusb_close(devh);
432 #ifdef NEED_LIBUSB_EXIT
433 hmcfgusb_exit();
434 #endif
435 return NULL;
436 }
437
438 dev->n_usb_pfd = 0;
439 for(i = 0; usb_pfd[i]; i++)
440 dev->n_usb_pfd++;
441
442 dev->pfd = malloc(dev->n_usb_pfd * sizeof(struct pollfd));
443 if (!dev->pfd) {
444 perror("Can't allocate memory for poll-fds");
445 libusb_cancel_transfer(dev->transfer);
446 libusb_handle_events(NULL);
447 free(dev);
448 free(cb_data);
449 libusb_close(devh);
450 #ifdef NEED_LIBUSB_EXIT
451 hmcfgusb_exit();
452 #endif
453 return NULL;
454 }
455
456 memset(dev->pfd, 0, dev->n_usb_pfd * sizeof(struct pollfd));
457
458 for (i = 0; i < dev->n_usb_pfd; i++) {
459 dev->pfd[i].fd = usb_pfd[i]->fd;
460 dev->pfd[i].events = usb_pfd[i]->events;
461 dev->pfd[i].revents = 0;
462 }
463
464 free(usb_pfd);
465
466 dev->n_pfd = dev->n_usb_pfd;
467
468 quit = 0;
469
470 return dev;
471 }
472
473 int hmcfgusb_add_pfd(struct hmcfgusb_dev *dev, int fd, short events)
474 {
475 dev->n_pfd++;
476 dev->pfd = realloc(dev->pfd, dev->n_pfd * sizeof(struct pollfd));
477 if (!dev->pfd) {
478 perror("Can't realloc poll-fds");
479 return 0;
480 }
481
482 dev->pfd[dev->n_pfd-1].fd = fd;
483 dev->pfd[dev->n_pfd-1].events = events;
484 dev->pfd[dev->n_pfd-1].revents = 0;
485
486 return 1;
487 }
488
489 int hmcfgusb_poll(struct hmcfgusb_dev *dev, int timeout)
490 {
491 struct timeval tv;
492 int usb_event = 0;
493 int timed_out = 0;
494 int i;
495 int n;
496 int fd_n;
497 int err;
498
499 errno = 0;
500
501 memset(&tv, 0, sizeof(tv));
502 err = libusb_get_next_timeout(NULL, &tv);
503 if (err < 0) {
504 fprintf(stderr, "libusb_get_next_timeout: %s\n", usb_strerror(err));
505 errno = EIO;
506 return -1;
507 } else if (err == 0) {
508 /* No pending timeout or a sane platform */
509 } else {
510 if ((tv.tv_sec == 0) && (tv.tv_usec == 0)) {
511 usb_event = 1;
512 } else if ((tv.tv_sec * 1000) < timeout) {
513 timeout = tv.tv_sec * 1000;
514 }
515 }
516
517 if (!usb_event) {
518 for (i = 0; i < dev->n_pfd; i++) {
519 dev->pfd[i].revents = 0;
520 }
521
522 n = poll(dev->pfd, dev->n_pfd, timeout);
523 if (n < 0) {
524 perror("poll");
525 errno = 0;
526 return -1;
527 } else if (n == 0) {
528 usb_event = 1;
529 timed_out = 1;
530 } else {
531 for (fd_n = 0; fd_n < dev->n_pfd; fd_n++) {
532 if (dev->pfd[fd_n].revents) {
533 if (fd_n < dev->n_usb_pfd) {
534 usb_event = 1;
535 break;
536 } else {
537 errno = 0;
538 return dev->pfd[fd_n].fd;
539 }
540 }
541 }
542 }
543 }
544
545 if (usb_event) {
546 memset(&tv, 0, sizeof(tv));
547 err = libusb_handle_events_timeout_completed(NULL, &tv, NULL);
548 if (err < 0) {
549 fprintf(stderr, "libusb_handle_events_timeout_completed: %s\n", usb_strerror(err));
550 errno = EIO;
551 return -1;
552 }
553 }
554
555 errno = 0;
556 if (quit) {
557 fprintf(stderr, "closing device-connection due to error %d\n", quit);
558 errno = quit;
559 }
560
561 if (timed_out)
562 errno = ETIMEDOUT;
563
564 return -1;
565 }
566
567 void hmcfgusb_enter_bootloader(struct hmcfgusb_dev *dev)
568 {
569 uint8_t out[ASYNC_SIZE];
570
571 if (dev->bootloader) {
572 fprintf(stderr, "request for bootloader mode, but device already in bootloader!\n");
573 return;
574 }
575
576 memset(out, 0, sizeof(out));
577 out[0] = 'B';
578 hmcfgusb_send(dev, out, sizeof(out), 1);
579
580 return;
581 }
582
583 void hmcfgusb_leave_bootloader(struct hmcfgusb_dev *dev)
584 {
585 uint8_t out[ASYNC_SIZE];
586
587 if (!dev->bootloader) {
588 fprintf(stderr, "request for leaving bootloader mode, but device already in normal mode!\n");
589 return;
590 }
591
592 memset(out, 0, sizeof(out));
593 out[0] = 'K';
594 hmcfgusb_send(dev, out, sizeof(out), 1);
595
596 return;
597 }
598
599 void hmcfgusb_close(struct hmcfgusb_dev *dev)
600 {
601 int err;
602
603 if (dev->transfer) {
604 libusb_cancel_transfer(dev->transfer);
605 libusb_handle_events(NULL);
606 }
607
608 err = libusb_release_interface(dev->usb_devh, INTERFACE);
609 if ((err != 0)) {
610 fprintf(stderr, "Can't release interface: %s\n", usb_strerror(err));
611 }
612
613 libusb_close(dev->usb_devh);
614 #ifdef NEED_LIBUSB_EXIT
615 hmcfgusb_exit();
616 #endif
617 free(dev->pfd);
618 free(dev);
619 }
620
621 void hmcfgusb_exit(void)
622 {
623 if (libusb_initialized) {
624 libusb_exit(NULL);
625 libusb_initialized = 0;
626 }
627 }
628
629 void hmcfgusb_set_debug(int d)
630 {
631 debug = d;
632 }
Impressum, Datenschutz