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