aura  0.1
All Data Structures Functions Variables Modules Pages
transport-usb.c
1 #include <aura/aura.h>
2 #include <aura/private.h>
3 #include <aura/usb_helpers.h>
4 #include <libusb.h>
5 
6 struct usb_interrupt_packet {
7  uint8_t pending_evts;
8 } __attribute__((packed));
9 
10 #define usbdev_is_be(pck) (pck->flags & (1<<0))
11 
12 struct usb_info_packet {
13  uint8_t flags;
14  uint16_t num_objs;
15  uint16_t io_buf_size;
16 } __attribute__((packed));
17 
18 struct usb_event_packet {
19  uint16_t id;
20  char data[];
21 } __attribute__((packed));
22 
23 /*
24  * Object Info Packet contains three NULL-terminated strings:
25  * name \00 argfmt \00 retfmt \00
26  */
27 
28 enum device_state {
29  AUSB_DEVICE_SEARCHING,
30  AUSB_DEVICE_INIT,
31  AUSB_DEVICE_DISCOVER,
32  AUSB_DEVICE_OPERATIONAL,
33  AUSB_DEVICE_FAILING,
34  AUSB_DEVICE_RESTART,
35 };
36 
37 /*
38 
39  OFFLINE | ONLINE
40  |
41  search -> init -> discover -> operational
42  | | | |
43  |
44  /\
45  restart
46  /\
47  |
48  failing
49  | <- err <- err <- err
50  Errors reset state to 'searching'
51  */
52 
53 struct usb_dev_info {
54  char *optbuf;
55  struct aura_node *node;
56  uint8_t flags;
57  struct aura_export_table *etbl;
58 
59  int state;
60  int current_object;
61  uint16_t num_objects;
62  uint16_t io_buf_size;
63  int pending;
64 
65  struct libusb_context *ctx;
66  libusb_device_handle *handle;
67 
68  struct aura_buffer *current_buffer;
69  unsigned char *ctrlbuf;
70  struct libusb_transfer* ctransfer;
71  bool cbusy;
72  bool ibusy;
73  bool itransfer_enabled;
74  struct libusb_transfer* itransfer;
75  unsigned char ibuffer[8]; /* static interrupt buffer */
76 
77  /* Usb device string */
78  struct ncusb_devwatch_data dev_descr;
79 };
80 
81 enum usb_requests {
82  RQ_GET_DEV_INFO,
83  RQ_GET_OBJ_INFO,
84  RQ_GET_EVENT,
85  RQ_PUT_CALL
86 };
87 
88 
89 /* Error handling stuff */
90 #define failing(inf) (inf->state == AUSB_DEVICE_FAILING)
91 
92 static void usb_panic_and_reset_state(struct aura_node *node)
93 {
94  struct usb_dev_info *inf = aura_get_transportdata(node);
95 
96  slog(4, SLOG_DEBUG, "usb: pending transfers: %s %s",
97  inf->ibusy ? "interrupt " : "",
98  inf->cbusy ? "control" : "" );
99 
100  if (inf->state != AUSB_DEVICE_SEARCHING) {
101  slog(4, SLOG_DEBUG, "usb: Entering 'failing' state");
102  inf->state = AUSB_DEVICE_FAILING;
103  if (inf->ibusy) {
104  libusb_cancel_transfer(inf->itransfer);
105  slog(4, SLOG_DEBUG, "usb: cancelling itransfer");
106  }
107  if (inf->cbusy) {
108  libusb_cancel_transfer(inf->ctransfer);
109  slog(4, SLOG_DEBUG, "usb: cancelling ctransfer");
110  }
111  }
112 
113  if ((!inf->ibusy) && (!inf->cbusy)) {
114  slog(4, SLOG_DEBUG, "usb: Cleanup done, entering 'restart' state");
115  /*
116  * We can't call libusb_close from within a callback
117  * some versions of libusb will hang, we'll close
118  * from main loop
119  */
120  /* libusb_close(inf->handle); */
121  inf->state = AUSB_DEVICE_RESTART;
122  }
123 }
124 
125 static void submit_control(struct aura_node *node)
126 {
127  int ret;
128  struct usb_dev_info *inf = aura_get_transportdata(node);
129 
130  ret = libusb_submit_transfer(inf->ctransfer);
131  if (ret!= 0) {
132  slog(0, SLOG_ERROR, "usb: error submitting control transfer");
133  usb_panic_and_reset_state(node);
134  }
135  inf->cbusy=true;
136 }
137 
138 static void submit_interrupt(struct aura_node *node)
139 {
140  struct usb_dev_info *inf = aura_get_transportdata(node);
141  int ret;
142  if (inf->ibusy)
143  return;
144  ret = libusb_submit_transfer(inf->itransfer);
145  inf->ibusy = true;
146  if (ret!= 0) {
147  slog(0, SLOG_ERROR, "usb: error submitting interrupt transfer");
148  usb_panic_and_reset_state(node);
149  }
150 }
151 
152 static int check_interrupt(struct libusb_transfer *transfer)
153 {
154  struct aura_node *node = transfer->user_data;
155  struct usb_dev_info *inf = aura_get_transportdata(node);
156  int ret = 0;
157 
158  inf->ibusy = false;
159 
160  if (failing(inf) || (transfer->status != LIBUSB_TRANSFER_COMPLETED)) {
161  usb_panic_and_reset_state(node);
162  ret = -EIO;
163  }
164  return ret;
165 }
166 
167 static int check_control(struct libusb_transfer *transfer)
168 {
169  struct aura_node *node = transfer->user_data;
170  struct usb_dev_info *inf = aura_get_transportdata(node);
171  int ret = 0;
172 
173  inf->cbusy = false;
174 
175  if (failing(inf) || (transfer->status != LIBUSB_TRANSFER_COMPLETED)) {
176  usb_panic_and_reset_state(node);
177  ret = -EIO;
178  }
179  return ret;
180 }
181 
182 /* FixMe: Boundary checking, hardware may be nasty */
183 static inline char *next(char *nx)
184 {
185  return &nx[strlen(nx)+1];
186 }
187 
188 static void itransfer_enable(struct aura_node *node, bool enable)
189 {
190  struct usb_dev_info *inf = aura_get_transportdata(node);
191 
192  inf->itransfer_enabled = enable;
193  if (failing(inf)) {
194  usb_panic_and_reset_state(node);
195  return;
196  }
197 
198  if (!enable)
199  return;
200 
201  submit_interrupt(node);
202 }
203 
204 static void cb_interrupt(struct libusb_transfer *transfer)
205 {
206  struct aura_node *node = transfer->user_data;
207  struct usb_dev_info *inf = aura_get_transportdata(node);
208 
209  if (0 != check_interrupt(transfer))
210  return;
211 
212  struct usb_interrupt_packet *pck = (struct usb_interrupt_packet *) inf->ibuffer;
213  inf->pending = (int) pck->pending_evts;
214  itransfer_enable(node, inf->itransfer_enabled);
215 }
216 
217 static void request_object(struct aura_node *node, int id);
218 static void cb_parse_object(struct libusb_transfer *transfer)
219 {
220  struct aura_node *node = transfer->user_data;
221  struct usb_dev_info *inf = aura_get_transportdata(node);
222  char is_method;
223  char *name, *afmt, *rfmt;
224 
225  check_control(transfer);
226  name = (char *) libusb_control_transfer_get_data(transfer);
227 
228  is_method = *name++;
229  afmt = next(name);
230  rfmt = next(afmt);
231 
232  slog(4, SLOG_DEBUG, "usb: got %s %s / '%s' '%s'",
233  is_method ? "method" : "event",
234  name, afmt, rfmt);
235 
236  aura_etable_add(inf->etbl, name,
237  is_method ? afmt : NULL,
238  rfmt);
239 
240  if (inf->current_object == inf->num_objects) {
241  slog(4, SLOG_DEBUG, "etable becomes active");
242  aura_etable_activate(inf->etbl);
243  aura_set_status(node, AURA_STATUS_ONLINE);
244  inf->state = AUSB_DEVICE_OPERATIONAL;
245  itransfer_enable(node, true);
246  return;
247  }
248 
249  slog(4, SLOG_DEBUG, "Requesting info about obj %d", inf->current_object);
250  /* Resubmit the transfer for the next round */
251  request_object(node, inf->current_object++);
252 }
253 
254 static void request_object(struct aura_node *node, int id)
255 {
256  struct usb_dev_info *inf = aura_get_transportdata(node);
257  libusb_fill_control_setup(inf->ctrlbuf,
258  LIBUSB_REQUEST_TYPE_VENDOR | LIBUSB_RECIPIENT_DEVICE | LIBUSB_ENDPOINT_IN,
259  RQ_GET_OBJ_INFO,
260  0, id, inf->io_buf_size - LIBUSB_CONTROL_SETUP_SIZE);
261  libusb_fill_control_transfer(inf->ctransfer, inf->handle, inf->ctrlbuf, cb_parse_object, node, 1500);
262  submit_control(node);
263 }
264 
265 static void cb_got_dev_info(struct libusb_transfer *transfer)
266 {
267  struct aura_node *node = transfer->user_data;
268  struct usb_dev_info *inf = aura_get_transportdata(node);
269  struct usb_info_packet *pck;
270  int newbufsize;
271  char *tmp;
272 
273  check_control(transfer);
274  slog(4, SLOG_DEBUG, "usb: Got info packet from device");
275 
276  if (transfer->actual_length < sizeof(struct usb_info_packet)) {
277  slog(0, SLOG_ERROR, "usb: short-read on info packet want %d got %d (API mismatch?)",
278  sizeof(struct usb_info_packet), transfer->actual_length);
279  usb_panic_and_reset_state(node);
280  return;
281  }
282 
283  pck = (struct usb_info_packet *)libusb_control_transfer_get_data(transfer);
284  aura_set_node_endian(node, usbdev_is_be(pck) ?
285  AURA_ENDIAN_BIG : AURA_ENDIAN_LITTLE);
286 
287  newbufsize = pck->io_buf_size + LIBUSB_CONTROL_SETUP_SIZE;
288  inf->num_objects = pck->num_objs;
289 
290  if (newbufsize > inf->io_buf_size) {
291  slog(4, SLOG_DEBUG, "usb: adjusting control buffer size: %d->%d bytes",
292  inf->io_buf_size, newbufsize);
293  tmp = realloc(inf->ctrlbuf, newbufsize);
294  if (!tmp) {
295  slog(0, SLOG_ERROR, "Allocation error while adjusting control buffer size");
296  aura_panic(node);
297  }
298  inf->io_buf_size = newbufsize;
299  inf->ctrlbuf = (unsigned char *)tmp;
300  }
301  slog(4, SLOG_DEBUG, "usb: Device has %d objects, now running discovery", inf->num_objects);
302  inf->etbl = aura_etable_create(node, inf->num_objects);
303  if (!inf->etbl)
304  {
305  slog(0, SLOG_ERROR, "usb: etable creation failed");
306  aura_panic(node);
307  }
308 
309  inf->current_object = 0;
310  request_object(node, inf->current_object++);
311 }
312 
313 static int usb_start_ops(struct libusb_device_handle *hndl, void *arg)
314 {
315  /* FixMe: Reading descriptors is synchronos. This is not needed
316  * often, but leaves a possibility of a flaky usb device to
317  * screw up the event processing.
318  * A proper workaround would be manually reading out string descriptors
319  * from a device in an async fasion in the background.
320  */
321  int ret;
322  struct usb_dev_info *inf = arg;
323  struct aura_node *node = inf->node;
324 
325  inf->handle = hndl;
326 
327  libusb_fill_interrupt_transfer(inf->itransfer, inf->handle, 0x81,
328  inf->ibuffer, 8,
329  cb_interrupt, node, 10000);
330 
331  libusb_fill_control_setup(inf->ctrlbuf,
332  LIBUSB_REQUEST_TYPE_VENDOR | LIBUSB_RECIPIENT_DEVICE | LIBUSB_ENDPOINT_IN,
333  RQ_GET_DEV_INFO,
334  0, 0, inf->io_buf_size);
335  libusb_fill_control_transfer(inf->ctransfer, inf->handle, inf->ctrlbuf, cb_got_dev_info, node, 1500);
336 
337  ret = libusb_submit_transfer(inf->ctransfer);
338  if (ret!= 0) {
339  libusb_close(inf->handle);
340  return -1;
341  }
342 
343  inf->state = AUSB_DEVICE_INIT; /* Change our state */
344  inf->cbusy = true;
345 
346  slog(4, SLOG_DEBUG, "usb: Device opened, info packet requested");
347  return 0;
348 };
349 
350 
351 static void parse_params(struct usb_dev_info *inf)
352 {
353  char *sptr;
354  char *tmp;
355 
356  tmp = strtok_r(inf->optbuf, ":", &sptr);
357  if (!tmp)
358  return;
359  inf->dev_descr.vid = strtoul(tmp, NULL, 16);
360 
361  tmp = strtok_r(NULL, ";", &sptr);
362  if (!tmp)
363  return;
364  inf->dev_descr.pid = strtoul(tmp, NULL, 16);
365 
366  tmp = strtok_r(NULL, ";", &sptr);
367  if (!tmp)
368  return;
369  inf->dev_descr.vendor = tmp;
370 
371  tmp = strtok_r(NULL, ";", &sptr);
372  if (!tmp)
373  return;
374  inf->dev_descr.product = tmp;
375 
376  tmp = strtok_r(NULL, ";", &sptr);
377  if (!tmp)
378  return;
379  inf->dev_descr.serial = tmp;
380 }
381 
382 static int usb_open(struct aura_node *node, const char *opts)
383 {
384  int ret;
385  struct usb_dev_info *inf = calloc(1, sizeof(*inf));
386 
387  if (!inf)
388  return -ENOMEM;
389 
390  ret = libusb_init(&inf->ctx);
391  if (ret != 0)
392  return -EIO;
393 
394  inf->io_buf_size = 256;
395  inf->optbuf = strdup(opts);
396  inf->dev_descr.device_found_func = usb_start_ops;
397  inf->dev_descr.arg = inf;
398  inf->node = node;
399  parse_params(inf);
400 
401  ncusb_start_descriptor_watching(node, inf->ctx);
402  aura_set_transportdata(node, inf);
403 
404  slog(4, SLOG_INFO, "usb: vid 0x%x pid 0x%x vendor %s product %s serial %s",
405  inf->dev_descr.vid, inf->dev_descr.pid, inf->dev_descr.vendor,
406  inf->dev_descr.product, inf->dev_descr.serial);
407 
408  inf->ctrlbuf = malloc(inf->io_buf_size);
409  if (!inf->ctrlbuf)
410  goto err_free_inf;
411  inf->itransfer = libusb_alloc_transfer(0);
412  if (!inf->itransfer)
413  goto err_free_cbuf;
414 
415  inf->ctransfer = libusb_alloc_transfer(0);
416  if (!inf->ctransfer)
417  goto err_free_int;
418 
419  slog(1, SLOG_INFO, "usb: Now looking for a matching device");
420 
421  ncusb_watch_for_device(inf->ctx, &inf->dev_descr);
422 
423  return 0;
424 
425 err_free_int:
426  libusb_free_transfer(inf->itransfer);
427 err_free_cbuf:
428  free(inf->ctrlbuf);
429 err_free_inf:
430  libusb_exit(inf->ctx);
431  free(inf);
432  return -ENOMEM;
433 }
434 
435 static void usb_close(struct aura_node *node)
436 {
437  struct usb_dev_info *inf = aura_get_transportdata(node);
438  inf->itransfer_enabled = false;
439  /* Waiting for pending transfers */
440  slog(4, SLOG_INFO, "usb: Waiting for transport to close...");
441  usb_panic_and_reset_state(node);
442 
443  while (inf->state != AUSB_DEVICE_RESTART)
444  libusb_handle_events(inf->ctx);
445 
446  slog(4, SLOG_INFO, "usb: Cleaning up...");
447  libusb_free_transfer(inf->ctransfer);
448  libusb_free_transfer(inf->itransfer);
449  free(inf->ctrlbuf);
450  if (inf->handle)
451  libusb_close(inf->handle);
452  libusb_exit(inf->ctx);
453  if (inf->optbuf)
454  free(inf->optbuf);
455  free(inf);
456 }
457 
458 static void cb_event_readout_done(struct libusb_transfer *transfer)
459 {
460  struct aura_node *node = transfer->user_data;
461  struct usb_dev_info *inf = aura_get_transportdata(node);
462  struct usb_event_packet *evt;
463  struct aura_buffer *buf = inf->current_buffer;
464  struct aura_object *o;
465 
466  if (0 != check_control(transfer))
467  goto ignore;
468 
469  if (transfer->actual_length < sizeof(struct usb_event_packet))
470  goto ignore;
471 
472 
473  evt = (struct usb_event_packet *) libusb_control_transfer_get_data(transfer);
474  o = aura_etable_find_id(node->tbl, evt->id);
475  if (!o) {
476  slog(0, SLOG_ERROR, "usb: got bogus event id from device %d, resetting", evt->id);
477  goto panic;
478  }
479 
480  if ((transfer->actual_length - LIBUSB_CONTROL_SETUP_SIZE) <
481  (sizeof(struct usb_event_packet) + o->retlen)) {
482  slog(0, SLOG_ERROR, "usb: short read for evt %d: %d bytes expected %d got",
483  evt->id, o->retlen + sizeof(struct usb_event_packet),
484  transfer->actual_length);
485  goto panic;
486  }
487 
488  inf->pending--;
489  slog(4, SLOG_DEBUG, "Event readout completed, %d bytes, %d evt left",
490  transfer->actual_length, inf->pending);
491  buf->object = o;
492  /* Position the buffer at the start of the responses */
493  buf->pos = LIBUSB_CONTROL_SETUP_SIZE + sizeof(struct usb_event_packet);
494  aura_queue_buffer(&node->inbound_buffers, buf);
495  return;
496 
497 panic:
498  usb_panic_and_reset_state(node);
499 ignore:
500  aura_buffer_release(buf);
501  inf->current_buffer = NULL;
502  return;
503 }
504 
505 static void submit_event_readout(struct aura_node *node)
506 {
507  struct usb_dev_info *inf = aura_get_transportdata(node);
508  struct aura_buffer *buf = aura_buffer_request(node, inf->io_buf_size);
509  if (!buf)
510  return; /* Nothing bad, we'll try again later */
511 
512  slog(0, SLOG_DEBUG, "Starting evt readout, max %d bytes, pending %d",
513  inf->io_buf_size, inf->pending);
514  libusb_fill_control_setup((unsigned char *)buf->data,
515  LIBUSB_REQUEST_TYPE_VENDOR | LIBUSB_RECIPIENT_DEVICE | LIBUSB_ENDPOINT_IN,
516  RQ_GET_EVENT,
517  0, 0, buf->size - LIBUSB_CONTROL_SETUP_SIZE);
518  libusb_fill_control_transfer(inf->ctransfer, inf->handle, (unsigned char *)buf->data,
519  cb_event_readout_done, node, 1500);
520  inf->current_buffer = buf;
521  submit_control(node);
522 }
523 
524 static void cb_call_write_done(struct libusb_transfer *transfer)
525 {
526  struct aura_node *node = transfer->user_data;
527  struct usb_dev_info *inf = aura_get_transportdata(node);
528 
529  if (0 != check_control(transfer)) {
530  /* Put it back to queue. Core will deal with it later */
531  goto requeue;
532  }
533 
534  if (transfer->actual_length - LIBUSB_CONTROL_SETUP_SIZE < transfer->length) {
535  slog(0, SLOG_ERROR, "usb: short-write on call packet want %d got %d (API mismatch?)",
536  transfer->length, transfer->actual_length);
537  usb_panic_and_reset_state(node);
538  goto requeue;
539  }
540 
541  aura_buffer_release(inf->current_buffer);
542  inf->current_buffer = NULL;
543  slog(4, SLOG_DEBUG, "Call write done");
544  return;
545 requeue:
546  aura_requeue_buffer(&node->outbound_buffers, inf->current_buffer);
547  inf->current_buffer = NULL;
548 }
549 
550 
551 static void submit_call_write(struct aura_node *node, struct aura_buffer *buf)
552 {
553  struct aura_object *o = buf->object;
554  struct usb_dev_info *inf = aura_get_transportdata(node);
555 
556  slog(4, SLOG_DEBUG, "Writing call %s data to device, %d bytes", o->name, o->arglen);
557  inf->current_buffer = buf;
558  libusb_fill_control_setup((unsigned char *)buf->data,
559  LIBUSB_REQUEST_TYPE_VENDOR | LIBUSB_RECIPIENT_DEVICE | LIBUSB_ENDPOINT_OUT,
560  RQ_PUT_CALL,
561  0, 0, o->arglen);
562  libusb_fill_control_transfer(inf->ctransfer, inf->handle,
563  (unsigned char *) buf->data,
564  cb_call_write_done, node, 1500);
565  submit_control(node);
566 }
567 
568 static void usb_loop(struct aura_node *node, const struct aura_pollfds *fd)
569 {
570  struct aura_buffer *buf;
571  struct usb_dev_info *inf = aura_get_transportdata(node);
572  struct timeval tv = {
573  .tv_sec = 0,
574  .tv_usec = 0
575  };
576 
577  libusb_handle_events_timeout(inf->ctx, &tv);
578 
579  if (inf->cbusy)
580  return;
581 
582  if (inf->state == AUSB_DEVICE_RESTART) {
583  slog(4, SLOG_DEBUG, "usb: transport offlined, starting to look for a device");
584  aura_set_status(node, AURA_STATUS_OFFLINE);
585  libusb_close(inf->handle);
586  inf->handle = NULL;
587  inf->state = AUSB_DEVICE_SEARCHING;
588  ncusb_watch_for_device(inf->ctx, &inf->dev_descr);
589  return;
590  }
591 
592  if (inf->state == AUSB_DEVICE_OPERATIONAL) {
593  if (inf->pending)
594  submit_event_readout(node);
595  else if ( (buf = aura_dequeue_buffer(&node->outbound_buffers)) ) {
596  submit_call_write(node, buf);
597  }
598  }
599 }
600 
601 static struct aura_transport usb = {
602  .name = "usb",
603  .open = usb_open,
604  .close = usb_close,
605  .loop = usb_loop,
606  .buffer_overhead = LIBUSB_CONTROL_SETUP_SIZE, /* Offset for usb SETUP structure */
607  .buffer_offset = LIBUSB_CONTROL_SETUP_SIZE
608 };
609 
610 AURA_TRANSPORT(usb);
void aura_requeue_buffer(struct list_head *head, struct aura_buffer *buf)
Definition: queue.c:62
void aura_set_status(struct aura_node *node, int status)
Definition: aura.c:808
struct aura_buffer * aura_buffer_request(struct aura_node *nd, int size)
Definition: buffer.c:40
struct aura_buffer * aura_dequeue_buffer(struct list_head *head)
Definition: queue.c:45
const char * name
Required.
Definition: aura.h:168
static void aura_set_transportdata(struct aura_node *node, void *udata)
Definition: inlines.h:60
int pos
Definition: aura.h:339
static void * aura_get_transportdata(struct aura_node *node)
Definition: inlines.h:69
int size
Definition: aura.h:337
void aura_queue_buffer(struct list_head *list, struct aura_buffer *buf)
Definition: queue.c:16
void __attribute__((noreturn)) aura_panic(struct aura_node *node)
Definition: panic.c:33
void aura_buffer_release(struct aura_buffer *buf)
Definition: buffer.c:80
void aura_set_node_endian(struct aura_node *node, enum aura_endianness en)
Definition: aura.c:870
char * data
Definition: aura.h:347
struct aura_object * object
Definition: aura.h:341