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