]> cvs.zerfleddert.de Git - hmcfgusb/blame_incremental - hmcfgusb.c
only wake the device up when the timeout really expired
[hmcfgusb] / hmcfgusb.c
... / ...
CommitLineData
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>
32#include <sys/time.h>
33#include <libusb-1.0/libusb.h>
34
35#include "hexdump.h"
36#include "hmcfgusb.h"
37
38#define USB_TIMEOUT 10000
39
40#define ID_VENDOR 0x1b1f
41#define ID_PRODUCT 0xc00f
42#define ID_PRODUCT_BL 0xc010
43
44/* TODO: dynamic */
45#define ASYNC_SIZE 0x0040
46#define ASYNC_INTERVAL 32
47
48#define EP_OUT 0x02
49#define EP_IN 0x83
50
51#define INTERFACE 0
52
53static int quit = 0;
54static int debug = 0;
55
56/* Not in all libusb-1.0 versions, so we have to roll our own :-( */
57static char * usb_strerror(int e)
58{
59 static char unknerr[256];
60
61 switch (e) {
62 case LIBUSB_SUCCESS:
63 return "Success";
64 case LIBUSB_ERROR_IO:
65 return "Input/output error";
66 case LIBUSB_ERROR_INVALID_PARAM:
67 return "Invalid parameter";
68 case LIBUSB_ERROR_ACCESS:
69 return "Access denied (insufficient permissions)";
70 case LIBUSB_ERROR_NO_DEVICE:
71 return "No such device (it may have been disconnected)";
72 case LIBUSB_ERROR_NOT_FOUND:
73 return "Entity not found";
74 case LIBUSB_ERROR_BUSY:
75 return "Resource busy";
76 case LIBUSB_ERROR_TIMEOUT:
77 return "Operation timed out";
78 case LIBUSB_ERROR_OVERFLOW:
79 return "Overflow";
80 case LIBUSB_ERROR_PIPE:
81 return "Pipe error";
82 case LIBUSB_ERROR_INTERRUPTED:
83 return "System call interrupted (perhaps due to signal)";
84 case LIBUSB_ERROR_NO_MEM:
85 return "Insufficient memory";
86 case LIBUSB_ERROR_NOT_SUPPORTED:
87 return "Operation not supported or unimplemented on this platform";
88 case LIBUSB_ERROR_OTHER:
89 return "Other error";
90 };
91 snprintf(unknerr, sizeof(unknerr), "Unknown error code %d / 0x%02x", e, e);
92 return unknerr;
93}
94
95static libusb_device_handle *hmcfgusb_find() {
96 libusb_device_handle *devh = NULL;
97 libusb_device **list;
98 ssize_t cnt;
99 ssize_t i;
100 int err;
101
102 cnt = libusb_get_device_list(NULL, &list);
103 if (cnt < 0) {
104 fprintf(stderr, "Can't get USB device list: %d\n", (int)cnt);
105 return NULL;
106 }
107
108 for (i = 0; i < cnt; i++){
109 struct libusb_device_descriptor desc;
110
111 err = libusb_get_device_descriptor(list[i], &desc);
112 if (err)
113 continue;
114
115 if ((desc.idVendor == ID_VENDOR) &&
116 ((desc.idProduct == ID_PRODUCT) || (desc.idProduct == ID_PRODUCT_BL))) {
117 libusb_device *dev = list[i];
118
119 err = libusb_open(dev, &devh);
120 if (err) {
121 fprintf(stderr, "Can't open device: %s\n", usb_strerror(err));
122 return NULL;
123 }
124
125 err = libusb_detach_kernel_driver(devh, INTERFACE);
126 if ((err != 0) && (err != LIBUSB_ERROR_NOT_FOUND)) {
127 fprintf(stderr, "Can't detach kernel driver: %s\n", usb_strerror(err));
128 return NULL;
129 }
130
131 err = libusb_claim_interface(devh, INTERFACE);
132 if ((err != 0)) {
133 fprintf(stderr, "Can't claim interface: %s\n", usb_strerror(err));
134 return NULL;
135 }
136
137 return devh;
138 }
139
140 }
141
142 return NULL;
143}
144
145int hmcfgusb_send_null_frame(struct hmcfgusb_dev *usbdev, int silent)
146{
147 int err;
148 int cnt;
149 unsigned char out[0x40];
150
151 memset(out, 0, sizeof(out));
152
153 err = libusb_interrupt_transfer(usbdev->usb_devh, EP_OUT, out, sizeof(out), &cnt, USB_TIMEOUT);
154 if (err && (!silent)) {
155 fprintf(stderr, "Can't send null frame: %s\n", usb_strerror(err));
156 return 0;
157 }
158
159 return 1;
160}
161
162int hmcfgusb_send(struct hmcfgusb_dev *usbdev, unsigned char* send_data, int len, int done)
163{
164 int err;
165 int cnt;
166 struct timeval tv_start, tv_end;
167 int msec;
168
169 if (debug) {
170 hexdump(send_data, len, "USB < ");
171 }
172
173 gettimeofday(&tv_start, NULL);
174
175 err = libusb_interrupt_transfer(usbdev->usb_devh, EP_OUT, send_data, len, &cnt, USB_TIMEOUT);
176 if (err) {
177 fprintf(stderr, "Can't send data: %s\n", usb_strerror(err));
178 return 0;
179 }
180
181 if (done) {
182 if (!hmcfgusb_send_null_frame(usbdev, 0)) {
183 return 0;
184 }
185 }
186
187 gettimeofday(&tv_end, NULL);
188 msec = ((tv_end.tv_sec-tv_start.tv_sec)*1000)+((tv_end.tv_usec-tv_start.tv_usec)/1000);
189
190 if (msec > 100) {
191 fprintf(stderr, "usb-transfer took more than 100ms (%dms), this may lead to timing problems!\n", msec);
192 } else if (debug) {
193 fprintf(stderr, "usb-transfer took %dms!\n", msec);
194 }
195
196 return 1;
197}
198
199static struct libusb_transfer *hmcfgusb_prepare_int(libusb_device_handle *devh, libusb_transfer_cb_fn cb, void *data)
200{
201 unsigned char *data_buf;
202 struct libusb_transfer *transfer;
203 int err;
204
205 data_buf = malloc(ASYNC_SIZE);
206 if (!data_buf) {
207 fprintf(stderr, "Can't allocate memory for data-buffer!\n");
208 return NULL;
209 }
210
211 transfer = libusb_alloc_transfer(0);
212 if (!transfer) {
213 fprintf(stderr, "Can't allocate memory for usb-transfer!\n");
214 free(data_buf);
215 return NULL;
216 }
217
218 libusb_fill_interrupt_transfer(transfer, devh, EP_IN,
219 data_buf, ASYNC_SIZE, cb, data, USB_TIMEOUT);
220
221 transfer->flags = LIBUSB_TRANSFER_SHORT_NOT_OK | LIBUSB_TRANSFER_FREE_BUFFER;
222
223 err = libusb_submit_transfer(transfer);
224 if (err != 0) {
225 fprintf(stderr, "Can't submit transfer: %s\n", usb_strerror(err));
226 libusb_free_transfer(transfer);
227 return NULL;
228 }
229
230 return transfer;
231}
232
233struct hmcfgusb_cb_data {
234 struct hmcfgusb_dev *dev;
235 hmcfgusb_cb_fn cb;
236 void *data;
237};
238
239static void LIBUSB_CALL hmcfgusb_interrupt(struct libusb_transfer *transfer)
240{
241 int err;
242 struct hmcfgusb_cb_data *cb_data;
243
244 cb_data = transfer->user_data;
245
246 if (transfer->status != LIBUSB_TRANSFER_COMPLETED) {
247 if (transfer->status != LIBUSB_TRANSFER_TIMED_OUT) {
248 fprintf(stderr, "Interrupt transfer not completed: %s!\n", usb_strerror(transfer->status));
249 quit = EIO;
250
251 if (cb_data && cb_data->dev && cb_data->dev->transfer) {
252 libusb_free_transfer(cb_data->dev->transfer);
253 cb_data->dev->transfer = NULL;
254 free(cb_data);
255 }
256 return;
257 }
258 } else {
259 if (cb_data && cb_data->cb) {
260 if (debug)
261 hexdump(transfer->buffer, transfer->actual_length, "USB > ");
262
263 if (!cb_data->cb(transfer->buffer, transfer->actual_length, cb_data->data)) {
264 quit = EIO;
265
266 if (cb_data && cb_data->dev && cb_data->dev->transfer) {
267 libusb_free_transfer(cb_data->dev->transfer);
268 cb_data->dev->transfer = NULL;
269 free(cb_data);
270 }
271
272 return;
273 }
274 } else {
275 hexdump(transfer->buffer, transfer->actual_length, "> ");
276 }
277 }
278
279 err = libusb_submit_transfer(transfer);
280 if (err != 0) {
281 fprintf(stderr, "Can't re-submit transfer: %s\n", usb_strerror(err));
282 libusb_free_transfer(transfer);
283 cb_data->dev->transfer = NULL;
284 free(cb_data);
285 }
286}
287
288struct hmcfgusb_dev *hmcfgusb_init(hmcfgusb_cb_fn cb, void *data)
289{
290 libusb_device_handle *devh = NULL;
291 const struct libusb_pollfd **usb_pfd = NULL;
292 struct hmcfgusb_dev *dev = NULL;
293 struct hmcfgusb_cb_data *cb_data = NULL;
294 int err;
295 int i;
296
297 err = libusb_init(NULL);
298 if (err != 0) {
299 fprintf(stderr, "Can't initialize libusb: %s\n", usb_strerror(err));
300 return NULL;
301 }
302
303 devh = hmcfgusb_find();
304 if (!devh) {
305 fprintf(stderr, "Can't find/open hmcfgusb!\n");
306 return NULL;
307 }
308
309 dev = malloc(sizeof(struct hmcfgusb_dev));
310 if (!dev) {
311 perror("Can't allocate memory for hmcfgusb_dev");
312 return NULL;
313 }
314
315 memset(dev, 0, sizeof(struct hmcfgusb_dev));
316 dev->usb_devh = devh;
317
318 cb_data = malloc(sizeof(struct hmcfgusb_cb_data));
319 if (!cb_data) {
320 perror("Can't allocate memory for hmcfgusb_cb_data");
321 free(dev);
322 return NULL;
323 }
324
325 memset(cb_data, 0, sizeof(struct hmcfgusb_cb_data));
326
327 cb_data->dev = dev;
328 cb_data->cb = cb;
329 cb_data->data = data;
330
331 dev->transfer = hmcfgusb_prepare_int(devh, hmcfgusb_interrupt, cb_data);
332 if (!dev->transfer) {
333 fprintf(stderr, "Can't prepare async device io!\n");
334 free(dev);
335 free(cb_data);
336 return NULL;
337 }
338
339 usb_pfd = libusb_get_pollfds(NULL);
340 if (!usb_pfd) {
341 fprintf(stderr, "Can't get FDset from libusb!\n");
342 free(dev);
343 free(cb_data);
344 return NULL;
345 }
346
347 dev->n_usb_pfd = 0;
348 for(i = 0; usb_pfd[i]; i++)
349 dev->n_usb_pfd++;
350
351 dev->pfd = malloc(dev->n_usb_pfd * sizeof(struct pollfd));
352 if (!dev->pfd) {
353 perror("Can't allocate memory for poll-fds");
354 free(dev);
355 free(cb_data);
356 return NULL;
357 }
358
359 memset(dev->pfd, 0, dev->n_usb_pfd * sizeof(struct pollfd));
360
361 for (i = 0; i < dev->n_usb_pfd; i++) {
362 dev->pfd[i].fd = usb_pfd[i]->fd;
363 dev->pfd[i].events = usb_pfd[i]->events;
364 dev->pfd[i].revents = 0;
365 }
366
367 free(usb_pfd);
368
369 dev->n_pfd = dev->n_usb_pfd;
370
371 quit = 0;
372
373 return dev;
374}
375
376int hmcfgusb_add_pfd(struct hmcfgusb_dev *dev, int fd, short events)
377{
378 dev->n_pfd++;
379 dev->pfd = realloc(dev->pfd, dev->n_pfd * sizeof(struct pollfd));
380 if (!dev->pfd) {
381 perror("Can't realloc poll-fds");
382 return 0;
383 }
384
385 dev->pfd[dev->n_pfd-1].fd = fd;
386 dev->pfd[dev->n_pfd-1].events = events;
387 dev->pfd[dev->n_pfd-1].revents = 0;
388
389 return 1;
390}
391
392int hmcfgusb_poll(struct hmcfgusb_dev *dev, int timeout)
393{
394 struct timeval tv;
395 int usb_event = 0;
396 int timed_out = 0;
397 int i;
398 int n;
399 int fd_n;
400 int err;
401
402 errno = 0;
403
404 memset(&tv, 0, sizeof(tv));
405 err = libusb_get_next_timeout(NULL, &tv);
406 if (err < 0) {
407 fprintf(stderr, "libusb_get_next_timeout: %s\n", usb_strerror(err));
408 errno = EIO;
409 return -1;
410 } else if (err == 0) {
411 /* No pending timeout or a sane platform */
412 tv.tv_sec = timeout;
413 } else {
414 if ((tv.tv_sec == 0) && (tv.tv_usec == 0)) {
415 usb_event = 1;
416 } else if (tv.tv_sec > timeout) {
417 tv.tv_sec = timeout;
418 tv.tv_usec = 0;
419 }
420 }
421
422 if (!usb_event) {
423 for (i = 0; i < dev->n_pfd; i++) {
424 dev->pfd[i].revents = 0;
425 }
426
427 n = poll(dev->pfd, dev->n_pfd, tv.tv_sec * 1000);
428 if (n < 0) {
429 perror("poll");
430 errno = 0;
431 return -1;
432 } else if (n == 0) {
433 usb_event = 1;
434 timed_out = 1;
435 } else {
436 for (fd_n = 0; fd_n < dev->n_pfd; fd_n++) {
437 if (dev->pfd[fd_n].revents) {
438 if (fd_n < dev->n_usb_pfd) {
439 usb_event = 1;
440 break;
441 } else {
442 errno = 0;
443 return dev->pfd[fd_n].fd;
444 }
445 }
446 }
447 }
448 }
449
450 if (usb_event) {
451 memset(&tv, 0, sizeof(tv));
452 err = libusb_handle_events_timeout_completed(NULL, &tv, NULL);
453 if (err < 0) {
454 fprintf(stderr, "libusb_handle_events_timeout_completed: %s\n", usb_strerror(err));
455 errno = EIO;
456 return -1;
457 }
458 }
459
460 errno = 0;
461 if (quit) {
462 fprintf(stderr, "closing device-connection due to error %d\n", quit);
463 errno = quit;
464 }
465
466 if (timed_out)
467 errno = ETIMEDOUT;
468
469 return -1;
470}
471
472void hmcfgusb_close(struct hmcfgusb_dev *dev)
473{
474 int err;
475
476 if (dev->transfer) {
477 libusb_cancel_transfer(dev->transfer);
478 }
479
480 err = libusb_release_interface(dev->usb_devh, INTERFACE);
481 if ((err != 0)) {
482 fprintf(stderr, "Can't release interface: %s\n", usb_strerror(err));
483 }
484
485 libusb_close(dev->usb_devh);
486 free(dev->pfd);
487 free(dev);
488
489 libusb_exit(NULL);
490}
491
492void hmcfgusb_set_debug(int d)
493{
494 debug = d;
495}
Impressum, Datenschutz