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