remove duplicate code
[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
37#define USB_TIMEOUT 10000
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
49static int quit = 0;
50
51/* Not in all libusb-1.0 versions, so we have to roll our own :-( */
52static char * usb_strerror(int e)
53{
54 static char unknerr[256];
55
56 switch (e) {
57 case LIBUSB_SUCCESS:
58 return "Success";
59 case LIBUSB_ERROR_IO:
60 return "Input/output error";
61 case LIBUSB_ERROR_INVALID_PARAM:
62 return "Invalid parameter";
63 case LIBUSB_ERROR_ACCESS:
64 return "Access denied (insufficient permissions)";
65 case LIBUSB_ERROR_NO_DEVICE:
66 return "No such device (it may have been disconnected)";
67 case LIBUSB_ERROR_NOT_FOUND:
68 return "Entity not found";
69 case LIBUSB_ERROR_BUSY:
70 return "Resource busy";
71 case LIBUSB_ERROR_TIMEOUT:
72 return "Operation timed out";
73 case LIBUSB_ERROR_OVERFLOW:
74 return "Overflow";
75 case LIBUSB_ERROR_PIPE:
76 return "Pipe error";
77 case LIBUSB_ERROR_INTERRUPTED:
78 return "System call interrupted (perhaps due to signal)";
79 case LIBUSB_ERROR_NO_MEM:
80 return "Insufficient memory";
81 case LIBUSB_ERROR_NOT_SUPPORTED:
82 return "Operation not supported or unimplemented on this platform";
83 case LIBUSB_ERROR_OTHER:
84 return "Other error";
85 };
86 snprintf(unknerr, sizeof(unknerr), "Unknown error code %d / 0x%02x", e, e);
87 return unknerr;
88}
89
90static libusb_device_handle *hmcfgusb_find() {
91 libusb_device_handle *devh = NULL;
92 libusb_device **list;
93 ssize_t cnt;
94 ssize_t i;
95 int err;
96
97 cnt = libusb_get_device_list(NULL, &list);
98 if (cnt < 0) {
99 fprintf(stderr, "Can't get USB device list: %d\n", (int)cnt);
100 return NULL;
101 }
102
103 for (i = 0; i < cnt; i++){
104 struct libusb_device_descriptor desc;
105
106 err = libusb_get_device_descriptor(list[i], &desc);
107 if (err)
108 continue;
109
110 if ((desc.idVendor == ID_VENDOR) && (desc.idProduct == ID_PRODUCT)) {
111 libusb_device *dev = list[i];
112
113 err = libusb_open(dev, &devh);
114 if (err) {
115 fprintf(stderr, "Can't open device: %s\n", usb_strerror(err));
116 return NULL;
117 }
118
119 err = libusb_detach_kernel_driver(devh, 0);
120 if ((err != 0) && (err != LIBUSB_ERROR_NOT_FOUND)) {
121 fprintf(stderr, "Can't detach kernel driver: %s\n", usb_strerror(err));
122 return NULL;
123 }
124
125 err = libusb_claim_interface(devh, 0);
126 if ((err != 0)) {
127 fprintf(stderr, "Can't claim interface: %s\n", usb_strerror(err));
128 return NULL;
129 }
130
131 return devh;
132 }
133
134 }
135
136 return NULL;
137}
138
139int hmcfgusb_send(struct hmcfgusb_dev *usbdev, unsigned char* send_data, int len, int done)
140{
141 int err;
142 int cnt;
143 int ret;
144
145 err = libusb_interrupt_transfer(usbdev->usb_devh, EP_OUT, send_data, len, &cnt, USB_TIMEOUT);
146 if (err) {
147 fprintf(stderr, "Can't send data: %s\n", usb_strerror(err));
148 if (err == LIBUSB_ERROR_NO_DEVICE)
149 exit(EXIT_FAILURE);
150 return 0;
151 }
152
153 if (done) {
154 err = libusb_interrupt_transfer(usbdev->usb_devh, EP_OUT, send_data, 0, &cnt, USB_TIMEOUT);
155 if (err) {
156 fprintf(stderr, "Can't send data: %s\n", usb_strerror(err));
157 if (err == LIBUSB_ERROR_NO_DEVICE)
158 exit(EXIT_FAILURE);
159 return 0;
160 }
161 }
162
163 return ret;
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
188 transfer->flags = LIBUSB_TRANSFER_SHORT_NOT_OK;
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);
194 free(data_buf);
195 return NULL;
196 }
197
198 return transfer;
199}
200
201struct hmcfgusb_cb_data {
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
211 if (transfer->status != LIBUSB_TRANSFER_COMPLETED) {
212 if (transfer->status != LIBUSB_TRANSFER_TIMED_OUT) {
213 fprintf(stderr, "Interrupt transfer not completed: %d!\n", transfer->status);
214 quit = EIO;
215 return;
216 }
9db2e455 217 } else {
27bb301b
MG
218 cb_data = transfer->user_data;
219 if (cb_data && cb_data->cb) {
220 cb_data->cb(transfer->buffer, transfer->actual_length, cb_data->data);
221 } else {
222 hexdump(transfer->buffer, transfer->actual_length, "RECV> ");
223 }
9db2e455
MG
224 }
225
226 err = libusb_submit_transfer(transfer);
227 if (err != 0) {
228 fprintf(stderr, "Can't re-submit transfer: %s\n", usb_strerror(err));
229 free(transfer->buffer);
230 libusb_free_transfer(transfer);
231 }
232}
233
234struct hmcfgusb_dev *hmcfgusb_init(hmcfgusb_cb_fn cb, void *data)
235{
236 libusb_device_handle *devh = NULL;
237 const struct libusb_pollfd **usb_pfd = NULL;
238 struct hmcfgusb_dev *dev = NULL;
239 struct hmcfgusb_cb_data *cb_data = NULL;
240 int err;
241 int i;
242
243 err = libusb_init(NULL);
244 if (err != 0) {
245 fprintf(stderr, "Can't initialize libusb: %s\n", usb_strerror(err));
246 return NULL;
247 }
248
249 devh = hmcfgusb_find();
250 if (!devh) {
251 fprintf(stderr, "Can't find/open hmcfgusb!\n");
252 return NULL;
253 }
254
255 dev = malloc(sizeof(struct hmcfgusb_dev));
256 if (!dev) {
257 perror("Can't allocate memory for hmcfgusb_dev");
258 return NULL;
259 }
260
261 memset(dev, 0, sizeof(struct hmcfgusb_dev));
262 dev->usb_devh = devh;
263
264 cb_data = malloc(sizeof(struct hmcfgusb_cb_data));
265 if (!cb_data) {
266 perror("Can't allocate memory for hmcfgusb_cb_data");
267 return NULL;
268 }
269
270 memset(cb_data, 0, sizeof(struct hmcfgusb_cb_data));
271
272 cb_data->cb = cb;
273 cb_data->data = data;
274
275 dev->transfer = hmcfgusb_prepare_int(devh, hmcfgusb_interrupt, cb_data);
276 if (!dev->transfer) {
277 fprintf(stderr, "Can't prepare async device io!\n");
278 return NULL;
279 }
280
281 usb_pfd = libusb_get_pollfds(NULL);
282 if (!usb_pfd) {
283 fprintf(stderr, "Can't get FDset from libusb!\n");
284 free(dev);
285 return NULL;
286 }
287
288 dev->n_usb_pfd = 0;
289 for(i = 0; usb_pfd[i]; i++)
290 dev->n_usb_pfd++;
291
292 dev->pfd = malloc(dev->n_usb_pfd * sizeof(struct pollfd));
293 if (!dev->pfd) {
294 perror("Can't allocate memory for poll-fds");
295 return NULL;
296 }
297
298 memset(dev->pfd, 0, dev->n_usb_pfd * sizeof(struct pollfd));
299
300 for (i = 0; i < dev->n_usb_pfd; i++) {
301 dev->pfd[i].fd = usb_pfd[i]->fd;
302 dev->pfd[i].events = usb_pfd[i]->events;
303 dev->pfd[i].revents = 0;
304 }
305
306 free(usb_pfd);
307
308 dev->n_pfd = dev->n_usb_pfd;
309
310 return dev;
311}
312
313int hmcfgusb_add_pfd(struct hmcfgusb_dev *dev, int fd, short events)
314{
315 dev->n_pfd++;
316 dev->pfd = realloc(dev->pfd, dev->n_pfd * sizeof(struct pollfd));
317 if (!dev->pfd) {
318 perror("Can't realloc poll-fds");
319 return 0;
320 }
321
322 dev->pfd[dev->n_pfd-1].fd = fd;
323 dev->pfd[dev->n_pfd-1].events = events;
324 dev->pfd[dev->n_pfd-1].revents = 0;
325
326 return 1;
327}
328
329int hmcfgusb_poll(struct hmcfgusb_dev *dev, int timeout)
330{
331 struct timeval tv;
332 int usb_event = 0;
333 int i;
334 int n;
335 int fd_n;
336 int err;
337
338 errno = 0;
339
340 memset(&tv, 0, sizeof(tv));
341 err = libusb_get_next_timeout(NULL, &tv);
342 if (err < 0) {
343 fprintf(stderr, "libusb_get_next_timeout: %s\n", usb_strerror(err));
344 errno = EIO;
345 return -1;
346 } else if (err == 0) {
347 /* No pending timeout or a sane platform */
348 tv.tv_sec = timeout;
349 } else {
350 if ((tv.tv_sec == 0) && (tv.tv_usec == 0)) {
351 usb_event = 1;
352 }
353 }
354
355 if (!usb_event) {
356 for (i = 0; i < dev->n_pfd; i++) {
357 dev->pfd[i].revents = 0;
358 }
359
360 n = poll(dev->pfd, dev->n_pfd, tv.tv_sec * 1000);
361 if (n < 0) {
362 perror("poll");
363 return -1;
364 } else if (n == 0) {
365 usb_event = 1;
366 } else {
367 for (fd_n = 0; fd_n < dev->n_pfd; fd_n++) {
368 if (dev->pfd[fd_n].revents) {
369 if (fd_n < dev->n_usb_pfd) {
370 usb_event = 1;
371 break;
372 } else {
373 return dev->pfd[fd_n].fd;
374 }
375 }
376 }
377 }
378 }
379
380 if (usb_event) {
381 memset(&tv, 0, sizeof(tv));
382 err = libusb_handle_events_timeout_completed(NULL, &tv, NULL);
383 if (err < 0) {
27bb301b 384 fprintf(stderr, "libusb_handle_events_timeout_completed: %s\n", usb_strerror(err));
9db2e455
MG
385 errno = EIO;
386 return -1;
387 }
388 }
389
390 if (quit)
391 errno = quit;
392
393 return -1;
394}
Impressum, Datenschutz