68 fpUsbContext(nullptr),
69 fpUsbDevList(nullptr),
72 fLoopState(kLoopStateStopped)
98 if (!
fUrl.
Set(url,
"|trace|noinit|",
"cuff", emsg))
return false;
103 emsg.
Init(
"RlinkPortCuff::Open()",
104 string(
"libusb_init() failed: ") +
113 if (libusb_pollfds_handle_timeouts(
fpUsbContext) == 0) {
114 emsg.
Init(
"RlinkPortCuff::Open()",
115 string(
"libusb_pollfds_handle_timeouts == 0 : "
116 "this program will not run on this legacy system"));
126 char* env_vid = ::getenv(
"RETRO_FX2_VID");
127 char* env_pid = ::getenv(
"RETRO_FX2_PID");
128 if (env_vid && ::strlen(env_vid) == 4 &&
129 env_pid && ::strlen(env_pid) == 4) {
130 fUrl.
SetPath(
string(env_vid) +
":" +
string(env_pid));
132 emsg.
Init(
"RlinkPortCuff::Open()",
133 "RETRO_FX2_VID/PID not or ill defined");
140 libusb_device* mydev =
nullptr;
143 string busnam =
fUrl.
Path().substr(1,3);
144 string devnam =
fUrl.
Path().substr(5,3);
145 unsigned long busnum;
146 unsigned long devnum;
154 if (libusb_get_bus_number(udev) == busnum &&
155 libusb_get_device_address(udev) == devnum) {
161 string vennam =
fUrl.
Path().substr(0,4);
162 string pronam =
fUrl.
Path().substr(5,4);
163 unsigned long vennum;
164 unsigned long pronum;
172 libusb_device_descriptor devdsc;
173 libusb_get_device_descriptor(udev, &devdsc);
174 if (devdsc.idVendor==vennum && devdsc.idProduct==pronum) {
179 emsg.
Init(
"RlinkPortCuff::Open()",
180 string(
"invalid usb path '") +
fUrl.
Path() +
181 "', not '/bus/dev' or 'vend:prod'");
186 if (mydev ==
nullptr) {
187 emsg.
Init(
"RlinkPortCuff::Open()",
188 string(
"no usb device '") +
fUrl.
Path() +
"', found'");
196 emsg.
Init(
"RlinkPortCuff::Open()",
197 string(
"opening usb device '") +
fUrl.
Path() +
"', failed: " +
202 if (
TraceOn()) cout <<
"libusb_open ok for '" <<
fUrl.
Path() <<
"'" << endl;
207 emsg.
Init(
"RlinkPortCuff::Open()",
208 string(
"failed to claim '") +
fUrl.
Path() +
"': " +
232 const libusb_pollfd** plist = libusb_get_pollfds(
fpUsbContext);
233 for (
auto p = plist; *p !=0; p++) {
254 if (
TraceOn()) cout <<
"Close() started" << endl;
258 if (
TraceOn()) cout <<
"Close() ended" << endl;
289 libusb_set_pollfd_notifiers(
fpUsbContext,
nullptr,
nullptr,
nullptr);
307 irc = ::pipe(pipefd);
309 emsg.
InitErrno(
"RlinkPortCuff::OpenPipe()",
"pipe() failed: ", errno);
329 libusb_transfer* t = libusb_alloc_transfer(0);
332 t->flags = LIBUSB_TRANSFER_FREE_BUFFER;
333 t->endpoint =
static_cast<unsigned char>(
kUSBReadEP|0x80);
334 t->type = LIBUSB_TRANSFER_TYPE_BULK;
336 t->status = LIBUSB_TRANSFER_COMPLETED;
337 t->buffer =
reinterpret_cast<unsigned char*
>(::malloc(
kUSBBufferSize));
339 t->actual_length = 0;
343 int irc = libusb_submit_transfer(t);
344 if (irc)
BadUSBCall(
"RlinkPortCuff::Driver()",
345 "libusb_submit_transfer()", irc);
350 if (
TraceOn()) cout <<
"event loop started" << endl;
354 if (irc==-1 && errno==EINTR)
continue;
356 cout <<
"poll() -> " << irc <<
" :";
357 for (
size_t i=0; i<
fPollFds.size(); i++)
364 if (irc < 0)
BadSysCall(
"RlinkPortCuff::Driver()",
"poll()", irc);
366 if (
fPollFds[0].revents & POLLHUP) {
368 }
else if (
fPollFds[0].revents & POLLIN) {
375 if (
TraceOn()) cout <<
"event loop ended, cleanup started" << endl;
387 if (irc==-1 && errno==EINTR)
continue;
389 if (irc < 0)
BadSysCall(
"RlinkPortCuff::Driver()",
"poll()", irc);
393 throw Rexception(
"RlinkPortCuff::Driver()",
"cleanup timeout");
396 if (
TraceOn()) cout <<
"cleanup ended" << endl;
398 }
catch (exception& e) {
399 cout <<
"exception caught in RlinkPortCuff::Driver(): '" << e.what()
417 if (
TraceOn()) cout <<
"write pipe read() -> " << ircs << endl;
418 if (ircs < 0)
BadSysCall(
"RlinkPortCuff::DriverEventWritePipe()",
427 t->length = int(ircs);
428 int irc = libusb_submit_transfer(t);
429 if (irc)
BadUSBCall(
"RlinkPortCuff::DriverEventWritePipe()",
430 "libusb_submit_transfer()", irc);
444 int irc = libusb_handle_events_timeout(
fpUsbContext, &tv);
447 if (irc)
BadUSBCall(
"RlinkPortCuff::DriverEventUSB()",
448 "libusb_handle_events_timeout()", irc);
457 libusb_transfer* t =
nullptr;
462 t = libusb_alloc_transfer(0);
464 t->flags = LIBUSB_TRANSFER_FREE_BUFFER;
465 t->endpoint =
static_cast<unsigned char>(
kUSBWriteEP);
466 t->type = LIBUSB_TRANSFER_TYPE_BULK;
468 t->buffer =
reinterpret_cast<unsigned char*
>(::malloc(
kUSBBufferSize));
473 t->status = LIBUSB_TRANSFER_COMPLETED;
475 t->actual_length = 0;
489 ::clock_gettime(CLOCK_REALTIME, &ts);
490 ::localtime_r(&ts.tv_sec, &tmval);
492 ::snprintf(buf, 20,
"%02d:%02d:%02d.%06d: ",
493 tmval.tm_hour, tmval.tm_min, tmval.tm_sec,
int(ts.tv_nsec)/1000);
506 throw Rexception(meth,
string(text) +
" failed with rc=" + ss.str(),
518 throw Rexception(meth,
string(text) +
" failed with rc=" + ss.str() +
527 const char* etext =
nullptr;
529 if (t->status == LIBUSB_TRANSFER_ERROR) etext =
"ERROR";
530 if (t->status == LIBUSB_TRANSFER_STALL) etext =
"STALL";
531 if (t->status == LIBUSB_TRANSFER_NO_DEVICE) etext =
"NO_DEVICE";
532 if (t->status == LIBUSB_TRANSFER_OVERFLOW) etext =
"OVERFLOW";
534 if (etext == 0)
return;
537 ::snprintf(buf, 1024,
"%s : transfer failure on ep=%d: %s",
538 meth,
int(t->endpoint&(~0x80)), etext);
553 return "LIBUSB_SUCCESS";
554 case LIBUSB_ERROR_IO:
555 return "LIBUSB_ERROR_IO";
556 case LIBUSB_ERROR_INVALID_PARAM:
557 return "LIBUSB_ERROR_INVALID_PARAM";
558 case LIBUSB_ERROR_ACCESS:
559 return "LIBUSB_ERROR_ACCESS";
560 case LIBUSB_ERROR_NO_DEVICE:
561 return "LIBUSB_ERROR_NO_DEVICE";
562 case LIBUSB_ERROR_NOT_FOUND:
563 return "LIBUSB_ERROR_NOT_FOUND";
564 case LIBUSB_ERROR_BUSY:
565 return "LIBUSB_ERROR_BUSY";
566 case LIBUSB_ERROR_TIMEOUT:
567 return "LIBUSB_ERROR_TIMEOUT";
568 case LIBUSB_ERROR_OVERFLOW:
569 return "LIBUSB_ERROR_OVERFLOW";
570 case LIBUSB_ERROR_PIPE:
571 return "LIBUSB_ERROR_PIPE";
572 case LIBUSB_ERROR_INTERRUPTED:
573 return "LIBUSB_ERROR_INTERRUPTED";
574 case LIBUSB_ERROR_NO_MEM:
575 return "LIBUSB_ERROR_NO_MEM";
576 case LIBUSB_ERROR_NOT_SUPPORTED:
577 return "LIBUSB_ERROR_NOT_SUPPORTED";
578 case LIBUSB_ERROR_OTHER:
579 return "LIBUSB_ERROR_OTHER";
581 return "**UNKNOWN**";
604 for (
size_t i=0; i<
fPollFds.size(); ) {
619 if (
TraceOn()) cout <<
"USB write done -> " << t->actual_length << endl;
624 throw Rexception(
"RlinkPortCuff::USBWriteDone()",
625 "BugCheck: fWriteQueuePending disordered");
633 libusb_free_transfer(t);
643 if (
TraceOn()) cout <<
"USB read done -> " << t->actual_length << endl;
648 throw Rexception(
"RlinkPortCuff::USBReadDone()",
649 "BugCheck: fReadQueuePending disordered");
654 if (t->actual_length>0) {
656 size_t(t->actual_length));
657 if (ircs < 0)
BadSysCall(
"RlinkPortCuff::USBReadDone()",
661 t->status = LIBUSB_TRANSFER_COMPLETED;
662 t->actual_length = 0;
663 int irc = libusb_submit_transfer(t);
664 if (irc)
BadUSBCall(
"RlinkPortCuff::USBReadDone()",
665 "libusb_submit_transfer()", irc);
669 libusb_free_transfer(t);
void Init(const std::string &meth, const std::string &text)
FIXME_docs.
void InitErrno(const std::string &meth, const std::string &text, int errnum)
FIXME_docs.
static const int kUSBWriteEP
USB write endpoint.
void PollfdRemove(int fd)
FIXME_docs.
void Cleanup()
FIXME_docs.
int fFdWriteDriver
fd for write (driver end)
static void ThunkPollfdRemove(int fd, void *udata)
FIXME_docs.
virtual void Close()
FIXME_docs.
void USBWriteDone(libusb_transfer *t)
FIXME_docs.
bool OpenPipe(int &fdread, int &fdwrite, RerrMsg &emsg)
FIXME_docs.
libusb_device_handle * fpUsbDevHdl
void DriverEventWritePipe()
FIXME_docs.
void BadUSBCall(const char *meth, const char *text, int rc)
FIXME_docs.
static const size_t kUSBBufferSize
USB buffer size.
void DriverEventUSB()
FIXME_docs.
const char * USBErrorName(int rc)
FIXME_docs.
bool TraceOn()
FIXME_docs.
void USBReadDone(libusb_transfer *t)
FIXME_docs.
void BadSysCall(const char *meth, const char *text, int rc)
FIXME_docs.
std::thread fDriverThread
driver thread
std::deque< libusb_transfer * > fReadQueuePending
std::vector< pollfd > fPollFds
void CheckUSBTransfer(const char *meth, libusb_transfer *t)
FIXME_docs.
std::deque< libusb_transfer * > fWriteQueueFree
RlinkPortCuff()
Default constructor.
virtual ~RlinkPortCuff()
Destructor.
void PollfdAdd(int fd, short events)
FIXME_docs.
libusb_context * fpUsbContext
static void ThunkPollfdAdd(int fd, short events, void *udata)
FIXME_docs.
libusb_device ** fpUsbDevList
static void ThunkUSBWriteDone(libusb_transfer *t)
FIXME_docs.
libusb_transfer * NewWriteTransfer()
FIXME_docs.
std::deque< libusb_transfer * > fWriteQueuePending
static void ThunkUSBReadDone(libusb_transfer *t)
FIXME_docs.
static const size_t kUSBReadQueue
USB read queue length.
static const int kUSBReadEP
USB read endpoint.
virtual bool Open(const std::string &url, RerrMsg &emsg)
FIXME_docs.
int fFdReadDriver
fd for read (driver end)
void CloseFd(int &fd)
FIXME_docs.
bool IsOpen() const
FIXME_docs.
virtual void Close()
FIXME_docs.
bool FindOpt(const std::string &name) const
FIXME_docs.
bool Set(const std::string &url, const std::string &optlist, RerrMsg &emsg)
FIXME_docs.
void SetPath(const std::string &path)
FIXME_docs.
const std::string & Path() const
FIXME_docs.
void Inc(size_t ind, double val=1.)
FIXME_docs.
void Define(size_t ind, const std::string &name, const std::string &text)
FIXME_docs.
Declaration of class ReventLoop.