USBUART
A library for reading/wring data via USB-UART adapters
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Modules
core.cpp
Go to the documentation of this file.
1 
9 /* This file is part of USBUART Library. http://usbuart.info/
10  *
11  * Copyright (C) 2016 Eugene Hutorny <eugene@hutorny.in.ua>
12  *
13  * The USBUART Library is free software; you can redistribute it and/or
14  * modify it under the terms of the GNU Lesser General Public License v2
15  * as published by the Free Software Foundation;
16  *
17  * The USBUART Library is distributed in the hope that it will be useful,
18  * but WITHOUT ANY WARRANTY; without even the implied warranty of
19  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
20  * See the GNU Lesser General Public License for more details.
21  *
22  * You should have received a copy of the GNU Lesser General Public License
23  * along with the USBUART Library; if not, see
24  * <http://www.gnu.org/licenses/gpl-2.0.html>.
25  */
26 
27 #include <stdarg.h>
28 #include <cstring>
29 #include <cerrno>
30 #include <algorithm>
31 #include <functional>
32 #include <exception>
33 #include <system_error>
34 #include <poll.h>
35 #include <fcntl.h>
36 #include <unistd.h>
37 #include <libusb.h>
38 #include "usbuart.hpp"
39 #include "vector_lock.hpp"
40 
41 //TODO ??? set limit max packet size per USB capabilities (64/512)
42 //FIXME flush files before terminating
43 //FIXME do not close channel immediately on readpipe EOF but let the transfer to complete
44 //TODO keep channel active if either read or write part works
45 //TODO add set protocol method
46 //TODO add option to call libusb_detach_kernel_driver
47 //TODO uartcat needs an EOF indication that all data were read to close
48 //TODO setlog level
49 //TODO gracefully close libusb when there is ongoing activity
50 //TODO method to return channel status (overrun and others)
51 
52 using namespace std;
53 
54 inline bool operator==(const pollfd& p, int fd) noexcept {
55  return p.fd == fd;
56 }
57 inline bool operator==(const pollfd& a, const pollfd& b) noexcept {
58  return a.fd == b.fd;
59 }
60 
61 namespace usbuart { class file_channel; }
62 bool operator==(const usbuart::file_channel* ch, const pollfd&) noexcept;
63 
64 extern int linux_enumerate_device(struct libusb_context *ctx,
65  uint8_t busnum, uint8_t devaddr, const char *sysfs_dir);
66 
67 namespace usbuart {
68 
69 /***************************************************************************/
70 namespace util {
71 template<typename A, typename V>
72 inline auto find(const A& a, V v) {
73  return std::find(a.begin(), a.end(), v);
74 }
75 
76 template<typename A, typename V>
77 inline auto erase(A& a, V v) {
78  return a.erase(std::remove(a.begin(), a.end(), v), a.end());
79 }
80 
81 }
82 
83 
84 template<class C>
85 struct destructor {
86  static void release(C* p) noexcept { if( p ) delete p; }
87 };
88 
89 template<typename T>
90 struct freer {
91  static void release(T* p) noexcept { if( p ) free(p); }
92 };
93 
94 template<>
95 struct destructor<libusb_transfer> {
96  static void release(libusb_transfer* p) noexcept {
97  if( p ) libusb_free_transfer(p);
98  }
99 };
100 
101 template<>
102 struct destructor<libusb_device_handle> {
103  static void release(libusb_device_handle* p) noexcept {
104  if( p ) libusb_close(p);
105  }
106 };
107 
113 template<typename T>
114 struct transaction {
115  typedef typename conditional<
116  is_class<T>::value, destructor<T>, freer<T> >::type cleanup;
117  inline transaction(bool& result, void* t) throw(error_t)
118  : transaction(result, (T*) t) {}
119  inline transaction(bool& result, T* t) throw(error_t)
120  : resource(t), success(result) {
121  if( resource == nullptr ) {
122  result = false;
123  throw error_t::out_of_memory;
124  }
125  }
126  inline ~transaction() {
127  if( ! success ) cleanup::release(resource);
128  }
129  inline operator T*() const noexcept { return resource; }
130  inline T* operator->() const noexcept { return resource; }
131 private:
132  T* const resource;
133  bool& success;
134 };
135 
136 static constexpr timeval maketimeval(int ms) noexcept {
137  return ms < 0
138  ? (timeval{0,0})
139  : (timeval{ ms / 1000 , (ms % 1000) * 1000 });
140 }
141 
142 static inline void throw_if(bool bad, const char * tag, const char* msg)
143  throw(error_t) {
144  if( ! bad ) return;
145  log.e(tag, "invalid parameter %s", msg);
146  throw error_t::invalid_param;
147 }
148 
149 static void validate(const eia_tia_232_info& i) throw(error_t) {
150  throw_if(i.databits < 5 || i.databits > 9, __, "databits");
151  throw_if(i.parity > parity_t::space, __, "parity");
152  throw_if(i.stopbits > stop_bits_t::two, __, "stopbits");
153  throw_if(i.flowcontrol > flow_control_t::xon_xoff, __, "flowcontrol");
154  throw_if(i.baudrate == 0, __, "flowcontrol");
155 }
156 
157 static void validate(const channel& ch) throw(error_t) {
158  throw_if(fcntl(ch.fd_read, F_GETFD)<0, __, "fd_read");
159  throw_if(fcntl(ch.fd_write, F_GETFD)<0, __, "fd_write");
160 }
161 
162 
163 /******************************************************************************/
164 class registry {
165 public:
166  inline void add(const driver::factory* factory) noexcept {
167  lock_guard<mutex> lock(update);
168  list.push_back(factory);
169  }
170  inline void remove(const driver::factory* factory) noexcept {
171  lock_guard<mutex> lock(update);
172  util::erase(list, factory);
173  }
174  inline driver* create(libusb_device_handle* dev,
175  uint8_t id) const throw(error_t){
176  lock_guard<mutex> lock(update);
177  for(auto & factory : list) {
178  driver* drv = factory->create(dev, id);
179  if( drv ) return drv;
180  }
181  throw error_t::not_supported;
182  }
183 private:
184  vector<const driver::factory*> list;
185  mutable mutex update;
186 };
187 
192 static registry& registrar() noexcept {
193  static registry reg;
194  return reg;
195 }
196 /******************************************************************************/
197 driver::factory::factory() noexcept {
198  registrar().add(this);
199 }
200 
201 driver::factory::~factory() noexcept {
202  registrar().remove(this);
203 }
204 
205 driver* driver::factory::create(libusb_device_handle* dev, uint8_t id)
206  const throw(error_t) {
207  return registrar().create(dev, id);
208 }
209 
210 device_id driver::factory::devid(libusb_device_handle* handle) noexcept {
211  libusb_device* dev = libusb_get_device(handle);
212  libusb_device_descriptor desc;
213  if( libusb_get_device_descriptor(dev, &desc) < 0 ) return { 0, 0, 0 };
214  return { desc.idVendor, desc.idProduct, 0 };
215 }
216 
217 void throw_error(const char* tag, int err) throw(error_t) {
218 // log.d(tag,"err=%d",err);
219  switch(err < 0 ? -err : err) {
220  case EAGAIN:
221  case EINTR:
222  log.i(tag,"i/o status %d", err);
223  return;
224  case EBUSY:
225  throw error_t::interface_busy;
226  case EACCES:
227  throw error_t::no_access;
228  default:
229  log.e(tag,"i/o error %d, shutting down", err);
230  throw error_t::io_error;
231  }
232 }
233 
234 static inline void setnonblock(int fd) throw(error_t) {
235  int flags = fcntl(fd, F_GETFL, 0);
236  if( flags < 0 ) throw error_t::fcntl_error;
237  if( fcntl(fd, F_SETFL, flags | O_NONBLOCK) < 0 )
238  throw error_t::fcntl_error;
239 }
240 
241 /******************************************************************************/
242 
243 class file_channel {
244 public:
245  inline file_channel(context::backend& _owner, const channel& ch,
246  driver* _drv) noexcept
247  : owner(_owner)
248  , dev(_drv->handle())
249  , readxfer0(nullptr)
250  , readxfer1(nullptr)
251  , current(nullptr)
252  , writexfer(nullptr)
253  , readpos{0,0}
254  , readxfer_busy{false, false}
255  , writexfer_busy(false)
256  , timeout(5000)
257  , fdrd(ch.fd_read)
258  , fdrw(ch.fd_write)
259  , drv(_drv)
260  , pipein_ready(false)
261  , pipeout_ready(false)
262  , pipein_hangup(false)
263  , pipeout_hangup(false)
264  , device_hangup(false)
265  { set_nonblocking(); }
266 
267  void init() throw(error_t) {
268  bool success = true;
269  transaction<unsigned char> readbuff0(success, malloc(chunksize()));
270  transaction<unsigned char> readbuff1(success, malloc(chunksize()));
271  transaction<unsigned char> writebuff(success, malloc(chunksize()));
272  transaction<libusb_transfer> rdxfer0(success, libusb_alloc_transfer(0));
273  transaction<libusb_transfer> rdxfer1(success, libusb_alloc_transfer(0));
274  transaction<libusb_transfer> wrxfer(success, libusb_alloc_transfer(0));
275 
276  readxfer0 = rdxfer0;
277  readxfer1 = rdxfer1;
278  current = rdxfer0;
279  writexfer = wrxfer;
280  libusb_fill_bulk_transfer(readxfer0, dev, drv->getifc().ep_bulk_in,
281  readbuff0, chunksize() , read_cb, this, timeout);
282 
283  libusb_fill_bulk_transfer(readxfer1, dev, drv->getifc().ep_bulk_in,
284  readbuff1, chunksize() , read_cb, this, timeout);
285 
286  libusb_fill_bulk_transfer(writexfer, dev, drv->getifc().ep_bulk_out,
287  writebuff, 0, write_cb, this, timeout);
288 
289  /* all set, start operations */
290  readxfer_busy[0] = submit_transfer(readxfer0);
291  readxfer_busy[1] = submit_transfer(readxfer1);
292  readpipe();
293  }
294 
295  virtual ~file_channel() noexcept {
296  log.d(__,"this=%p", this);
297  if( writexfer ) { /* init may fail leaving nulls in all pointers */
298  free(writexfer->buffer);
299  libusb_free_transfer(writexfer);
300  }
301  if( readxfer1 ) {
302  free(readxfer1->buffer);
303  libusb_free_transfer(readxfer1);
304  }
305  if( readxfer0 ) {
306  free(readxfer0->buffer);
307  libusb_free_transfer(readxfer0);
308  }
309  delete drv;
310  libusb_close(dev);
311  }
312 
313  virtual bool equals(const channel& ch) noexcept {
314  return ch.fd_read == fdrd || ch.fd_write == fdrw;
315  }
316 
317  inline int _writefd() const noexcept { return fdrw; } //TODO replace with direct access to fdrw
318 
319  inline int _readfd() const noexcept { return fdrd; } //TODO replace with direct access to fdrd
320 
321  inline void poll_request(int fd, bool reading) noexcept;
322 
324  bool close() noexcept {
325  if( writexfer_busy )
326  libusb_cancel_transfer(writexfer);
327  if( readxfer_busy[0] )
328  libusb_cancel_transfer(readxfer0);
329  if( readxfer_busy[1] )
330  libusb_cancel_transfer(readxfer1);
331  pipein_hangup = true;
332  pipeout_hangup = true;
333  return ! (readxfer_busy[0] || readxfer_busy[1] || writexfer_busy);
334  }
335 
336  inline void events() noexcept {
337 // log.d(__,"%p ready %d/%d hangup: %d/%d", this,
338 // pipein_ready, pipeout_ready, pipein_hangup, pipeout_hangup);
339  if( pipein_ready ) readpipe();
340  if( pipeout_ready ) writepipe(current);
341  }
342 
343  inline void reset() throw(error_t) { drv->reset(); }
344  inline void sendbreak() throw(error_t) { drv->sendbreak(); }
345 
346  inline int status() noexcept {
347  return
348  (pipein_hangup ? 0 : status_t::read_pipe_ok ) |
349  (pipeout_hangup ? 0 : status_t::write_pipe_ok) |
350  (device_hangup ? 0 : status_t::usb_dev_ok );
351  }
352 
353 
354  /* possible results of read:
355  * - success (res > 0) - transfer request
356  * - EOF (res = 0 && errno == 0) - request removal
357  * - no data (res = 0 && errno == EAGAIN) - poll request
358  * - interrupted (res < 0 && errno == EINTR) - poll request
359  * - error (res < 0) - request removal
360  *
361  * possible results of write:
362  * - success (res == size) - transfer request
363  * - partial (res >= 0 && res < size) - poll request
364  * - EOF (res < 0 && errno == EPIPE) - request removal
365  * - no data (res <?= 0 && errno == EAGAIN) - poll request
366  * - interrupted (res < 0 && errno == EINTR) - poll request
367  * - error (res < 0 ) - request removal */
368  bool is_error(const char *tag, int res) noexcept { //FIXME consider merging with throw_error
369  switch(errno) {
370  case EAGAIN: return false;
371  case EINTR:
372  log.i(tag, "interrupted with res=%d, attempting to continue", res);
373  return false;
374  case 0:
375  if( res == 0 ) {
376  log.i(tag, "EOF");
377  return false;
378  }
379  default:
380  log.e(tag, "i/o error %d, shutting down\n", errno);
381  }
382  request_removal(false);
383  return true;
384  }
385 
386  void readpipe() noexcept {
387  size_t size;
388  void * buff = getwritebuff(size); /* reading done to USB write buffer */
389 // log.d(__,"size=%d", size);
390  ssize_t res = read(_readfd(), buff, size); /* whatever read from file */
391  if( res <= 0 && is_error(__,res) ) {
392  pipein_hangup = true;
393  return;
394  }
395 // log.d(__,"%ld", (long)res); /* on some platforms sszie_t is long */
396  if( res > 0 ) submit(res); /* submit to USB */
397  else if ( res == 0 ) {
398  pipein_hangup = true;
399 // request_removal(false); /* EOF */
400  }
401  else poll_request(_readfd(), true);
402  }
403 
404 
405  void writepipe(libusb_transfer* transfer) noexcept {
406  size_t size = 0;
407  unsigned char* buff = getreadbuff(transfer, size); /* write from USB read buffer*/
408  if( ! size ) return;
409  ssize_t res = write(_writefd(), buff, size); /* write to file */
410 // log.d(__,"[%d]=\"%*.*s\" -> %d", size, size, size, (char*) buff, res);
411  if( res <= 0 && is_error(__,res) ) {
412  pipeout_hangup = true;
413  return;
414  }
415  if( res > 0 && ! consumed(transfer, res) )
416  poll_request(_writefd(), false);
417  }
418 
419  void set_nonblocking() throw(error_t) {
420  setnonblock(_readfd());
421  setnonblock(_writefd());
422  }
423 
424  inline void set_events(int events, bool read) noexcept {
425  if( events & POLLIN ) pipein_ready = true;
426  if( events & POLLOUT ) pipeout_ready = true;
427  if( events & POLLHUP ) {
428  (read ? pipein_hangup : pipeout_hangup) = true;
429  request_removal(false);
430  }
431  }
432 
433  inline void request_removal(bool enforce) noexcept;
434 
435  bool error_callback(libusb_transfer* transfer) noexcept {
436  if( transfer == readxfer0 ) readxfer_busy[0] = false;
437  if( transfer == readxfer1 ) readxfer_busy[1] = false;
438  if( transfer == writexfer ) writexfer_busy = false;
439  switch( transfer->status ) {
440  case LIBUSB_TRANSFER_CANCELLED:
441  case LIBUSB_TRANSFER_NO_DEVICE:
442  request_removal(true);
443  case LIBUSB_TRANSFER_TIMED_OUT:
444  case LIBUSB_TRANSFER_COMPLETED:
445  return false;
446  case LIBUSB_TRANSFER_ERROR:
447  case LIBUSB_TRANSFER_STALL:
448  case LIBUSB_TRANSFER_OVERFLOW:
449  //TODO how to handle these errors
450  log.e(__,"transfer severe error %s", libusb_error_name(transfer->status));
451  request_removal(true);
452  return true;
453  }
454  log.w(__,"transfer error %s", libusb_error_name(transfer->status));
455  return false;
456  }
457 
458 
459  static void read_cb(libusb_transfer* transfer) noexcept {
460  file_channel * chnl = (file_channel*) transfer->user_data;
461  if( chnl ) {
462  if( transfer->status == LIBUSB_TRANSFER_COMPLETED ||
463  chnl->error_callback(transfer) )
464  chnl->read_callback(transfer);
465  }
466  else log.e(__, "broken callback in transfer %p",transfer);
467  }
468 
469  static void write_cb(libusb_transfer* transfer) noexcept {
470  file_channel* chnl = (file_channel*) transfer->user_data;
471  if( chnl ) {
472  if( transfer->status == LIBUSB_TRANSFER_COMPLETED ||
473  chnl->error_callback(transfer) )
474  chnl->write_callback(transfer);
475  }
476  else log.e(__, "broken callback in transfer %p",transfer);
477  }
478 
479  inline size_t chunksize() const noexcept {
480  return drv->getifc().chunk_size; //TODO driver may opt chunk_size
481  }
482 
483  bool submit_transfer(libusb_transfer* transfer) noexcept {
484 // if( transfer->actual_length > 2 )
485 // log.d(__,"length=%d", transfer->length);
486  int err;
487  switch( err=libusb_submit_transfer(transfer) ) {
488  case 0:
489  return true;
490  case LIBUSB_ERROR_NO_DEVICE:
491  log.w(__, "NO DEVICE");
492  break;
493  default:
494  log.e(__,"libusb_submit_transfer failed with error %d: %s",
495  err, libusb_error_name(err));
496  }
497  request_removal(true);
498  return false;
499  }
500 
501  void read_callback(libusb_transfer* readxfer) noexcept {
502 // if( readxfer->actual_length > 2 )
503 // log.d(__,"actual_length=%d readpos={%d,%d}", readxfer->actual_length, readpos[0], readpos[1]);
504  drv->read_callback(readxfer, readpos[readxfer == readxfer1]);
505  if( pipeout_hangup ) return;
506  if( readpos[readxfer == readxfer1] >= readxfer->actual_length ) {
507  readxfer_busy[readxfer == readxfer1] = submit_transfer(readxfer);
508  } else {
509  readxfer_busy[readxfer == readxfer1] = false;
510  writepipe(readxfer);
511  }
512  }
513 
514  void write_callback(libusb_transfer*) noexcept {
515 // log.d(__,"actual_length=%d", writexfer->actual_length);
516  if( pipein_hangup ) return;
517  if( writexfer->actual_length < writexfer->length ) {
518  if( writexfer->actual_length != 0 )
519  memmove(writexfer->buffer,
520  writexfer->buffer + writexfer->actual_length,
521  writexfer->length - writexfer->actual_length);
522  log.i(__,"partially complete transfer %d/%d",
523  writexfer->actual_length, writexfer->length);
524  writexfer_busy = submit_transfer(writexfer);
525  } else {
526  drv->write_callback(writexfer);
527  writexfer_busy = false;
528  readpipe();
529  }
530  }
531 
532  inline unsigned char* getreadbuff(libusb_transfer* readxfer,
533  size_t& size) const noexcept {
534  if( readxfer_busy[readxfer == readxfer1] ) {
535  log.w(__,"accessing busy read transfer");
536  size = 0;
537  return nullptr;
538  }
539  size = readxfer->actual_length - readpos[readxfer == readxfer1];
540  return readxfer->buffer + readpos[readxfer == readxfer1];
541  }
542 
543  unsigned char* getwritebuff(size_t& size) const noexcept {
544  if( writexfer_busy ) {
545  size = 0;
546  log.w(__,"accessing busy write transfer");
547  return nullptr;
548  }
549  size = chunksize();
550  return writexfer->buffer;
551  }
552 
553  inline void submit(size_t size) noexcept {
554 // log.d(__,"size=%d", size);
555  if( writexfer_busy ) {
556  log.e(__,"wrong state");
557  }
558  writexfer->length = size;
559  writexfer_busy = submit_transfer(writexfer);
560  }
561 
562  bool consumed(libusb_transfer* readxfer, size_t size) noexcept {
563 // log.d(__,"size=%d", size);
564  if( readxfer_busy[readxfer == readxfer1] ) {
565  log.e(__, "wrong state of readxfer %p", readxfer);
566  return false;
567  }
568  auto& pos(readpos[readxfer == readxfer1]);
569  pos += size;
570 // if( pos > readxfer->actual_length )
571 // log.d(__, "readpos > readxfer->actual_length ");
572  if( pos >= readxfer->actual_length ) {
573  readxfer_busy[readxfer == readxfer1] = submit_transfer(readxfer);
574  current = readxfer == readxfer1 ? readxfer0 : readxfer1;
575  return true;
576  }
577  return false;
578  }
579 
580  inline bool busy() const noexcept {
581 // log.d(__,"w=%d r={%d,%d}",writexfer_busy,readxfer_busy[0],readxfer_busy[0]);
582  return writexfer_busy || readxfer_busy[0] || readxfer_busy[1];
583  }
584 
585  inline bool operator==(int fd) const noexcept {
586  return fdrd == fd || fdrw == fd;
587  }
588 
589 protected:
590  friend class context::backend;
591  context::backend& owner;
592  libusb_device_handle* const dev;
593  libusb_transfer *readxfer0;
594  libusb_transfer *readxfer1;
595  libusb_transfer *current;
596  libusb_transfer *writexfer;
597  size_t readpos[2];
598  bool readxfer_busy[2];
599  bool writexfer_busy;
600  unsigned timeout;
601  int fdrd;
602  int fdrw;
603  driver* const drv;
604  volatile bool pipein_ready;
605  volatile bool pipeout_ready;
606  volatile bool pipein_hangup;
607  volatile bool pipeout_hangup;
608  volatile bool device_hangup;
609 };
610 
611 
612 class pipe_channel : public file_channel {
613 public:
614  inline pipe_channel(context::backend& _owner, channel& ch, driver* _drv) throw(error_t)
615  : file_channel(_owner
616  , bipipe(ch), _drv)
617  , exrd(ch.fd_read)
618  , exrw(ch.fd_write) {}
619  ~pipe_channel() noexcept {
620 // log.d(__,"!");
621  ::close(exrd);
622  ::close(fdrw);
623  ::close(fdrd);
624  ::close(exrw);
625  }
626  bool equals(const channel& ch) noexcept {
627  return ch.fd_read == exrd || ch.fd_write == exrw;
628  }
629 private:
630  int exrd;
631  int exrw;
632  struct bipipe : channel {
633  inline bipipe(channel& ex) throw(error_t) {
634  int a[2], b[2];
635  if( ::pipe(a) ) throw error_t::pipe_error;
636  if( ::pipe(b) ) {
637  ::close(a[0]);
638  ::close(a[1]);
639  throw error_t::pipe_error;
640  }
641  fd_read = a[0];
642  fd_write = b[1];
643  ex.fd_read = b[0];
644  ex.fd_write = b[1];
645  }
646  };
647 
648 };
649 
650 
651 /***************************************************************************/
652 
653 class context::backend {
654 public:
655  backend() throw(error_t) {
656  if( int err = libusb_init(&ctx) ) {
657  log.e(__,"libusb_error %d : %s", err, libusb_error_name(err));
658  throw error_t::libusb_error;
659  }
660  }
661  ~backend() {
662  log.d(__,"this=%p", this);
663  while( child_list.size() ) {
664  auto & child = child_list.back();
665  request_removal(child);
666  child_list.pop_back();
667  child->close();
668  }
669  cleanup();
670  static constexpr int N = 5;
671  for(int i = N; i && delete_list.size(); --i) {
672 // log.d(__,"delete_list.size()=%d count=%d", delete_list.size(),i);
673  handle_libusb_events((N+1-i)*100);
674  cleanup();
675  }
676  libusb_exit(ctx);
677  }
678 
679  file_channel* find(const channel& ch) noexcept {
680  for(auto i : child_list) {
681  log.d(__,"i=%p", (file_channel*) i);
682  if( i != nullptr && i->equals(ch) ) {
683  if( util::find(delete_list, i) == delete_list.end())
684  return i;
685  }
686  }
687  return nullptr;
688  }
689 
690  inline int attach(device_id id, channel ch,
691  const eia_tia_232_info& pi) throw(error_t) {
692  validate(pi);
693  validate(ch);
694  return attach(find(id), id.ifc, ch, pi);
695  }
696 
697  inline int attach(device_addr addr, channel ch,
698  const eia_tia_232_info& pi) throw(error_t) {
699  validate(pi);
700  validate(ch);
701  return attach(find(addr), addr.ifc, ch, pi);
702  }
703 
704  int attach(libusb_device* dev, uint8_t ifc, channel& ch,
705  const eia_tia_232_info& pi, bool pipes = false) throw(error_t) {
706  bool ok1 = false, ok2 = false;
707  if( dev == nullptr ) return -error_t::no_device;
708  transaction<driver> drv(ok1, create(dev, ifc));
709  transaction<file_channel> child(ok2, (pipes ?
710  new pipe_channel(*this, ch, drv):new file_channel(*this, ch, drv)));
711  ok1 = true;
712  log.i(__,"channel {%d,%d}", ch.fd_read, ch.fd_write);
713  drv->setup(pi);
714  child->init();
715  child_list.push_back(child);
716  ok2 = true;
717  return +error_t::success;
718  }
719 
720 
721  inline int pipe(device_id id, channel& ch,
722  const eia_tia_232_info& pi) throw(error_t) {
723  validate(pi);
724  return attach(find(id), id.ifc, ch, pi, true);
725  }
726 
727  inline int pipe(device_addr ba, channel& ch,
728  const eia_tia_232_info& pi) throw(error_t) {
729  validate(pi);
730  return attach(find(ba), ba.ifc, ch, pi, true);
731  }
732 
733  void append_poll_list(vector<pollfd>& list) noexcept {
734  const libusb_pollfd **pollfds = libusb_get_pollfds(ctx);
735  const libusb_pollfd **i = pollfds;
736  while( *i ) {
737  list.push_back({(*i)->fd, (*i)->events, 0});
738  ++i;
739  }
740  libusb_free_pollfds(pollfds);
741  }
742 
743  inline int handle_libusb_events(int timeout) noexcept {
744  struct timeval tv = maketimeval(timeout);
745  return libusb_handle_events_timeout(ctx, &tv);
746  }
747 
748 
749  int handle_events(int timeout) throw(error_t) {
750  if( poll_list.size() == 0 ) return handle_libusb_events(timeout);
751  int res = poll_events(timeout);
752  return res >= 0 ? handle_libusb_events(timeout) : res;
753  }
754 
755  int poll_events(int timeout) throw(error_t) {
756  if( poll_list.size() == 0 ) return 0;
757  vector<pollfd> pollfd_list(poll_list);
758  append_poll_list(pollfd_list);
759  int polled = poll(poll_list.data(), poll_list.size(), timeout);
760  if( polled < 0 ) {
761  if( polled == EINVAL ) throw error_t::poll_error;
762  throw_error(__,errno);
763  return polled;
764  }
765  for(auto item = pollfd_list.begin();
766  polled && item != pollfd_list.end(); ++item) {
767  auto child = util::find(child_list, *item);
768  if( child == child_list.end() ) continue;
769  if( item->revents ) {
770  --polled;
771  (*child)->set_events(item->revents, item->fd == (*child)->fdrd);
772  util::erase(poll_list, *item);
773  pending = true;
774  }
775  }
776  return polled;
777  }
778 
779  /* called from a libusb callback, poll_list locked,
780  * poll already quit, so it is safe to add to the poll_list
781  */
782  inline void poll_request(int fd, short int events) noexcept {
783  if( util::find(poll_list, fd) != poll_list.end() ) {
784  log.w(__, "%d already in poll_list", fd);
785  return;
786  }
787  poll_list.push_back({ fd, events, 0 });
788 // log.d(__,"[%d]=%d",poll_list.size()-1,fd);
789  }
790 
791  inline libusb_device* find(const device_addr& addr) const noexcept {
792  return find([addr](libusb_device* dev) -> bool {
793  return libusb_get_bus_number(dev) == addr.busid &&
794  libusb_get_device_address(dev) == addr.devid;
795  });
796  }
797  inline libusb_device* find(const device_id& addr) const noexcept {
798  return find([addr](libusb_device* dev) -> bool {
799  libusb_device_descriptor desc;
800  if( libusb_get_device_descriptor(dev, &desc) < 0 ) return false;
801  return desc.idVendor == addr.vid && desc.idProduct == addr.pid;
802  });
803  }
804 
805  driver* create(libusb_device* dev, uint8_t id) throw(error_t) {
806  libusb_device_handle* devh;
807  int res = libusb_open(dev, &devh);
808  libusb_unref_device(dev); /* it was refed in find */
809  if( res ) {
810  int err = errno;
811  log.i(__,"libusb_open fail (%d) %s%s%s", res,
812  libusb_error_name(res), (errno ? ", " : ""),
813  (errno ? strerror(errno) : ""));
814  throw_error(__, err == 0 ? res : err);
815  }
816 
817  bool success = false;
818  transaction<libusb_device_handle> begin(success, devh);
819  driver* result = registrar().create(devh,id);
820  success = result != nullptr;
821  return result;
822  }
823 
824  libusb_device* find(function<bool(libusb_device*)> match) const throw(error_t) {
825  libusb_device** list = nullptr;
826  libusb_device* found = nullptr;
827  int n = libusb_get_device_list(ctx, &list);
828  if( n < 0 ) {
829  log.e(__, "libusb_get_device_list fail");
830  throw error_t::libusb_error;
831  }
832  for(int i = 0; i < n && found == nullptr; ++i) {
833  if( match(list[i]) ) {
834  found = list[i];
835  log.i(__, "found %03d/%03d", libusb_get_bus_number(found),
836  libusb_get_device_address(found));
837  break;
838  }
839  }
840  if( found ) libusb_ref_device(found);
841  if( list ) libusb_free_device_list(list, 1);
842  return found;
843  }
844 
845  void close(const channel& chnl) {
846  file_channel* child = find(chnl);
847 // log.d(__,"%p",child);
848  if( child == nullptr ) return;
849  request_removal(child);
850  }
851 
852  inline void request_removal(file_channel* child) noexcept {
853  util::erase(child_list, child);
854  if( util::find(delete_list, child) == delete_list.end() ) {
855 // log.d(__,"child=%p",child);
856  delete_list.push_back(child);
857  }
858  }
859 
860  bool cleanup() noexcept {
861 // if( delete_list.size() > 0 )
862 // log.d(__,"delete_list[0]==%p child_list[0]=%p",delete_list[0],
863 // child_list.size() > 0 ? child_list[0] : nullptr);
864  for(auto i = delete_list.begin(); i < delete_list.end(); ) {
865  file_channel * child = *i;
866  if( child->busy() ) {
867  log.i(__,"busy channel skips cleanup %p",child);
868  ++i;
869  continue;
870  }
871  util::erase(poll_list, child->fdrd);
872  util::erase(poll_list, child->fdrw);
873  child->close();
874  delete child;
875  delete_list.erase(i);
876  }
877  return child_list.size() == 0;
878  }
879 
880  void handle_pending_events() noexcept {
881  for(auto i = child_list.begin(); i != child_list.end(); i++ ) {
882  (*i)->events();
883  }
884  pending = false;
885  }
886 
887  libusb_context* ctx = nullptr;
888  vector_lock<pollfd> poll_list;
889  vector_lock<file_channel*> child_list;
890  vector<file_channel*> delete_list;
891  bool pending = false;
892 };
893 
894 inline void file_channel::poll_request(int fd, bool reading) noexcept {
895  owner.poll_request(fd, reading ? (POLLIN|POLLHUP):(POLLOUT|POLLHUP));
896 }
897 
898 inline void file_channel::request_removal(bool enforce) noexcept {
899  device_hangup = device_hangup || enforce;
900  if( device_hangup || (pipein_hangup && pipeout_hangup) ) {
901  close();
902  owner.request_removal(this);
903  }
904 }
905 
906 /****************************************************************************/
908 inline int safe(const char* tag,function<int()> unsafe) noexcept {
909  try { return unsafe();
910  } catch(error_t err) {
911  if( err != error_t::no_device )
912  log.e(tag,"error %d",+err);
913  return -err;
914  } catch(std::system_error& err) {
915  log.e(tag,"system error %d: %s",err.code().value(), err.what());
916  return err.code().value();
917  } catch(std::exception& err) {
918  log.e(tag,"exception %s", err.what());
919  return -error_t::unknown_error;
920  } catch(...) {
921  log.e(tag,"uknown error!");
922  return -error_t::unknown_error;
923  }
924 }
925 
926 /****************************************************************************/
928 context::context() throw(error_t) : priv(new context::backend()) {}
929 
930 context::~context() noexcept {
931  delete priv;
932 }
933 
934 
935 int context::attach(device_id id, channel ch,
936  const eia_tia_232_info& pi) noexcept {
937  return safe(__,[&]{ return priv->attach(id,ch,pi); });
938 }
939 
940 int context::attach(device_addr ba, channel ch, const eia_tia_232_info& pi) noexcept {
941  return safe(__,[&]{ return priv->attach(ba,ch,pi); });
942 }
943 
944 int context::pipe(device_id id, channel& ch,
945  const eia_tia_232_info& pi) noexcept {
946  return safe(__,[&]{ return priv->pipe(id,ch,pi); });
947 }
948 
949 int context::pipe(device_addr ba, channel& ch,
950  const eia_tia_232_info& pi) noexcept {
951  return safe(__,[&]{ return priv->pipe(ba,ch,pi); });
952 }
953 
955 void context::close(channel ch) noexcept {
956  safe(__,[&]{
957  lock_guard<decltype(priv->child_list)> lock(priv->child_list);
958  priv->close(ch);
959  return 0;
960  });
961 }
962 
964 int context::reset(channel ch) noexcept {
965  return safe(__,[&]{
966  shared_guard<decltype(priv->child_list)> lock(priv->child_list);
967  file_channel* child = priv->find(ch);
968  if( child == nullptr ) return -error_t::no_channel;
969  child->reset();
970  return +error_t::success;
971  });
972 }
973 
975 int context::status(channel ch) noexcept {
976  return safe(__,[&]{
977  shared_guard<decltype(priv->child_list)> lock(priv->child_list);
978  file_channel* child = priv->find(ch);
979  return child == nullptr ? -error_t::no_channel : child->status();
980  });
981 }
982 
983 
985 int context::sendbreak(channel ch) noexcept {
986  return safe(__,[&]()->int{
987  shared_guard<decltype(priv->child_list)> lock(priv->child_list);
988  file_channel* child = priv->find(ch);
989  if( child == nullptr ) return -error_t::no_channel;
990  child->sendbreak();
991  return +error_t::success;
992  });
993 }
994 
996 int context::loop(int timeout) noexcept {
997  return safe(__,[&]()->int{
998  int result;
999  {
1000  lock_guard<decltype(priv->poll_list)> lock(priv->poll_list);
1001  result = priv->handle_events(timeout);
1002  }
1003  shared_guard<decltype(priv->child_list)> locked(priv->child_list);
1004  if( priv->pending ) priv->handle_pending_events();
1005  if( priv->delete_list.size() ) {
1006  priv->handle_libusb_events(timeout);
1007  locked.upgrade();
1008  priv->cleanup();
1009  }
1010  return (result == 0 && priv->child_list.size() == 0 )
1011  ? -error_t::no_channels : result;
1012  });
1013 }
1014 
1015 libusb_context* context::native() noexcept {
1016  return priv->ctx;
1017 }
1018 
1019 
1021 context& context::instance() noexcept {
1022  static context _instance;
1023  return _instance;
1024 }
1025 
1026 loglevel_t context::setloglevel(loglevel_t lvl) noexcept {
1027  loglevel_t old = log.level;
1028  log.level = lvl;
1029  return old;
1030 }
1031 
1032 
1033 } /* namespace usbuart */
1034 bool operator==(const usbuart::file_channel* ch, const pollfd& fd) noexcept {
1035  return *ch == fd.fd;
1036 }
Device address in terms bus ID, device number.
Definition: usbuart.h:84
int safe(const char *tag, function< int()> unsafe) noexcept
safe wrapper for unsafe calls
Definition: core.cpp:908
I/O channel, represented by a pair of file descriptors.
Definition: usbuart.h:70
implementation header USBUART Library
USBUART namespace.
Definition: usbuart.h:29
Device ID (Vendor ID/Product ID).
Definition: usbuart.h:91
USBUART API facade class.
Definition: usbuart.h:226
error_t
API Error codes.
Definition: usbuart.h:177