]> git.zerfleddert.de Git - hmcfgusb/blame_incremental - hmcfgusb.c
Add support for printing the transfer status
[hmcfgusb] / hmcfgusb.c
... / ...
CommitLineData
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
59static int quit = 0;
60static int debug = 0;
61static int libusb_initialized = 0;
62
63/* Not in all libusb-1.0 versions, so we have to roll our own :-( */
64static 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
102static 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 "No device";
117 case LIBUSB_TRANSFER_OVERFLOW:
118 return "Transfer overflow";
119 };
120 snprintf(unknerr, sizeof(unknerr), "Unknown transfer status %d / 0x%02x", e, e);
121 return unknerr;
122}
123
124static libusb_device_handle *hmcfgusb_find(int vid, int pid, char *serial) {
125 libusb_device_handle *devh = NULL;
126 libusb_device **list;
127 ssize_t cnt;
128 ssize_t i;
129 int err;
130
131 cnt = libusb_get_device_list(NULL, &list);
132 if (cnt < 0) {
133 fprintf(stderr, "Can't get USB device list: %d\n", (int)cnt);
134 return NULL;
135 }
136
137 for (i = 0; i < cnt; i++){
138 struct libusb_device_descriptor desc;
139
140 err = libusb_get_device_descriptor(list[i], &desc);
141 if (err)
142 continue;
143
144 if ((desc.idVendor == vid) && (desc.idProduct == pid)) {
145 libusb_device *dev = list[i];
146
147 err = libusb_open(dev, &devh);
148 if (err) {
149 fprintf(stderr, "Can't open device: %s\n", usb_strerror(err));
150 libusb_free_device_list(list, 1);
151 return NULL;
152 }
153
154 if (serial) {
155 if (desc.iSerialNumber > 0) {
156 uint8_t devSerial[256];
157 err = libusb_get_string_descriptor_ascii(devh, desc.iSerialNumber, devSerial, sizeof(devSerial));
158 if (err < 0) {
159 fprintf(stderr, "Can't read serial-number: %s\n", usb_strerror(err));
160 libusb_close(devh);
161 libusb_free_device_list(list, 1);
162 return NULL;
163 }
164 if (strcmp((char*)devSerial, (char*)serial)) {
165 libusb_close(devh);
166 continue;
167 }
168 } else {
169 libusb_close(devh);
170 continue;
171 }
172 }
173
174 err = libusb_detach_kernel_driver(devh, INTERFACE);
175 if ((err != 0) && (err != LIBUSB_ERROR_NOT_FOUND)) {
176 fprintf(stderr, "Can't detach kernel driver: %s\n", usb_strerror(err));
177 libusb_close(devh);
178 libusb_free_device_list(list, 1);
179 return NULL;
180 }
181
182 err = libusb_claim_interface(devh, INTERFACE);
183 if ((err != 0)) {
184 fprintf(stderr, "Can't claim interface: %s\n", usb_strerror(err));
185 libusb_close(devh);
186 libusb_free_device_list(list, 1);
187 return NULL;
188 }
189
190 libusb_free_device_list(list, 0);
191 return devh;
192 }
193
194 }
195
196 libusb_free_device_list(list, 1);
197 return NULL;
198}
199
200int hmcfgusb_send_null_frame(struct hmcfgusb_dev *usbdev, int silent)
201{
202 int err;
203 int cnt;
204 unsigned char out[0x40];
205
206 memset(out, 0, sizeof(out));
207
208 err = libusb_interrupt_transfer(usbdev->usb_devh, EP_OUT, out, 0, &cnt, USB_TIMEOUT);
209 if (err && (!silent)) {
210 fprintf(stderr, "Can't send null frame: %s\n", usb_strerror(err));
211 return 0;
212 }
213
214 return 1;
215}
216
217int hmcfgusb_send(struct hmcfgusb_dev *usbdev, unsigned char* send_data, int len, int done)
218{
219 int err;
220 int cnt;
221 struct timeval tv_start, tv_end;
222 int msec;
223
224 if (debug) {
225 hexdump(send_data, len, "USB < ");
226 }
227
228 gettimeofday(&tv_start, NULL);
229
230 err = libusb_interrupt_transfer(usbdev->usb_devh, EP_OUT, send_data, len, &cnt, USB_TIMEOUT);
231 if (err) {
232 fprintf(stderr, "Can't send data: %s\n", usb_strerror(err));
233 return 0;
234 }
235
236 if (done) {
237 if (!hmcfgusb_send_null_frame(usbdev, 0)) {
238 return 0;
239 }
240 }
241
242 gettimeofday(&tv_end, NULL);
243 msec = ((tv_end.tv_sec-tv_start.tv_sec)*1000)+((tv_end.tv_usec-tv_start.tv_usec)/1000);
244
245 if (msec > 100) {
246 fprintf(stderr, "usb-transfer took more than 100ms (%dms), this may lead to timing problems!\n", msec);
247 } else if (debug) {
248 fprintf(stderr, "usb-transfer took %dms!\n", msec);
249 }
250
251 return 1;
252}
253
254static struct libusb_transfer *hmcfgusb_prepare_int(libusb_device_handle *devh, libusb_transfer_cb_fn cb, void *data, int in_size)
255{
256 unsigned char *data_buf;
257 struct libusb_transfer *transfer;
258 int err;
259
260 data_buf = malloc(in_size);
261 if (!data_buf) {
262 fprintf(stderr, "Can't allocate memory for data-buffer!\n");
263 return NULL;
264 }
265
266 transfer = libusb_alloc_transfer(0);
267 if (!transfer) {
268 fprintf(stderr, "Can't allocate memory for usb-transfer!\n");
269 free(data_buf);
270 return NULL;
271 }
272
273 libusb_fill_interrupt_transfer(transfer, devh, EP_IN,
274 data_buf, in_size, cb, data, USB_TIMEOUT);
275
276 transfer->flags = LIBUSB_TRANSFER_FREE_BUFFER;
277
278 err = libusb_submit_transfer(transfer);
279 if (err != 0) {
280 fprintf(stderr, "Can't submit transfer: %s\n", usb_strerror(err));
281 libusb_free_transfer(transfer);
282 return NULL;
283 }
284
285 return transfer;
286}
287
288struct hmcfgusb_cb_data {
289 struct hmcfgusb_dev *dev;
290 hmcfgusb_cb_fn cb;
291 void *data;
292};
293
294static void LIBUSB_CALL hmcfgusb_interrupt(struct libusb_transfer *transfer)
295{
296 int err;
297 struct hmcfgusb_cb_data *cb_data;
298
299 cb_data = transfer->user_data;
300
301 if (transfer->status != LIBUSB_TRANSFER_COMPLETED) {
302 if (transfer->status != LIBUSB_TRANSFER_TIMED_OUT) {
303 if (transfer->status != LIBUSB_TRANSFER_CANCELLED)
304 fprintf(stderr, "Interrupt transfer not completed: %s!\n", usb_str_transfer_status(transfer->status));
305
306 quit = EIO;
307 goto out;
308 }
309 } else {
310 if (cb_data && cb_data->cb) {
311 if (debug)
312 hexdump(transfer->buffer, transfer->actual_length, "USB > ");
313
314 if (!cb_data->cb(transfer->buffer, transfer->actual_length, cb_data->data)) {
315 quit = EIO;
316 goto out;
317 }
318 } else {
319 hexdump(transfer->buffer, transfer->actual_length, "> ");
320 }
321 }
322
323 err = libusb_submit_transfer(transfer);
324 if (err != 0) {
325 fprintf(stderr, "Can't re-submit transfer: %s\n", usb_strerror(err));
326 goto out;
327 }
328
329 return;
330
331out:
332 libusb_free_transfer(transfer);
333 if (cb_data) {
334 if (cb_data->dev && cb_data->dev->transfer) {
335 cb_data->dev->transfer = NULL;
336 }
337 free(cb_data);
338 }
339}
340
341struct hmcfgusb_dev *hmcfgusb_init(hmcfgusb_cb_fn cb, void *data, char *serial)
342{
343 libusb_device_handle *devh = NULL;
344 const struct libusb_pollfd **usb_pfd = NULL;
345 struct hmcfgusb_dev *dev = NULL;
346 struct hmcfgusb_cb_data *cb_data = NULL;
347 int bootloader = 0;
348 int err;
349 int i;
350
351 if (!libusb_initialized) {
352 err = libusb_init(NULL);
353 if (err != 0) {
354 fprintf(stderr, "Can't initialize libusb: %s\n", usb_strerror(err));
355 return NULL;
356 }
357 }
358 libusb_initialized = 1;
359
360 devh = hmcfgusb_find(ID_VENDOR, ID_PRODUCT, serial);
361 if (!devh) {
362 devh = hmcfgusb_find(ID_VENDOR, ID_PRODUCT_BL, serial);
363 if (!devh) {
364 if (serial) {
365 fprintf(stderr, "Can't find/open HM-CFG-USB with serial %s!\n", serial);
366 } else {
367 fprintf(stderr, "Can't find/open HM-CFG-USB!\n");
368 }
369#ifdef NEED_LIBUSB_EXIT
370 hmcfgusb_exit();
371#endif
372 return NULL;
373 }
374 bootloader = 1;
375 }
376
377 dev = malloc(sizeof(struct hmcfgusb_dev));
378 if (!dev) {
379 perror("Can't allocate memory for hmcfgusb_dev");
380 libusb_close(devh);
381#ifdef NEED_LIBUSB_EXIT
382 hmcfgusb_exit();
383#endif
384 return NULL;
385 }
386
387 memset(dev, 0, sizeof(struct hmcfgusb_dev));
388 dev->usb_devh = devh;
389 dev->bootloader = bootloader;
390 dev->opened_at = time(NULL);
391
392 cb_data = malloc(sizeof(struct hmcfgusb_cb_data));
393 if (!cb_data) {
394 perror("Can't allocate memory for hmcfgusb_cb_data");
395 free(dev);
396 libusb_close(devh);
397#ifdef NEED_LIBUSB_EXIT
398 hmcfgusb_exit();
399#endif
400 return NULL;
401 }
402
403 memset(cb_data, 0, sizeof(struct hmcfgusb_cb_data));
404
405 cb_data->dev = dev;
406 cb_data->cb = cb;
407 cb_data->data = data;
408
409 dev->transfer = hmcfgusb_prepare_int(devh, hmcfgusb_interrupt, cb_data, ASYNC_SIZE);
410
411 if (!dev->transfer) {
412 fprintf(stderr, "Can't prepare async device io!\n");
413 free(dev);
414 free(cb_data);
415 libusb_close(devh);
416#ifdef NEED_LIBUSB_EXIT
417 hmcfgusb_exit();
418#endif
419 return NULL;
420 }
421
422 usb_pfd = libusb_get_pollfds(NULL);
423 if (!usb_pfd) {
424 fprintf(stderr, "Can't get FDset from libusb!\n");
425 libusb_cancel_transfer(dev->transfer);
426 libusb_handle_events(NULL);
427 free(dev);
428 free(cb_data);
429 libusb_close(devh);
430#ifdef NEED_LIBUSB_EXIT
431 hmcfgusb_exit();
432#endif
433 return NULL;
434 }
435
436 dev->n_usb_pfd = 0;
437 for(i = 0; usb_pfd[i]; i++)
438 dev->n_usb_pfd++;
439
440 dev->pfd = malloc(dev->n_usb_pfd * sizeof(struct pollfd));
441 if (!dev->pfd) {
442 perror("Can't allocate memory for poll-fds");
443 libusb_cancel_transfer(dev->transfer);
444 libusb_handle_events(NULL);
445 free(dev);
446 free(cb_data);
447 libusb_close(devh);
448#ifdef NEED_LIBUSB_EXIT
449 hmcfgusb_exit();
450#endif
451 return NULL;
452 }
453
454 memset(dev->pfd, 0, dev->n_usb_pfd * sizeof(struct pollfd));
455
456 for (i = 0; i < dev->n_usb_pfd; i++) {
457 dev->pfd[i].fd = usb_pfd[i]->fd;
458 dev->pfd[i].events = usb_pfd[i]->events;
459 dev->pfd[i].revents = 0;
460 }
461
462 free(usb_pfd);
463
464 dev->n_pfd = dev->n_usb_pfd;
465
466 quit = 0;
467
468 return dev;
469}
470
471int hmcfgusb_add_pfd(struct hmcfgusb_dev *dev, int fd, short events)
472{
473 dev->n_pfd++;
474 dev->pfd = realloc(dev->pfd, dev->n_pfd * sizeof(struct pollfd));
475 if (!dev->pfd) {
476 perror("Can't realloc poll-fds");
477 return 0;
478 }
479
480 dev->pfd[dev->n_pfd-1].fd = fd;
481 dev->pfd[dev->n_pfd-1].events = events;
482 dev->pfd[dev->n_pfd-1].revents = 0;
483
484 return 1;
485}
486
487int hmcfgusb_poll(struct hmcfgusb_dev *dev, int timeout)
488{
489 struct timeval tv;
490 int usb_event = 0;
491 int timed_out = 0;
492 int i;
493 int n;
494 int fd_n;
495 int err;
496
497 errno = 0;
498
499 memset(&tv, 0, sizeof(tv));
500 err = libusb_get_next_timeout(NULL, &tv);
501 if (err < 0) {
502 fprintf(stderr, "libusb_get_next_timeout: %s\n", usb_strerror(err));
503 errno = EIO;
504 return -1;
505 } else if (err == 0) {
506 /* No pending timeout or a sane platform */
507 } else {
508 if ((tv.tv_sec == 0) && (tv.tv_usec == 0)) {
509 usb_event = 1;
510 } else if ((tv.tv_sec * 1000) < timeout) {
511 timeout = tv.tv_sec * 1000;
512 }
513 }
514
515 if (!usb_event) {
516 for (i = 0; i < dev->n_pfd; i++) {
517 dev->pfd[i].revents = 0;
518 }
519
520 n = poll(dev->pfd, dev->n_pfd, timeout);
521 if (n < 0) {
522 perror("poll");
523 errno = 0;
524 return -1;
525 } else if (n == 0) {
526 usb_event = 1;
527 timed_out = 1;
528 } else {
529 for (fd_n = 0; fd_n < dev->n_pfd; fd_n++) {
530 if (dev->pfd[fd_n].revents) {
531 if (fd_n < dev->n_usb_pfd) {
532 usb_event = 1;
533 break;
534 } else {
535 errno = 0;
536 return dev->pfd[fd_n].fd;
537 }
538 }
539 }
540 }
541 }
542
543 if (usb_event) {
544 memset(&tv, 0, sizeof(tv));
545 err = libusb_handle_events_timeout_completed(NULL, &tv, NULL);
546 if (err < 0) {
547 fprintf(stderr, "libusb_handle_events_timeout_completed: %s\n", usb_strerror(err));
548 errno = EIO;
549 return -1;
550 }
551 }
552
553 errno = 0;
554 if (quit) {
555 fprintf(stderr, "closing device-connection due to error %d\n", quit);
556 errno = quit;
557 }
558
559 if (timed_out)
560 errno = ETIMEDOUT;
561
562 return -1;
563}
564
565void hmcfgusb_enter_bootloader(struct hmcfgusb_dev *dev)
566{
567 uint8_t out[ASYNC_SIZE];
568
569 if (dev->bootloader) {
570 fprintf(stderr, "request for bootloader mode, but device already in bootloader!\n");
571 return;
572 }
573
574 memset(out, 0, sizeof(out));
575 out[0] = 'B';
576 hmcfgusb_send(dev, out, sizeof(out), 1);
577
578 return;
579}
580
581void hmcfgusb_leave_bootloader(struct hmcfgusb_dev *dev)
582{
583 uint8_t out[ASYNC_SIZE];
584
585 if (!dev->bootloader) {
586 fprintf(stderr, "request for leaving bootloader mode, but device already in normal mode!\n");
587 return;
588 }
589
590 memset(out, 0, sizeof(out));
591 out[0] = 'K';
592 hmcfgusb_send(dev, out, sizeof(out), 1);
593
594 return;
595}
596
597void hmcfgusb_close(struct hmcfgusb_dev *dev)
598{
599 int err;
600
601 if (dev->transfer) {
602 libusb_cancel_transfer(dev->transfer);
603 libusb_handle_events(NULL);
604 }
605
606 err = libusb_release_interface(dev->usb_devh, INTERFACE);
607 if ((err != 0)) {
608 fprintf(stderr, "Can't release interface: %s\n", usb_strerror(err));
609 }
610
611 libusb_close(dev->usb_devh);
612#ifdef NEED_LIBUSB_EXIT
613 hmcfgusb_exit();
614#endif
615 free(dev->pfd);
616 free(dev);
617}
618
619void hmcfgusb_exit(void)
620{
621 if (libusb_initialized) {
622 libusb_exit(NULL);
623 libusb_initialized = 0;
624 }
625}
626
627void hmcfgusb_set_debug(int d)
628{
629 debug = d;
630}
Impressum, Datenschutz