]> git.zerfleddert.de Git - hmcfgusb/blob - hmcfgusb.c
ce8c595268918381544c756d60f3b36d0045ae5f
[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 gettimeofday(&tv_start, NULL);
167 }
168
169 err = libusb_interrupt_transfer(usbdev->usb_devh, EP_OUT, send_data, len, &cnt, USB_TIMEOUT);
170 if (err) {
171 fprintf(stderr, "Can't send data: %s\n", usb_strerror(err));
172 return 0;
173 }
174
175 if (done) {
176 if (!hmcfgusb_send_null_frame(usbdev)) {
177 return 0;
178 }
179 }
180
181 if (debug) {
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 fprintf(stderr, "send took %dms!\n", msec);
185 }
186
187 return 1;
188 }
189
190 static struct libusb_transfer *hmcfgusb_prepare_int(libusb_device_handle *devh, libusb_transfer_cb_fn cb, void *data)
191 {
192 unsigned char *data_buf;
193 struct libusb_transfer *transfer;
194 int err;
195
196 data_buf = malloc(ASYNC_SIZE);
197 if (!data_buf) {
198 fprintf(stderr, "Can't allocate memory for data-buffer!\n");
199 return NULL;
200 }
201
202 transfer = libusb_alloc_transfer(0);
203 if (!transfer) {
204 fprintf(stderr, "Can't allocate memory for usb-transfer!\n");
205 free(data_buf);
206 return NULL;
207 }
208
209 libusb_fill_interrupt_transfer(transfer, devh, EP_IN,
210 data_buf, ASYNC_SIZE, cb, data, USB_TIMEOUT);
211
212 transfer->flags = LIBUSB_TRANSFER_SHORT_NOT_OK | LIBUSB_TRANSFER_FREE_BUFFER;
213
214 err = libusb_submit_transfer(transfer);
215 if (err != 0) {
216 fprintf(stderr, "Can't submit transfer: %s\n", usb_strerror(err));
217 libusb_free_transfer(transfer);
218 return NULL;
219 }
220
221 return transfer;
222 }
223
224 struct hmcfgusb_cb_data {
225 struct hmcfgusb_dev *dev;
226 hmcfgusb_cb_fn cb;
227 void *data;
228 };
229
230 static void LIBUSB_CALL hmcfgusb_interrupt(struct libusb_transfer *transfer)
231 {
232 int err;
233 struct hmcfgusb_cb_data *cb_data;
234
235 cb_data = transfer->user_data;
236
237 if (transfer->status != LIBUSB_TRANSFER_COMPLETED) {
238 if (transfer->status != LIBUSB_TRANSFER_TIMED_OUT) {
239 fprintf(stderr, "Interrupt transfer not completed: %d!\n", transfer->status);
240 quit = EIO;
241
242 if (cb_data && cb_data->dev && cb_data->dev->transfer) {
243 libusb_free_transfer(cb_data->dev->transfer);
244 cb_data->dev->transfer = NULL;
245 free(cb_data);
246 }
247 return;
248 }
249 } else {
250 if (cb_data && cb_data->cb) {
251 if (debug)
252 hexdump(transfer->buffer, transfer->actual_length, "USB > ");
253
254 if (!cb_data->cb(transfer->buffer, transfer->actual_length, cb_data->data)) {
255 quit = EIO;
256
257 if (cb_data && cb_data->dev && cb_data->dev->transfer) {
258 libusb_free_transfer(cb_data->dev->transfer);
259 cb_data->dev->transfer = NULL;
260 free(cb_data);
261 }
262
263 return;
264 }
265 } else {
266 hexdump(transfer->buffer, transfer->actual_length, "> ");
267 }
268 }
269
270 err = libusb_submit_transfer(transfer);
271 if (err != 0) {
272 fprintf(stderr, "Can't re-submit transfer: %s\n", usb_strerror(err));
273 libusb_free_transfer(transfer);
274 cb_data->dev->transfer = NULL;
275 free(cb_data);
276 }
277 }
278
279 struct hmcfgusb_dev *hmcfgusb_init(hmcfgusb_cb_fn cb, void *data)
280 {
281 libusb_device_handle *devh = NULL;
282 const struct libusb_pollfd **usb_pfd = NULL;
283 struct hmcfgusb_dev *dev = NULL;
284 struct hmcfgusb_cb_data *cb_data = NULL;
285 int err;
286 int i;
287
288 err = libusb_init(NULL);
289 if (err != 0) {
290 fprintf(stderr, "Can't initialize libusb: %s\n", usb_strerror(err));
291 return NULL;
292 }
293
294 devh = hmcfgusb_find();
295 if (!devh) {
296 fprintf(stderr, "Can't find/open hmcfgusb!\n");
297 return NULL;
298 }
299
300 dev = malloc(sizeof(struct hmcfgusb_dev));
301 if (!dev) {
302 perror("Can't allocate memory for hmcfgusb_dev");
303 return NULL;
304 }
305
306 memset(dev, 0, sizeof(struct hmcfgusb_dev));
307 dev->usb_devh = devh;
308
309 cb_data = malloc(sizeof(struct hmcfgusb_cb_data));
310 if (!cb_data) {
311 perror("Can't allocate memory for hmcfgusb_cb_data");
312 free(dev);
313 return NULL;
314 }
315
316 memset(cb_data, 0, sizeof(struct hmcfgusb_cb_data));
317
318 cb_data->dev = dev;
319 cb_data->cb = cb;
320 cb_data->data = data;
321
322 dev->transfer = hmcfgusb_prepare_int(devh, hmcfgusb_interrupt, cb_data);
323 if (!dev->transfer) {
324 fprintf(stderr, "Can't prepare async device io!\n");
325 free(dev);
326 free(cb_data);
327 return NULL;
328 }
329
330 usb_pfd = libusb_get_pollfds(NULL);
331 if (!usb_pfd) {
332 fprintf(stderr, "Can't get FDset from libusb!\n");
333 free(dev);
334 free(cb_data);
335 return NULL;
336 }
337
338 dev->n_usb_pfd = 0;
339 for(i = 0; usb_pfd[i]; i++)
340 dev->n_usb_pfd++;
341
342 dev->pfd = malloc(dev->n_usb_pfd * sizeof(struct pollfd));
343 if (!dev->pfd) {
344 perror("Can't allocate memory for poll-fds");
345 free(dev);
346 free(cb_data);
347 return NULL;
348 }
349
350 memset(dev->pfd, 0, dev->n_usb_pfd * sizeof(struct pollfd));
351
352 for (i = 0; i < dev->n_usb_pfd; i++) {
353 dev->pfd[i].fd = usb_pfd[i]->fd;
354 dev->pfd[i].events = usb_pfd[i]->events;
355 dev->pfd[i].revents = 0;
356 }
357
358 free(usb_pfd);
359
360 dev->n_pfd = dev->n_usb_pfd;
361
362 quit = 0;
363
364 return dev;
365 }
366
367 int hmcfgusb_add_pfd(struct hmcfgusb_dev *dev, int fd, short events)
368 {
369 dev->n_pfd++;
370 dev->pfd = realloc(dev->pfd, dev->n_pfd * sizeof(struct pollfd));
371 if (!dev->pfd) {
372 perror("Can't realloc poll-fds");
373 return 0;
374 }
375
376 dev->pfd[dev->n_pfd-1].fd = fd;
377 dev->pfd[dev->n_pfd-1].events = events;
378 dev->pfd[dev->n_pfd-1].revents = 0;
379
380 return 1;
381 }
382
383 int hmcfgusb_poll(struct hmcfgusb_dev *dev, int timeout)
384 {
385 struct timeval tv;
386 int usb_event = 0;
387 int i;
388 int n;
389 int fd_n;
390 int err;
391
392 errno = 0;
393
394 memset(&tv, 0, sizeof(tv));
395 err = libusb_get_next_timeout(NULL, &tv);
396 if (err < 0) {
397 fprintf(stderr, "libusb_get_next_timeout: %s\n", usb_strerror(err));
398 errno = EIO;
399 return -1;
400 } else if (err == 0) {
401 /* No pending timeout or a sane platform */
402 tv.tv_sec = timeout;
403 } else {
404 if ((tv.tv_sec == 0) && (tv.tv_usec == 0)) {
405 usb_event = 1;
406 }
407 }
408
409 if (!usb_event) {
410 for (i = 0; i < dev->n_pfd; i++) {
411 dev->pfd[i].revents = 0;
412 }
413
414 n = poll(dev->pfd, dev->n_pfd, tv.tv_sec * 1000);
415 if (n < 0) {
416 perror("poll");
417 errno = 0;
418 return -1;
419 } else if (n == 0) {
420 usb_event = 1;
421 } else {
422 for (fd_n = 0; fd_n < dev->n_pfd; fd_n++) {
423 if (dev->pfd[fd_n].revents) {
424 if (fd_n < dev->n_usb_pfd) {
425 usb_event = 1;
426 break;
427 } else {
428 errno = 0;
429 return dev->pfd[fd_n].fd;
430 }
431 }
432 }
433 }
434 }
435
436 if (usb_event) {
437 memset(&tv, 0, sizeof(tv));
438 err = libusb_handle_events_timeout_completed(NULL, &tv, NULL);
439 if (err < 0) {
440 fprintf(stderr, "libusb_handle_events_timeout_completed: %s\n", usb_strerror(err));
441 errno = EIO;
442 return -1;
443 }
444 }
445
446 errno = 0;
447 if (quit) {
448 fprintf(stderr, "closing device-connection due to error %d\n", quit);
449 errno = quit;
450 }
451
452 return -1;
453 }
454
455 void hmcfgusb_close(struct hmcfgusb_dev *dev)
456 {
457 int err;
458
459 if (dev->transfer) {
460 libusb_cancel_transfer(dev->transfer);
461 }
462
463 err = libusb_release_interface(dev->usb_devh, INTERFACE);
464 if ((err != 0)) {
465 fprintf(stderr, "Can't release interface: %s\n", usb_strerror(err));
466 }
467
468 libusb_close(dev->usb_devh);
469 free(dev->pfd);
470 free(dev);
471
472 libusb_exit(NULL);
473 }
474
475 void hmcfgusb_set_debug(int d)
476 {
477 debug = d;
478 }
Impressum, Datenschutz