summaryrefslogtreecommitdiffstats
path: root/src/common/port
diff options
context:
space:
mode:
Diffstat (limited to 'src/common/port')
-rw-r--r--src/common/port/Makefile.am7
-rw-r--r--src/common/port/parallel.cpp247
-rw-r--r--src/common/port/parallel.h70
-rw-r--r--src/common/port/port.cpp98
-rw-r--r--src/common/port/port.h56
-rw-r--r--src/common/port/port.pro10
-rw-r--r--src/common/port/port_base.cpp127
-rw-r--r--src/common/port/port_base.h59
-rw-r--r--src/common/port/serial.cpp523
-rw-r--r--src/common/port/serial.h113
-rw-r--r--src/common/port/usb_port.cpp411
-rw-r--r--src/common/port/usb_port.h70
12 files changed, 1791 insertions, 0 deletions
diff --git a/src/common/port/Makefile.am b/src/common/port/Makefile.am
new file mode 100644
index 0000000..d1d2ce8
--- /dev/null
+++ b/src/common/port/Makefile.am
@@ -0,0 +1,7 @@
+INCLUDES = -I$(top_srcdir)/src $(all_includes)
+METASOURCES = AUTO
+
+noinst_LTLIBRARIES = libport.la
+libport_la_SOURCES = parallel.cpp port.cpp serial.cpp usb_port.cpp \
+ port_base.cpp
+libport_la_LDFLAGS = $(all_libraries)
diff --git a/src/common/port/parallel.cpp b/src/common/port/parallel.cpp
new file mode 100644
index 0000000..22b6a4c
--- /dev/null
+++ b/src/common/port/parallel.cpp
@@ -0,0 +1,247 @@
+/***************************************************************************
+ * Copyright (C) 2005-2007 Nicolas Hadacek <hadacek@kde.org> *
+ * Copyright (C) 2003-2004 Alain Gibaud <alain.gibaud@free.fr> *
+ * Copyright (C) 2002-2003 Stephen Landamore <stephen@landamore.com> *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ ***************************************************************************/
+#include "parallel.h"
+
+#include "common/global/global.h"
+#if defined(HAVE_PPDEV)
+# include <linux/ppdev.h>
+# include <linux/parport.h>
+# include <fcntl.h>
+# include <sys/ioctl.h>
+# include <unistd.h> // needed on some system
+# include <errno.h>
+#elif defined(HAVE_PPBUS)
+# include <sys/param.h>
+# include <machine/cpufunc.h>
+# include <dev/ppbus/ppi.h>
+# include <dev/ppbus/ppbconf.h>
+# include <fcntl.h>
+# include <unistd.h>
+# include <errno.h>
+#endif
+#include "common/common/misc.h"
+
+//-----------------------------------------------------------------------------
+QStringList *Port::Parallel::_list = 0;
+
+Port::IODirs Port::Parallel::probe(const QString &device)
+{
+#if defined(HAVE_PPDEV)
+ int fd = ::open(device.latin1(), O_RDWR);
+ if ( fd<0) return NoIO;
+ if ( ioctl(fd, PPCLAIM)<0 ) {
+ ::close(fd) ;
+ return In;
+ }
+ ioctl(fd, PPRELEASE);
+ ::close(fd);
+ return (In | Out);
+#elif defined(HAVE_PPBUS)
+ int fd = ::open(device.latin1(), O_RDWR);
+ if ( fd<0 ) return NoIO;
+ ::close(fd);
+ return In | Out; // #### can we detect read-only ?
+#else
+ return NoIO;
+#endif
+}
+
+QStringList Port::Parallel::deviceList()
+{
+ QStringList list;
+#if defined(HAVE_PPDEV)
+ // standard parport in user space
+ for(int i = 0; i<8; ++i) list.append(QString("/dev/parport%1").arg(i));
+ // new devfs parport flavour
+ for(int i = 0; i<8; ++i) list.append(QString("/dev/parports/%1").arg(i));
+#elif defined(HAVE_PPBUS)
+ // FreeBSD
+ for(int i = 0; i<8; ++i) list.append(QString("/dev/ppi%1").arg(i));
+#endif
+ return list;
+}
+
+const QStringList &Port::Parallel::probedDeviceList()
+{
+ if ( _list==0 ) {
+ QStringList all = deviceList();
+ _list = new QStringList;
+ for (uint i=0; i<uint(all.count()); i++)
+ if( probe(all[i]) & (In | Out) ) _list->append(all[i]);
+ }
+ return *_list;
+}
+
+bool Port::Parallel::isAvailable()
+{
+#if defined(HAVE_PPDEV) || defined(HAVE_PPBUS)
+ return true;
+#else
+ return false;
+#endif
+}
+
+//-----------------------------------------------------------------------------
+const Port::Parallel::PPinData Port::Parallel::PIN_DATA[Nb_Pins] = {
+ { Control, 0x01, Out, "/DS" }, // !strobe
+ { Data, 0x01, Out, "D0" }, // data 0
+ { Data, 0x02, Out, "D1" }, // data 1
+ { Data, 0x04, Out, "D2" }, // data 2
+ { Data, 0x08, Out, "D3" }, // data 3
+ { Data, 0x10, Out, "D4" }, // data 4
+ { Data, 0x20, Out, "D5" }, // data 5
+ { Data, 0x40, Out, "D6" }, // data 6
+ { Data, 0x80, Out, "D7" }, // data 7
+ { Status, 0x40, In, "/ACK" }, // !ack
+ { Status, 0x80, In, "BUSY" }, // busy
+ { Status, 0x20, In, "PAPER" }, // pout
+ { Status, 0x10, In, "SELin" }, // select
+ { Control, 0x02, Out, "LF" }, // !feed
+ { Status, 0x08, In, "/ERROR" }, // !error
+ { Control, 0x04, Out, "PRIME" }, // !init
+ { Control, 0x08, Out, "SELout" }, // !si
+ { Nb_RequestTypes, 0x00, NoIO, "GND" }, // GND
+ { Nb_RequestTypes, 0x00, NoIO, "GND" }, // GND
+ { Nb_RequestTypes, 0x00, NoIO, "GND" }, // GND
+ { Nb_RequestTypes, 0x00, NoIO, "GND" }, // GND
+ { Nb_RequestTypes, 0x00, NoIO, "GND" }, // GND
+ { Nb_RequestTypes, 0x00, NoIO, "GND" }, // GND
+ { Nb_RequestTypes, 0x00, NoIO, "GND" }, // GND
+ { Nb_RequestTypes, 0x00, NoIO, "GND" } // GND
+};
+
+QValueVector<Port::PinData> Port::Parallel::pinData(IODir dir) const
+{
+ QValueVector<PinData> v;
+ for (uint i=0; i<Nb_Pins; i++) {
+ if ( PIN_DATA[i].dir!=dir ) continue;
+ PinData pd = { i, PIN_DATA[i].label };
+ v.append(pd);
+ }
+ return v;
+}
+
+bool Port::Parallel::isGroundPin(uint pin) const
+{
+ return ( PIN_DATA[pin].label==QString("GND") );
+}
+
+Port::IODir Port::Parallel::ioDir(uint pin) const
+{
+ return PIN_DATA[pin].dir;
+}
+
+const Port::Parallel::RequestData Port::Parallel::REQUEST_DATA[Nb_RequestTypes] = {
+#if defined(HAVE_PPDEV)
+ { PPRCONTROL, PPWCONTROL },
+ { PPRSTATUS, 0 },
+ { PPRDATA, PPWDATA }
+#elif defined(HAVE_PPBUS)
+ { PPIGCTRL, PPISCTRL },
+ { PPIGSTATUS, 0 },
+ { PPIGDATA, PPISDATA }
+#else
+ { 0, 0 },
+ { 0, 0 },
+ { 0, 0 }
+#endif
+};
+
+Port::Parallel::Parallel(const QString &device, Log::Base &base)
+ : Base(base), _fd(-1), _device(device)
+{
+ for (uint i=0; i<Nb_RequestTypes; i++) _images[i] = 0;
+}
+
+bool Port::Parallel::internalOpen()
+{
+#if defined(HAVE_PPDEV)
+ _fd = ::open(_device.latin1(), O_RDWR);
+ if ( _fd<0 ) {
+ setSystemError(i18n("Could not open device \"%1\"").arg(_device));
+ return false;
+ }
+ if ( ioctl(_fd, PPCLAIM)<0 ) {
+ setSystemError(i18n("Could not claim device \"%1\": check it is read/write enabled").arg(_device));
+ return false;
+ }
+#elif defined(HAVE_PPBUS)
+ _fd = ::open(_device.latin1(), O_RDWR);
+ if ( _fd<0 ) {
+ setSystemError(i18n("Could not open device \"%1\"").arg(_device));
+ return false;
+ }
+#endif
+ return true;
+}
+
+void Port::Parallel::internalClose()
+{
+ if ( _fd<0 ) return;
+#if defined(HAVE_PPDEV)
+ ioctl(_fd, PPRELEASE);
+ ::close(_fd);
+#elif defined(HAVE_PPBUS)
+ ::close(_fd);
+#endif
+ _fd = -1;
+}
+
+bool Port::Parallel::setPinOn(uint pin, bool on, LogicType type)
+{
+ if ( _fd<0 ) return false;
+#if defined(HAVE_PPDEV) || defined(HAVE_PPBUS)
+ Q_ASSERT( pin<Nb_Pins );
+ Q_ASSERT( PIN_DATA[pin].dir==Out );
+ RequestType rtype = PIN_DATA[pin].rType;
+ Q_ASSERT( rtype!=Nb_RequestTypes );
+ uchar mask = PIN_DATA[pin].mask;
+ uchar c = (XOR(type==NegativeLogic, on) ? _images[rtype] | mask : _images[rtype] & ~mask);
+ int request = REQUEST_DATA[rtype].write;
+ Q_ASSERT( request!=0 );
+ if ( ioctl(_fd, request, &c)<0 ) {
+ setSystemError(i18n("Error setting pin %1 to %2").arg(PIN_DATA[pin].label).arg(on));
+ return false;
+ }
+ _images[rtype] = c;
+#endif
+ return true;
+}
+
+bool Port::Parallel::readPin(uint pin, LogicType type, bool &value)
+{
+ if ( _fd<0 ) return false;
+#if defined(HAVE_PPDEV) || defined(HAVE_PPBUS)
+ Q_ASSERT( pin<Nb_Pins );
+ Q_ASSERT( PIN_DATA[pin].dir==In );
+ RequestType rtype = PIN_DATA[pin].rType;
+ Q_ASSERT( rtype!=Nb_RequestTypes );
+ int request = REQUEST_DATA[rtype].read;
+ Q_ASSERT( request!=0 );
+ uchar c = 0;
+ if ( ioctl(_fd, request, &c)<0 ) {
+ setSystemError(i18n("Error reading bit on pin %1").arg(PIN_DATA[pin].label));
+ return false;
+ }
+ _images[rtype] = c;
+ value = (type==NegativeLogic ? ~c : c ) & PIN_DATA[pin].mask;
+#endif
+ return true;
+}
+
+void Port::Parallel::setSystemError(const QString &message)
+{
+#if defined(HAVE_PPDEV) || defined(HAVE_PPBUS)
+ log(Log::LineType::Error, message + QString(" (errno=%1).").arg(strerror(errno)));
+#else
+ Q_UNUSED(message);
+#endif
+}
diff --git a/src/common/port/parallel.h b/src/common/port/parallel.h
new file mode 100644
index 0000000..1473db0
--- /dev/null
+++ b/src/common/port/parallel.h
@@ -0,0 +1,70 @@
+/***************************************************************************
+ * Copyright (C) 2005 Nicolas Hadacek <hadacek@kde.org> *
+ * Copyright (C) 2003-2004 Alain Gibaud <alain.gibaud@free.fr> *
+ * Copyright (C) 2002-2003 Stephen Landamore <stephen@landamore.com> *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ ***************************************************************************/
+#ifndef PARALLEL_H
+#define PARALLEL_H
+
+#include "port_base.h"
+#if defined(Q_OS_WIN)
+# undef ERROR
+#endif
+
+namespace Port
+{
+
+class Parallel : public Base
+{
+public:
+ Parallel(const QString &device, Log::Base &base);
+ virtual ~Parallel() { close(); }
+ virtual Description description() const { return Description(PortType::Parallel, _device); }
+
+ static IODirs probe(const QString &device);
+ static const QStringList &probedDeviceList();
+ static bool isAvailable();
+
+ enum Pin { DS = 0, D0, D1, D2, D3, D4, D5, D6, D7, ACK, BUSY, PAPER, SELin,
+ LF, ERROR, PRIME, SELout, P18, P19, P20, P21, P22, P23, P24, P25,
+ Nb_Pins };
+ enum RequestType { Control = 0, Status, Data, Nb_RequestTypes };
+ struct PPinData {
+ RequestType rType;
+ uchar mask;
+ IODir dir;
+ const char *label;
+ };
+ static const PPinData PIN_DATA[Nb_Pins];
+ virtual bool setPinOn(uint pin, bool on, LogicType type);
+ virtual bool readPin(uint pin, LogicType type, bool &value);
+ virtual QValueVector<PinData> pinData(IODir dir) const;
+ virtual bool isGroundPin(uint pin) const;
+ virtual uint groundPin() const { return P25; }
+ virtual IODir ioDir(uint pin) const;
+
+private:
+ int _fd;
+ QString _device;
+ struct RequestData {
+ int read, write;
+ };
+ static const RequestData REQUEST_DATA[Nb_RequestTypes];
+ uchar _images[Nb_RequestTypes];
+
+ static QStringList *_list;
+ static QStringList deviceList();
+
+ virtual bool internalOpen();
+ virtual void internalClose();
+ virtual void setSystemError(const QString &message);
+};
+
+} // namespace
+
+#endif
diff --git a/src/common/port/port.cpp b/src/common/port/port.cpp
new file mode 100644
index 0000000..c56c120
--- /dev/null
+++ b/src/common/port/port.cpp
@@ -0,0 +1,98 @@
+/***************************************************************************
+ * Copyright (C) 2005-2006 Nicolas Hadacek <hadacek@kde.org> *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ ***************************************************************************/
+#include "port.h"
+
+#if defined(Q_WS_WIN)
+# include <sys/timeb.h>
+#else
+# include <sys/time.h>
+#endif
+
+#include "common/global/global.h"
+#include "serial.h"
+#include "parallel.h"
+#include "usb_port.h"
+
+//-----------------------------------------------------------------------------
+void getTime(int &sec, int &usec)
+{
+#if defined (Q_OS_WIN)
+ struct _timeb tb;
+ _ftime (&tb);
+ sec = tb.time;
+ usec = tb.millitm * 1000 + 500;
+#else
+ struct timeval tv;
+ gettimeofday(&tv, 0);
+ usec = tv.tv_usec;
+ sec = tv.tv_sec;
+#endif
+}
+
+void Port::msleep(uint ms)
+{
+ usleep(ms*1000);
+}
+
+// from Brian C Lane's code
+// works better than usleep
+void Port::usleep(uint us)
+{
+ if ( us==0 ) return;
+ int tsec, tusec;
+ getTime(tsec, tusec);
+ int usec = (tusec + us) % 1000000;
+ int sec = tsec + (tusec + us) / 1000000;
+ for (;;) {
+ getTime(tsec, tusec);
+ if ( tsec>sec ) return;
+ if ( tsec==sec && tusec>usec ) return;
+ }
+}
+
+//-----------------------------------------------------------------------------
+const PortType::Data PortType::DATA[Nb_Types] = {
+ { I18N_NOOP("Serial Port"), "serial", true },
+ { I18N_NOOP("Parallel Port"), "parallel", true },
+ { I18N_NOOP("USB Port"), "usb", false }
+};
+
+const char * const Port::IO_DIR_NAMES[3] = { "no_io", "in", "out" };
+
+QStringList Port::probedDeviceList(PortType type)
+{
+ if ( !isAvailable(type) ) return QStringList();
+ switch (type.type()) {
+ case PortType::Serial: return Serial::probedDeviceList();
+ case PortType::Parallel: return Parallel::probedDeviceList();
+ case PortType::USB: return USB::probedDeviceList();
+ case PortType::Nb_Types: break;
+ }
+ return QStringList();
+}
+
+bool Port::isAvailable(PortType type)
+{
+ switch (type.type()) {
+ case PortType::Serial: return Serial::isAvailable();
+ case PortType::Parallel: return Parallel::isAvailable();
+ case PortType::USB: return USB::isAvailable();
+ case PortType::Nb_Types: break;
+ }
+ return false;
+}
+
+PortType Port::findType(const QString &portDevice)
+{
+ FOR_EACH(PortType, type) {
+ if ( !type.data().withDevice ) continue;
+ if ( probedDeviceList(type).contains(portDevice) ) return type;
+ }
+ return PortType::Nb_Types;
+}
diff --git a/src/common/port/port.h b/src/common/port/port.h
new file mode 100644
index 0000000..f385deb
--- /dev/null
+++ b/src/common/port/port.h
@@ -0,0 +1,56 @@
+/***************************************************************************
+ * Copyright (C) 2005-2006 Nicolas Hadacek <hadacek@kde.org> *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ ***************************************************************************/
+#ifndef PORT_H
+#define PORT_H
+
+#include <qstringlist.h>
+
+#include "common/global/global.h"
+#include "common/common/key_enum.h"
+
+//-----------------------------------------------------------------------------
+struct PortTypeData {
+ const char *label, *key;
+ bool withDevice;
+};
+
+BEGIN_DECLARE_ENUM(PortType)
+ Serial = 0, Parallel, USB
+END_DECLARE_ENUM(PortType, PortTypeData)
+
+namespace Port
+{
+//-----------------------------------------------------------------------------
+extern void msleep(uint ms); // in milliseconds
+extern void usleep(uint us); // in microseconds
+
+//-----------------------------------------------------------------------------
+ class Description {
+ public:
+ Description() : type(PortType::Nb_Types) {}
+ Description(PortType ptype, const QString &pdevice) : type(ptype), device(pdevice) {}
+ PortType type;
+ QString device;
+ };
+
+ enum IODir { NoIO = 0, In = 1, Out = 2 };
+ extern const char * const IO_DIR_NAMES[3];
+ Q_DECLARE_FLAGS(IODirs, IODir)
+ Q_DECLARE_OPERATORS_FOR_FLAGS(IODirs)
+ extern QStringList probedDeviceList(PortType type);
+ extern PortType findType(const QString &device);
+ extern bool isAvailable(PortType type);
+ struct PinData {
+ uint pin;
+ const char *label;
+ };
+ enum LogicType { PositiveLogic, NegativeLogic };
+} // namespace
+
+#endif
diff --git a/src/common/port/port.pro b/src/common/port/port.pro
new file mode 100644
index 0000000..f09414b
--- /dev/null
+++ b/src/common/port/port.pro
@@ -0,0 +1,10 @@
+STOPDIR = ../../..
+include($${STOPDIR}/lib.pro)
+
+TARGET = port
+HEADERS += port.h port_base.h parallel.h serial.h usb_port.h
+SOURCES += port.cpp port_base.cpp parallel.cpp serial.cpp usb_port.cpp
+contains(DEFINES, HAVE_USB) {
+ win32:INCLUDEPATH += $${LIBUSB_PATH}\include
+}
+
diff --git a/src/common/port/port_base.cpp b/src/common/port/port_base.cpp
new file mode 100644
index 0000000..7528eeb
--- /dev/null
+++ b/src/common/port/port_base.cpp
@@ -0,0 +1,127 @@
+/***************************************************************************
+ * Copyright (C) 2005-2006 Nicolas Hadacek <hadacek@kde.org> *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ ***************************************************************************/
+#include "port_base.h"
+
+#include "common/global/global.h"
+#include "common/common/misc.h"
+#include "common/common/number.h"
+
+bool Port::Base::open()
+{
+ close();
+ resetError();
+ _closing = false;
+ return internalOpen();
+}
+
+void Port::Base::close()
+{
+ _closing = true;
+ internalClose();
+}
+
+bool Port::Base::send(const char *data, uint size, uint timeout)
+{
+ log(Log::DebugLevel::LowLevel, QString("Sending: \"%1\"").arg(toPrintable(data, size, PrintAlphaNum)));
+ return internalSend(data, size, timeout);
+}
+
+bool Port::Base::receive(uint size, QString &s, uint timeout)
+{
+ QMemArray<uchar> a;
+ if ( !receive(size, a, timeout) ) return false;
+ s.fill(0, size);
+ for (uint i=0; i<size; i++) s[i] = a[i];
+ return true;
+}
+
+bool Port::Base::receive(uint size, QMemArray<uchar> &a, uint timeout)
+{
+ a.resize(size);
+ bool ok = internalReceive(size, (char *)a.data(), timeout);
+ if (ok) log(Log::DebugLevel::LowLevel, QString("Received: \"%1\"").arg(toPrintable(a, PrintAlphaNum)));
+ return ok;
+}
+
+bool Port::Base::receiveChar(char &c, uint timeout)
+{
+ if ( !internalReceive(1, &c, timeout) ) return false;
+ log(Log::DebugLevel::LowLevel, QString("Received: \"%1\"").arg(toPrintable(c, PrintAlphaNum)));
+ return true;
+}
+
+bool Port::Base::setPinOn(uint, bool, LogicType)
+{
+ qFatal("setPinOn not implemented");
+ return false;
+}
+bool Port::Base::readPin(uint, LogicType, bool &)
+{
+ qFatal("readPin not implemented");
+ return 0;
+}
+QValueVector<Port::PinData> Port::Base::pinData(IODir) const
+{
+ qFatal("pinData not implemented");
+ return QValueVector<PinData>();
+}
+bool Port::Base::isGroundPin(uint) const
+{
+ qFatal("isGroundPin not implemented");
+ return false;
+}
+uint Port::Base::groundPin() const
+{
+ qFatal("groundPin not implemented");
+ return 0;
+}
+Port::IODir Port::Base::ioDir(uint) const
+{
+ qFatal("ioType not implemented");
+ return NoIO;
+}
+
+void Port::Base::log(Log::LineType lineType, const QString &message)
+{
+ if ( lineType==Log::LineType::Error && _closing ) return;
+ Log::Base::log(lineType, description().type.label() + ": " + message);
+ if ( lineType==Log::LineType::Error ) close();
+}
+
+void Port::Base::log(Log::DebugLevel level, const QString &message)
+{
+ Log::Base::log(level, description().type.label() + ": " + message);
+}
+
+void Port::Base::logData(const QString &)
+{
+/*
+ QString vs;
+ for (uint i=0; i<s.length(); i++) {
+ char c = s[i];
+ switch (c) {
+ case '\r': vs += "\\r"; break;
+ case '\n': vs += "\\n"; break;
+ case '<': vs += "&lt;"; break;
+ case '>': vs += "&gt;"; break;
+ default: {
+ if ( c>=32 && c<=126 ) vs += c;
+ else {
+ QString tmp;
+ tmp.sprintf("\\x%02x", c);
+ vs += tmp;
+ }
+ break;
+ }
+ }
+ }
+ qDebug("%s", vs.latin1());
+*/
+ //log(Log::Debug, vs);
+}
diff --git a/src/common/port/port_base.h b/src/common/port/port_base.h
new file mode 100644
index 0000000..3ae4787
--- /dev/null
+++ b/src/common/port/port_base.h
@@ -0,0 +1,59 @@
+/***************************************************************************
+ * Copyright (C) 2005-2006 Nicolas Hadacek <hadacek@kde.org> *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ ***************************************************************************/
+#ifndef PORT_BASE_H
+#define PORT_BASE_H
+
+#include "port.h"
+#include "common/global/log.h"
+
+namespace Port
+{
+
+class Base : public Log::Base
+{
+public:
+ Base(Log::Base &base) : Log::Base(&base) {}
+ virtual PortType type() const { return description().type; }
+ virtual Description description() const = 0;
+ bool open();
+ void close();
+
+ virtual void log(Log::LineType kind, const QString &message);
+ virtual void log(Log::DebugLevel level, const QString &message);
+ void logData(const QString &s);
+
+ enum { DEFAULT_TIMEOUT = 3000 }; // 3s
+ bool sendChar(char c, uint timeout = DEFAULT_TIMEOUT) { return send(&c, 1, timeout); }
+ bool send(const char *data, uint size, uint timeout = DEFAULT_TIMEOUT);
+ bool send(const QMemArray<uchar> &a, uint timeout = DEFAULT_TIMEOUT) { return send((const char *)a.data(), a.count(), timeout); }
+ bool receiveChar(char &c, uint timeout = DEFAULT_TIMEOUT);
+ bool receive(uint size, QString &s, uint timeout = DEFAULT_TIMEOUT);
+ bool receive(uint size, QMemArray<uchar> &a, uint timeout = DEFAULT_TIMEOUT);
+
+ virtual bool setPinOn(uint pin, bool on, LogicType type);
+ virtual bool readPin(uint pin, LogicType type, bool &value);
+ virtual QValueVector<PinData> pinData(IODir dir) const;
+ virtual bool isGroundPin(uint pin) const;
+ virtual uint groundPin() const;
+ virtual IODir ioDir(uint pin) const;
+
+protected:
+ virtual bool internalOpen() = 0;
+ virtual void internalClose() = 0;
+ virtual bool internalSend(const char *, uint, uint) { qFatal("Not implemented"); return false; }
+ virtual bool internalReceive(uint, char *, uint) { qFatal("Not implemented"); return false; }
+ virtual void setSystemError(const QString &message) = 0;
+
+private:
+ bool _closing;
+};
+
+} // namespace
+
+#endif
diff --git a/src/common/port/serial.cpp b/src/common/port/serial.cpp
new file mode 100644
index 0000000..b370d22
--- /dev/null
+++ b/src/common/port/serial.cpp
@@ -0,0 +1,523 @@
+/***************************************************************************
+ * Copyright (C) 2005-2006 Nicolas Hadacek <hadacek@kde.org> *
+ * Copyright (C) 2003-2004 Alain Gibaud <alain.gibaud@free.fr> *
+ * Copyright (C) 2002-2003 Stephen Landamore <stephen@landamore.com> *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ ***************************************************************************/
+#include "serial.h"
+
+#ifdef Q_OS_UNIX
+# include <stdio.h>
+# include <fcntl.h>
+# include <sys/time.h>
+# include <sys/types.h>
+# include <sys/stat.h>
+# include <sys/ioctl.h>
+# include <errno.h>
+# include <unistd.h> // needed on some system
+#endif
+#include <qdatetime.h>
+
+//-----------------------------------------------------------------------------
+QStringList *Port::Serial::_list = 0;
+#if defined(Q_OS_UNIX)
+const Port::Serial::Handle Port::Serial::INVALID_HANDLE = -1;
+#elif defined(Q_OS_WIN)
+const Port::Serial::Handle Port::Serial::INVALID_HANDLE = INVALID_HANDLE_VALUE;
+#endif
+
+Port::Serial::Handle Port::Serial::openHandle(const QString &device, IODirs dirs)
+{
+#if defined(Q_OS_UNIX)
+ // open non blocking: avoid missing DCD (comment from xwisp2)
+ int mode = O_NOCTTY | O_NONBLOCK;
+ if ( dirs & In ) {
+ if ( dirs & Out ) mode |= O_RDWR;
+ else mode |= O_RDONLY;
+ } else mode |= O_WRONLY;
+ return ::open(device.latin1(), mode);
+#elif defined(Q_OS_WIN)
+ int mode = 0;
+ if ( dirs & In ) mode |= GENERIC_READ;
+ if ( dirs & Out ) mode |= GENERIC_WRITE;
+ return CreateFileA(device.latin1(), mode, 0, NULL, OPEN_EXISTING, FILE_FLAG_NO_BUFFERING, NULL);
+#endif
+}
+
+void Port::Serial::closeHandle(Handle handle)
+{
+#if defined(Q_OS_UNIX)
+ ::close(handle);
+#elif defined(Q_OS_WIN)
+ CloseHandle(handle);
+#endif
+}
+
+Port::IODirs Port::Serial::probe(const QString &device)
+{
+ Handle handle = openHandle(device, In);
+ if ( handle==INVALID_HANDLE ) return NoIO;
+ closeHandle(handle);
+ handle = openHandle(device, In | Out);
+ if ( handle==INVALID_HANDLE ) return In;
+ closeHandle(handle);
+ return (In | Out);
+}
+
+QStringList Port::Serial::deviceList()
+{
+ QStringList list;
+#if defined(Q_OS_UNIX)
+ // standard serport in user space
+ for (uint i=0; i<8; i++) list.append(QString("/dev/ttyS%1").arg(i));
+ // new devfs serport flavour
+ for (uint i=0; i<8; i++) list.append(QString("/dev/tts/%1").arg(i));
+ // standard USB serport in user space
+ for (uint i=0; i<8; i++) list.append(QString("/dev/ttyUSB%1").arg(i));
+ // new devfs USB serport flavour
+ for (uint i=0; i<8; i++) list.append(QString("/dev/usb/tts/%1").arg(i));
+ // FreeBSD
+ for (uint i=0; i<8; i++) list.append(QString("/dev/ttyd%1").arg(i));
+ // Slackware 11 devfs USB Serial port support.
+ for (uint i=0; i<8; i++) list.append(QString("/dev/tts/USB%1").arg(i));
+ // MacOSX devfs USB Serial port support.
+ list.append("/dev/tty.usbserial");
+#elif defined(Q_OS_WIN)
+ for (uint i=1; i<10; i++) list.append(QString("COM%1").arg(i));
+#endif
+ return list;
+}
+
+const QStringList &Port::Serial::probedDeviceList()
+{
+ if ( _list==0 ) {
+ QStringList all = deviceList();
+ _list = new QStringList;
+ for (uint i=0; i<uint(all.count()); i++)
+ if( probe(all[i]) & (In | Out) ) _list->append(all[i]);
+ }
+ return *_list;
+}
+
+//-----------------------------------------------------------------------------
+const uint Port::Serial::SPEED_VALUES[Nb_Speeds] = {
+ 0, 50, 75, 110, 134, 150, 200, 300, 600, 1200, 1800, 2400, 4800, 9600, 19200, 38400,
+ 57600, 115200
+};
+const Port::Serial::SpeedData Port::Serial::SPEED_DATA[Nb_Speeds] = {
+#if defined(Q_OS_UNIX)
+ { true, B0 }, { true, B50 }, { true, B75 }, { true, B110 },
+ { true, B134 }, { true, B150 }, { true, B200 }, { true, B300 },
+ { true, B600 }, { true, B1200 }, { true, B1800 }, { true, B2400 },
+ { true, B4800 }, { true, B9600 }, { true, B19200 }, { true, B38400 },
+ { true, B57600 }, { true, B115200 }
+#elif defined(Q_OS_WIN)
+ { false, 0 }, { false, 0 }, { false, 0 }, { true, CBR_110 },
+ { false, 0 }, { false, 0 }, { false, 0 }, { true, CBR_300 },
+ { true, CBR_600 }, { true, CBR_1200 }, { false, 0 }, { true, CBR_2400 },
+ { true, CBR_4800 }, { true, CBR_9600 }, { true, CBR_19200 }, { true, CBR_38400 },
+ { true, CBR_57600 }, { true, CBR_115200 }
+#endif
+};
+
+const Port::Serial::SPinData Port::Serial::PIN_DATA[Nb_Pins] = {
+ { In, "DCD" }, { In, "RX" }, { Out, "TX" }, { Out, "DTR" },
+ { NoIO, "GND" }, { In, "DSR" }, { Out, "RTS" }, { In, "CTS" },
+ { Out, "RI" }
+};
+
+QValueVector<Port::PinData> Port::Serial::pinData(IODir dir) const
+{
+ QValueVector<PinData> v;
+ for (uint i=0; i<Nb_Pins; i++) {
+ if ( PIN_DATA[i].dir!=dir ) continue;
+ PinData pd = { i, PIN_DATA[i].label };
+ v.append(pd);
+ }
+ return v;
+}
+bool Port::Serial::isGroundPin(uint pin) const
+{
+ return ( PIN_DATA[pin].label==QString("GND") );
+}
+
+Port::IODir Port::Serial::ioDir(uint pin) const
+{
+ return PIN_DATA[pin].dir;
+}
+
+Port::Serial::Serial(const QString &device, Properties properties, Log::Base &base)
+ : Base(base), _device(device), _properties(properties), _fd(INVALID_HANDLE)
+{}
+
+bool Port::Serial::getParameters(Parameters &parameters)
+{
+ if ( _fd==INVALID_HANDLE ) return false;
+#if defined(Q_OS_UNIX)
+ if ( tcgetattr(_fd, &parameters)<0 ) {
+#elif defined(Q_OS_WIN)
+ if ( !GetCommState(_fd, &parameters.dcb) || !GetCommTimeouts(_fd, &parameters.comtmo) ) {
+#endif
+ setSystemError(i18n("Could not get file descriptor parameters"));
+ return false;
+ }
+ return true;
+}
+
+bool Port::Serial::setParameters(const Parameters &parameters)
+{
+ if ( _fd==INVALID_HANDLE ) return false;
+#if defined(Q_OS_UNIX)
+ if ( tcsetattr(_fd, TCSANOW, &parameters)<0 ) {
+#elif defined(Q_OS_WIN)
+ Parameters tmp = parameters;
+ if ( !SetCommState(_fd, &tmp.dcb) || !SetCommTimeouts(_fd, &tmp.comtmo) ) {
+#endif
+ setSystemError(i18n("Could not set file descriptor parameters"));
+ return false;
+ }
+ return true;
+}
+
+bool Port::Serial::internalOpen()
+{
+ _fd = openHandle(_device, In | Out);
+ if ( _fd==INVALID_HANDLE ) {
+ setSystemError(i18n("Could not open device \"%1\" read-write").arg(_device));
+ return false;
+ }
+ if ( !getParameters(_oldParameters) ) return false; // save configuration
+#if defined(Q_OS_UNIX)
+ if ( _properties & Blocking ) {
+ int flags = fcntl(_fd, F_GETFL, 0);
+ if ( fcntl(_fd, F_SETFL, flags & ~O_NONBLOCK)<0 ) {
+ setSystemError(i18n("Could not modify file descriptor flags"));
+ return false;
+ }
+ }
+#endif
+ return flush(DEFAULT_TIMEOUT);
+}
+
+void Port::Serial::internalClose()
+{
+ if ( _fd==INVALID_HANDLE ) return;
+ if ( _properties & NeedFlush ) flush(0);
+ if ( _properties & NeedBreak ) doBreak(1);
+ setParameters(_oldParameters);
+ closeHandle(_fd);
+ _fd = INVALID_HANDLE;
+}
+
+bool Port::Serial::internalSend(const char *data, uint size, uint timeout)
+{
+ if ( _fd==INVALID_HANDLE ) return false;
+ QTime time;
+ time.start();
+ for (uint todo=size; todo!=0; ) {
+#if defined(Q_OS_UNIX)
+ int res = write(_fd, data+size-todo, todo);
+ if ( res<0 && errno!=EAGAIN ) {
+#elif defined(Q_OS_WIN)
+ DWORD res = 0;
+ if ( WriteFile(_fd, data+size-todo, todo, &res, NULL)==0 ) {
+#endif
+ setSystemError(i18n("Error sending data"));
+ return false;
+ }
+ if ( res>0 ) todo -= res;
+ else {
+ if ( uint(time.elapsed())>timeout ) {
+ log(Log::LineType::Error, i18n("Timeout sending data (%1/%2 bytes sent).").arg(size-todo).arg(size));
+ return false;
+ }
+ msleep(1);
+ }
+ }
+ if ( (_properties & NeedDrain) && !drain(timeout) ) return false;
+ return true;
+}
+
+bool Port::Serial::internalReceive(uint size, char *data, uint timeout)
+{
+ if ( _fd==INVALID_HANDLE ) return false;
+ QTime time;
+ time.start();
+ for(uint todo=size; todo!=0; ) {
+#if defined(Q_OS_UNIX)
+ // this help reduce CPU usage. It also prevents blocking if the serial cable is disconnected
+ fd_set rfd;
+ FD_ZERO(&rfd);
+ FD_SET(_fd, &rfd);
+ struct timeval tv;
+ tv.tv_sec = timeout / 1000;
+ tv.tv_usec = (timeout%1000)*1000;
+ int res = select(_fd+1, &rfd, 0, 0, &tv);
+ if ( res<0 ) {
+ setSystemError(i18n("Error receiving data"));
+ return false;
+ }
+ if ( res==0 ) {
+ log(Log::LineType::Error, i18n("Timeout waiting for data."));
+ return false;
+ }
+ res = read(_fd, data+size-todo, todo);
+ if ( res<0 && errno!=EAGAIN ) {
+#elif defined(Q_OS_WIN)
+ DWORD res = 0;
+ if ( ReadFile(_fd, data+size-todo, todo, &res, NULL)==0 ) {
+#endif
+ setSystemError(i18n("Error receiving data"));
+ return false;
+ }
+ if ( res>0 ) todo -= res;
+ else {
+ if ( uint(time.elapsed())>timeout ) {
+ log(Log::LineType::Error, i18n("Timeout receiving data (%1/%2 bytes received).").arg(size-todo).arg(size));
+ return false;
+ }
+ msleep(1);
+ }
+ }
+ return true;
+}
+
+bool Port::Serial::drain(uint timeout)
+{
+ if ( _fd==INVALID_HANDLE ) return false;
+#if defined(Q_OS_UNIX)
+ // tcdrain will block if the serial cable is disconnected
+ // so we first check for data in output buffer...
+ QTime time;
+ time.start();
+ for (;;) {
+ int nb;
+ if ( ioctl(_fd, TIOCOUTQ, &nb)==-1 ) {
+ setSystemError(i18n("Error checking for data in output buffer"));
+ return false;
+ }
+ if ( nb==0 ) break;
+ if ( uint(time.elapsed())>timeout ) {
+ _fd = INVALID_HANDLE; // otherwise close blocks...
+ log(Log::LineType::Error, i18n("Timeout sending data (%1 bytes left).").arg(nb));
+ return false;
+ }
+ }
+ if ( tcdrain(_fd)<0 ) {
+ setSystemError(i18n("Error while draining"));
+ return false;
+ }
+#endif
+ return true;
+}
+
+bool Port::Serial::flush(uint timeout)
+{
+ if ( _fd==INVALID_HANDLE ) return false;
+ if ( (_properties & NeedDrain) && !drain(timeout) ) return false;
+#if defined(Q_OS_UNIX)
+ if ( tcflush(_fd, TCIFLUSH)<0 ) {
+#elif defined(Q_OS_WIN)
+ if ( FlushFileBuffers(_fd)==0 || PurgeComm(_fd, PURGE_TXABORT)==0
+ || PurgeComm(_fd, PURGE_RXABORT)==0 || PurgeComm(_fd, PURGE_TXCLEAR)==0
+ || PurgeComm(_fd, PURGE_RXCLEAR)==0 ) {
+#endif
+ setSystemError(i18n("Could not flush device"));
+ return false;
+ }
+ return true;
+}
+
+bool Port::Serial::internalSetPinOn(Pin pin, bool on)
+{
+#if defined(Q_OS_UNIX)
+ int bit = 0;
+ switch (pin) {
+ case TX: return ( ioctl(_fd, on ? TIOCSBRK : TIOCCBRK, 0)>=0 );
+ case DTR: bit = TIOCM_DTR; break;
+ case RTS: bit = TIOCM_RTS; break;
+ case RI: bit = TIOCM_RI; break;
+ default: Q_ASSERT(false); return false;
+ }
+ return ( ioctl(_fd, on ? TIOCMBIS : TIOCMBIC, &bit)>=0 );
+#elif defined(Q_OS_WIN)
+ DWORD func = 0;
+ switch (pin) {
+ case TX: func = (on ? SETBREAK : CLRBREAK); break;
+ case DTR: func = (on ? SETDTR : CLRDTR); break;
+ case RTS: func = (on ? SETRTS : CLRRTS); break;
+ case RI: // #### not possible with Win32 API ??
+ default: Q_ASSERT(false); return false;
+ }
+ return ( EscapeCommFunction(_fd, func)!=0 );
+#endif
+}
+
+bool Port::Serial::setPinOn(uint pin, bool on, LogicType type)
+{
+ if ( _fd==INVALID_HANDLE ) return false;
+ if ( type==NegativeLogic ) on = !on;
+ Q_ASSERT( pin<Nb_Pins );
+ Q_ASSERT( PIN_DATA[pin].dir==Out );
+ if ( !internalSetPinOn(Pin(pin), on) ) {
+ setSystemError(i18n("Error setting bit %1 of serial port to %2").arg(PIN_DATA[pin].label).arg(on));
+ return false;
+ }
+ return true;
+}
+
+bool Port::Serial::internalReadPin(Pin pin, LogicType type, bool &value)
+{
+#if defined(Q_OS_UNIX)
+ int bits;
+ if ( ioctl(_fd, TIOCMGET, &bits)<0 ) return false;
+ int mask = 0;
+ switch (pin) {
+ case DCD: mask = TIOCM_CD; break;
+ case RX : mask = TIOCM_SR; break;
+ case DSR: mask = TIOCM_DSR; break;
+ case CTS: mask = TIOCM_CTS; break;
+ default: Q_ASSERT(false); return false;
+ }
+ value = ((type==NegativeLogic ? ~bits : bits) & mask);
+ return true;
+#elif defined(Q_OS_WIN)
+ DWORD status;
+ if ( GetCommModemStatus(_fd, &status)==0 ) return false;
+ switch (pin) {
+ case DCD: value = (status & MS_RLSD_ON); break;
+ case DSR: value = (status & MS_DSR_ON); break;
+ case CTS: value = (status & MS_CTS_ON); break;
+ case RX: // not possible with Win32 API ??
+ default: Q_ASSERT(false); return false;
+ }
+ if ( type==NegativeLogic) value = !value;
+ return true;
+#endif
+}
+
+ bool Port::Serial::readPin(uint pin, LogicType type, bool &value)
+{
+ if ( _fd==INVALID_HANDLE ) return false;
+ Q_ASSERT( pin<Nb_Pins );
+ Q_ASSERT( PIN_DATA[pin].dir==In );
+ if ( !internalReadPin(Pin(pin), type, value) ) {
+ setSystemError(i18n("Error reading serial pin %1").arg(PIN_DATA[pin].label));
+ return false;
+ }
+ return true;
+}
+
+bool Port::Serial::setMode(InputFlags inputFlags, ControlFlags controlFlags, Speed speed, uint readTimeout)
+{
+ Q_ASSERT (SPEED_DATA[speed].supported );
+ if ( !flush(0) ) return false;
+ Parameters parameters;
+ if ( !getParameters(parameters) ) return false;
+#if defined(Q_OS_UNIX)
+ cfsetispeed(&parameters, SPEED_DATA[speed].flag);
+ cfsetospeed(&parameters, SPEED_DATA[speed].flag);
+ parameters.c_cflag &= ~(CSIZE|PARENB|PARODD|HUPCL|CSTOPB);
+ int cflags = 0;
+ if ( controlFlags & ByteSize8 ) cflags |= CS8;
+ if ( controlFlags & HardwareFlowControl ) cflags |= CRTSCTS;
+ if ( controlFlags & EnableReceiver ) cflags |= CREAD;
+ if ( controlFlags & IgnoreControlLines ) cflags |= CLOCAL;
+ parameters.c_cflag |= cflags;
+ parameters.c_iflag &= ~(ISTRIP|INPCK|IGNCR|INLCR|ICRNL|IXOFF|IXON);
+ int iflags = 0;
+ if ( inputFlags & IgnoreBreak ) iflags |= IGNBRK;
+ if ( inputFlags & IgnoreParity ) iflags |= IGNPAR;
+ parameters.c_iflag |= iflags;
+ parameters.c_oflag &= ~OPOST;
+ parameters.c_lflag &= ~(ICANON|ECHO|ECHONL|ISIG|IEXTEN|TOSTOP);
+ parameters.c_cc[VMIN] = 0; // wait for 1 char or timeout
+ parameters.c_cc[VTIME] = readTimeout/100; // wait in deciseconds
+#elif defined(Q_OS_WIN)
+ if ( controlFlags & EnableReceiver ) ; // #### not sure what to do
+ if ( controlFlags & IgnoreControlLines ) ; // #### not sure what to do
+ setHardwareFlowControl(parameters, (controlFlags & HardwareFlowControl));
+ if ( inputFlags & IgnoreParity ) parameters.dcb.Parity = NOPARITY;
+ parameters.dcb.StopBits = ONESTOPBIT;
+ if ( controlFlags & ByteSize8 ) parameters.dcb.ByteSize = 8;
+ parameters.dcb.BaudRate = SPEED_DATA[speed].flag;
+ parameters.comtmo.ReadIntervalTimeout = MAXDWORD;
+ parameters.comtmo.ReadTotalTimeoutMultiplier = MAXDWORD;
+ parameters.comtmo.ReadTotalTimeoutConstant = readTimeout;
+ parameters.comtmo.WriteTotalTimeoutMultiplier = 1;
+ parameters.comtmo.WriteTotalTimeoutConstant = readTimeout;
+#endif
+ return setParameters(parameters);
+ // need flush ??
+}
+
+bool Port::Serial::doBreak(uint duration)
+{
+ if ( !flush(0) ) return false;
+#if defined(Q_OS_UNIX)
+ if ( ioctl(_fd, TIOCSBRK)<0 ) {
+#elif defined(Q_OS_WIN)
+ if ( SetCommBreak(_fd)==0 ) {
+#endif
+ setSystemError(i18n("Error setting up break"));
+ return false;
+ }
+ msleep(duration);
+#if defined(Q_OS_UNIX)
+ if ( ioctl(_fd, TIOCCBRK) ) {
+#elif defined(Q_OS_WIN)
+ if ( ClearCommBreak(_fd)==0 ) {
+#endif
+ setSystemError(i18n("Error clearing up break"));
+ return false;
+ }
+ msleep(1);
+ return flush(0);
+}
+
+void Port::Serial::setHardwareFlowControl(Parameters &parameters, bool on)
+{
+#if defined(Q_OS_UNIX)
+ if (on) parameters.c_cflag |= CRTSCTS;
+ else parameters.c_cflag &= ~CRTSCTS;
+ parameters.c_cc[VMIN] = 0; // #### needed ?
+ parameters.c_cc[VTIME] = 0; // #### needed ?
+#elif defined(Q_OS_WIN)
+ // #### not sure about that
+ parameters.dcb.fParity = on;
+ parameters.dcb.fOutxCtsFlow = on;
+ parameters.dcb.fOutxDsrFlow = on;
+ parameters.dcb.fDtrControl = (on ? DTR_CONTROL_ENABLE : DTR_CONTROL_DISABLE);
+ parameters.dcb.fDsrSensitivity = on;
+ parameters.dcb.fTXContinueOnXoff = on;
+ parameters.dcb.fOutX = on;
+ parameters.dcb.fInX = on;
+ parameters.dcb.fNull = on;
+ parameters.dcb.fRtsControl = (on ? RTS_CONTROL_ENABLE : RTS_CONTROL_DISABLE);
+ parameters.dcb.fAbortOnError = on;
+#endif
+}
+
+bool Port::Serial::setHardwareFlowControl(bool on)
+{
+ Parameters parameters;
+ if ( !getParameters(parameters) ) return false;
+ setHardwareFlowControl(parameters, on);
+ return setParameters(parameters);
+}
+
+void Port::Serial::setSystemError(const QString &message)
+{
+#if defined(Q_OS_UNIX)
+ log(Log::LineType::Error, message + QString(" (errno=%1)").arg(strerror(errno)));
+#elif defined(Q_OS_WIN)
+ LPVOID lpMsgBuf;
+ FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, GetLastError(), MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPTSTR)&lpMsgBuf, 0, NULL);
+ log(Log::LineType::Error, message + QString(" (last error=%1 %2)").arg(GetLastError()).arg((const char *)(LPCTSTR)lpMsgBuf));
+ LocalFree(lpMsgBuf);
+#endif
+}
diff --git a/src/common/port/serial.h b/src/common/port/serial.h
new file mode 100644
index 0000000..b2911e6
--- /dev/null
+++ b/src/common/port/serial.h
@@ -0,0 +1,113 @@
+/***************************************************************************
+ * Copyright (C) 2005 Nicolas Hadacek <hadacek@kde.org> *
+ * Copyright (C) 2003-2004 Alain Gibaud <alain.gibaud@free.fr> *
+ * Copyright (C) 2002-2003 Stephen Landamore <stephen@landamore.com> *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ ***************************************************************************/
+#ifndef SERIAL_H
+#define SERIAL_H
+
+#include "common/global/global.h"
+#ifdef Q_OS_UNIX
+# include <termios.h>
+#elif defined(Q_OS_WIN)
+# include <io.h>
+#endif
+#include "port_base.h"
+
+namespace Port
+{
+
+class Serial : public Base
+{
+public:
+ enum Speed { S0 = 0, S50, S75, S110, S134, S150, S200, S300, S600, S1200,
+ S1800, S2400, S4800, S9600, S19200, S38400, S57600, S115200,
+ Nb_Speeds };
+ static const uint SPEED_VALUES[Nb_Speeds];
+ struct SpeedData {
+ bool supported;
+ uint flag;
+ };
+ static const SpeedData SPEED_DATA[Nb_Speeds];
+
+ enum Property { NoProperty = 0, NeedDrain = 1, NeedFlush = 2, NeedBreak = 4,
+ Blocking = 8 };
+ Q_DECLARE_FLAGS(Properties, Property)
+
+ Serial(const QString &device, Properties properties, Log::Base &base);
+ virtual ~Serial() { close(); }
+ virtual Description description() const { return Description(PortType::Serial, _device); }
+
+ static const QStringList &probedDeviceList();
+ static IODirs probe(const QString &device);
+ static bool isAvailable() { return true; }
+
+ enum InputFlag { NoInputFlag = 0, IgnoreBreak = 1, IgnoreParity = 2 };
+ Q_DECLARE_FLAGS(InputFlags, InputFlag)
+ enum ControlFlag { NoControlFlag = 0, ByteSize8 = 1, HardwareFlowControl = 2,
+ EnableReceiver = 4, IgnoreControlLines = 8 };
+ Q_DECLARE_FLAGS(ControlFlags, ControlFlag)
+ bool setMode(InputFlags inputFlags, ControlFlags controlFlags, Speed speed, uint readTimeout); // in ms
+ bool drain(uint timeout);
+ bool flush(uint timeout);
+ bool doBreak(uint duration); // in ms
+ bool setHardwareFlowControl(bool on);
+
+ enum Pin { DCD = 0, RX, TX, DTR, SG, DSR, RTS, CTS, RI, Nb_Pins };
+ struct SPinData {
+ IODir dir;
+ const char *label;
+ };
+ static const SPinData PIN_DATA[Nb_Pins];
+ virtual bool setPinOn(uint pin, bool on, LogicType type);
+ virtual bool readPin(uint pin, LogicType type, bool &value);
+ virtual QValueVector<PinData> pinData(IODir dir) const;
+ virtual bool isGroundPin(uint pin) const;
+ virtual uint groundPin() const { return SG; }
+ virtual IODir ioDir(uint pin) const;
+
+private:
+ QString _device;
+ Properties _properties;
+#if defined(Q_OS_UNIX)
+ typedef int Handle;
+ typedef termios Parameters;
+#elif defined(Q_OS_WIN)
+ typedef HANDLE Handle;
+ struct Parameters {
+ DCB dcb;
+ COMMTIMEOUTS comtmo;
+ };
+#endif
+ Handle _fd;
+ Parameters _oldParameters;
+
+ bool setParameters(const Parameters &parameters);
+ bool getParameters(Parameters &parameters);
+ virtual bool internalOpen();
+ virtual void internalClose();
+ virtual bool internalSend(const char *data, uint size, uint timeout);
+ virtual bool internalReceive(uint size, char *data, uint timeout);
+ virtual void setSystemError(const QString &message);
+ bool internalSetPinOn(Pin pin, bool on);
+ bool internalReadPin(Pin pin, LogicType type, bool &value);
+
+ static const Handle INVALID_HANDLE;
+ static Handle openHandle(const QString &device, IODirs dirs);
+ static void closeHandle(Handle handle);
+ static QStringList *_list;
+ static QStringList deviceList();
+ static void setHardwareFlowControl(Parameters &parameters, bool on);
+};
+Q_DECLARE_OPERATORS_FOR_FLAGS(Serial::Properties)
+Q_DECLARE_OPERATORS_FOR_FLAGS(Serial::InputFlags)
+Q_DECLARE_OPERATORS_FOR_FLAGS(Serial::ControlFlags)
+
+} // namespace
+
+#endif
diff --git a/src/common/port/usb_port.cpp b/src/common/port/usb_port.cpp
new file mode 100644
index 0000000..392b483
--- /dev/null
+++ b/src/common/port/usb_port.cpp
@@ -0,0 +1,411 @@
+/***************************************************************************
+ * Copyright (C) 2005 Lorenz Mösenlechner & Matthias Kranz *
+ * <icd2linux@hcilab.org> *
+ * Copyright (C) 2003-2005 Orion Sky Lawlor <olawlor@acm.org> *
+ * Copyright (C) 2005-2007 Nicolas Hadacek <hadacek@kde.org> *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ ***************************************************************************/
+#include "usb_port.h"
+
+#ifdef HAVE_USB
+# include <usb.h>
+#endif
+#include <qdatetime.h>
+
+#include "common/common/version_data.h"
+#include "common/common/number.h"
+
+//-----------------------------------------------------------------------------
+class Port::USB::Private
+{
+public:
+#ifdef HAVE_USB
+ Private() : _interface(0) {}
+ const usb_interface_descriptor *_interface;
+#endif
+};
+
+bool Port::USB::_initialized = false;
+
+void Port::USB::initialize()
+{
+ if (_initialized) return;
+ _initialized = true;
+#ifdef HAVE_USB
+ usb_init();
+ VersionData vd = VersionData::fromString(LIBUSB_VERSION);
+ QString s = QString("libusb %1").arg(vd.pretty());
+ if ( vd<VersionData(0, 1, 8) ) qWarning("%s: may be too old (you need at least version 0.1.8)", s.latin1());
+#endif
+}
+
+bool Port::USB::isAvailable()
+{
+#ifdef HAVE_USB
+ return true;
+#else
+ return false;
+#endif
+}
+
+usb_bus *Port::USB::getBusses()
+{
+ initialize();
+#ifdef HAVE_USB
+ // refresh libusb structures
+ usb_find_busses();
+ usb_find_devices();
+ return usb_get_busses();
+#else
+ return 0;
+#endif
+}
+
+bool Port::USB::findBulk(const struct usb_device *dev)
+{
+ initialize();
+#ifdef HAVE_USB
+ int configuration = -1, interface = -1, altsetting = -1, bulk_endpoint = -1;
+ // walk through the possible configs, etc.
+ qDebug("This device has %d possible configuration(s).", dev->descriptor.bNumConfigurations);
+ for (int c=0; c<dev->descriptor.bNumConfigurations; c++) {
+ qDebug("Looking at configuration %d...This configuration has %d interfaces.", c, dev->config[c].bNumInterfaces);
+ for(int i=0; i<dev->config[c].bNumInterfaces; i++) {
+ qDebug(" Looking at interface %d...This interface has %d altsettings.", i, dev->config[c].interface[i].num_altsetting);
+ for (int a=0; a < dev->config[c].interface[i].num_altsetting; a++) {
+ qDebug(" Looking at altsetting %d...This altsetting has %d endpoints.", a, dev->config[c].interface[i].altsetting[a].bNumEndpoints);
+ for (int e=0; e < dev->config[c].interface[i].altsetting[a].bNumEndpoints; e++) {
+ QString s;
+ s.sprintf(" Endpoint %d: Address %02xh, attributes %02xh ", e, dev->config[c].interface[i].altsetting[a].endpoint[e].bEndpointAddress, dev->config[c].interface[i].altsetting[a].endpoint[e].bmAttributes);
+ uchar attribs = (dev->config[c].interface[i].altsetting[a].endpoint[e].bmAttributes & 3);
+ switch (attribs) {
+ case 0: s += "(Control) "; break;
+ case 1: s += "(Isochronous) "; break;
+ case 2: s += "(Bulk) ";
+ /* Found the correct configuration, interface etc... it has bulk endpoints! */
+ configuration=c;
+ interface=i;
+ altsetting=a;
+ break;
+ case 3: s += "(Interrupt) "; break;
+ default: s += "ERROR! Got an illegal value in endpoint bmAttributes";
+ }
+ if ( attribs!=0 ) {
+ uchar dir = (dev->config[c].interface[i].altsetting[a].endpoint[e].bEndpointAddress & 0x80);
+ switch (dir) {
+ case 0x00: s += "(Out)";
+ // Do nothing in this case
+ break;
+ case 0x80: s += "(In)";
+ if ((dev->config[c].interface[i].altsetting[a].endpoint[e].bmAttributes & 0x03) == 2) /* Make sure it's a *bulk* endpoint */ {
+ // Found the correct endpoint to use for bulk transfer; use its ADDRESS
+ bulk_endpoint=dev->config[c].interface[i].altsetting[a].endpoint[e].bEndpointAddress;
+ }
+ break;
+ default: s += "ERROR! Got an illegal value in endpoint bEndpointAddress";
+ }
+ }
+ qDebug("%s", s.latin1());
+ }
+ }
+ }
+ }
+ if (bulk_endpoint<0) {
+ qDebug("No valid interface found!");
+ return false;
+ }
+#endif
+ return true;
+}
+
+QStringList Port::USB::probedDeviceList()
+{
+ initialize();
+ QStringList list;
+#ifdef HAVE_USB
+ usb_init(); // needed ?
+ for (usb_bus *bus=getBusses(); bus; bus=bus->next) {
+ for (struct usb_device *dev=bus->devices; dev; dev=dev->next) {
+ if ( dev->descriptor.idVendor==0 ) continue; // controller
+ list.append(QString("Vendor Id: %1 - Product Id: %2")
+ .arg(toLabel(NumberBase::Hex, dev->descriptor.idVendor, 4)).arg(toLabel(NumberBase::Hex, dev->descriptor.idProduct, 4)));
+ }
+ }
+#endif
+ return list;
+}
+
+struct usb_device *Port::USB::findDevice(uint vendorId, uint productId)
+{
+ initialize();
+#ifdef HAVE_USB
+ for (usb_bus *bus=getBusses(); bus; bus=bus->next) {
+ for (struct usb_device *dev=bus->devices; dev; dev=dev->next) {
+ if ( dev->descriptor.idVendor==vendorId && dev->descriptor.idProduct==productId )
+ return dev;
+ }
+ }
+#else
+ Q_UNUSED(vendorId); Q_UNUSED(productId);
+ qDebug("USB support disabled");
+#endif
+ return 0;
+}
+
+//-----------------------------------------------------------------------------
+const char * const Port::USB::ENDPOINT_MODE_NAMES[Nb_EndpointModes] = {
+ "bulk", "interrupt", "control", "isochronous"
+};
+
+Port::USB::USB(Log::Base &base, uint vendorId, uint productId, uint config, uint interface)
+ : Base(base), _vendorId(vendorId), _productId(productId),
+ _config(config), _interface(interface), _handle(0), _device(0)
+{
+ Q_ASSERT( config>=1 );
+ _private = new Private;
+ initialize();
+}
+
+Port::USB::~USB()
+{
+ close();
+ delete _private;
+}
+
+void Port::USB::setSystemError(const QString &message)
+{
+#ifdef HAVE_USB
+ log(Log::LineType::Error, message + QString(" (err=%1).").arg(usb_strerror()));
+#else
+ log(Log::LineType::Error, message);
+#endif
+}
+
+void Port::USB::tryToDetachDriver()
+{
+ // try to detach an already existing driver... (linux only)
+#if defined(LIBUSB_HAS_GET_DRIVER_NP) && LIBUSB_HAS_GET_DRIVER_NP
+ log(Log::DebugLevel::Extra, "find if there is already an installed driver");
+ char dname[256] = "";
+ if ( usb_get_driver_np(_handle, _interface, dname, 255)<0 ) return;
+ log(Log::DebugLevel::Normal, QString(" a driver \"%1\" is already installed...").arg(dname));
+# if defined(LIBUSB_HAS_DETACH_KERNEL_DRIVER_NP) && LIBUSB_HAS_DETACH_KERNEL_DRIVER_NP
+ usb_detach_kernel_driver_np(_handle, _interface);
+ log(Log::DebugLevel::Normal, " try to detach it...");
+ if ( usb_get_driver_np(_handle, _interface, dname, 255)<0 ) return;
+ log(Log::DebugLevel::Normal, " failed to detach it");
+# endif
+#endif
+}
+
+bool Port::USB::internalOpen()
+{
+#ifdef HAVE_USB
+ _device = findDevice(_vendorId, _productId);
+ if ( _device==0 ) {
+ log(Log::LineType::Error, i18n("Could not find USB device (vendor=%1 product=%2).")
+ .arg(toLabel(NumberBase::Hex, _vendorId, 4)).arg(toLabel(NumberBase::Hex, _productId, 4)));
+ return false;
+ }
+ log(Log::DebugLevel::Extra, QString("found USB device as \"%1\" on bus \"%2\"").arg(_device->filename).arg(_device->bus->dirname));
+ _handle = usb_open(_device);
+ if ( _handle==0 ) {
+ setSystemError(i18n("Error opening USB device."));
+ return false;
+ }
+// windows: usb_reset takes about 7-10 seconds to re-enumerate the device...
+// BSD : not implemented in libusb...
+# if !defined(Q_OS_BSD4) && !defined(Q_OS_WIN)
+ if ( usb_reset(_handle)<0 ) {
+ setSystemError(i18n("Error resetting USB device."));
+ return false;
+ }
+# endif
+ usb_close(_handle);
+ _handle = usb_open(_device);
+ if ( _handle==0 ) {
+ setSystemError(i18n("Error opening USB device."));
+ return false;
+ }
+ tryToDetachDriver();
+ uint i = 0;
+ for (; i<_device->descriptor.bNumConfigurations; i++)
+ if ( _config==_device->config[i].bConfigurationValue ) break;
+ if ( i==_device->descriptor.bNumConfigurations ) {
+ uint old = _config;
+ i = 0;
+ _config = _device->config[i].bConfigurationValue;
+ log(Log::LineType::Warning, i18n("Configuration %1 not present: using %2").arg(old).arg(_config));
+ }
+ const usb_config_descriptor &configd = _device->config[i];
+ if ( usb_set_configuration(_handle, _config)<0 ) {
+ setSystemError(i18n("Error setting USB configuration %1.").arg(_config));
+ return false;
+ }
+ for (i=0; i<configd.bNumInterfaces; i++)
+ if ( _interface==configd.interface[i].altsetting[0].bInterfaceNumber ) break;
+ if ( i==configd.bNumInterfaces ) {
+ uint old = _interface;
+ i = 0;
+ _interface = configd.interface[i].altsetting[0].bInterfaceNumber;
+ log(Log::LineType::Warning, i18n("Interface %1 not present: using %2").arg(old).arg(_interface));
+ }
+ _private->_interface = &(configd.interface[i].altsetting[0]);
+ if ( usb_claim_interface(_handle, _interface)<0 ) {
+ setSystemError(i18n("Could not claim USB interface %1").arg(_interface));
+ return false;
+ }
+ log(Log::DebugLevel::Max, QString("alternate setting is %1").arg(_private->_interface->bAlternateSetting));
+ log(Log::DebugLevel::Max, QString("USB bcdDevice: %1").arg(toHexLabel(_device->descriptor.bcdDevice, 4)));
+ return true;
+#else
+ log(Log::LineType::Error, i18n("USB support disabled"));
+ return false;
+#endif
+}
+
+void Port::USB::internalClose()
+{
+ if ( _handle==0 ) return;
+#ifdef HAVE_USB
+ usb_release_interface(_handle, _interface);
+ usb_close(_handle);
+ _private->_interface = 0;
+#endif
+ _device = 0;
+ _handle = 0;
+}
+
+bool Port::USB::sendControlMessage(const ControlMessageData &data)
+{
+ if ( hasError() ) return false;
+#ifdef HAVE_USB
+ QString s = data.bytes;
+ uint length = strlen(data.bytes) / 2;
+ QByteArray ba(length);
+ for (uint i=0; i<length; i++)
+ ba[i] = fromString(NumberBase::Hex, s.mid(2*i, 2), 0);
+ int res = usb_control_msg(_handle, data.type, data.request, data.value, 0, ba.data(), length, 1000); // 1s
+ if ( res<0 ) {
+ setSystemError(i18n("Error sending control message to USB port."));
+ return false;
+ }
+#endif
+ return true;
+}
+
+uint timeout(uint size)
+{
+ return qMax(size*5, uint(1000)); // 5ms per byte or 1s
+}
+
+
+Port::USB::EndpointMode Port::USB::endpointMode(uint ep) const
+{
+#ifdef HAVE_USB
+ uint index = ep & USB_ENDPOINT_ADDRESS_MASK;
+ Q_ASSERT(_private->_interface);
+ const usb_endpoint_descriptor *ued = _private->_interface->endpoint + index;
+ Q_ASSERT(ued);
+ switch (ued->bmAttributes & USB_ENDPOINT_TYPE_MASK) {
+ case USB_ENDPOINT_TYPE_BULK: return Bulk;
+ case USB_ENDPOINT_TYPE_INTERRUPT: return Interrupt;
+ case USB_ENDPOINT_TYPE_ISOCHRONOUS: return Isochronous;
+ case USB_ENDPOINT_TYPE_CONTROL: return Control;
+ default: break;
+ }
+#endif
+ Q_ASSERT(false);
+ return Nb_EndpointModes;
+}
+
+Port::IODir Port::USB::endpointDir(uint ep) const
+{
+#ifdef HAVE_USB
+ switch (ep & USB_ENDPOINT_DIR_MASK) {
+ case USB_ENDPOINT_IN: return In;
+ case USB_ENDPOINT_OUT: return Out;
+ default: break;
+ }
+#endif
+ Q_ASSERT(false);
+ return NoIO;
+}
+
+bool Port::USB::write(uint ep, const char *data, uint size)
+{
+ if ( hasError() ) return false;
+#ifdef HAVE_USB
+ IODir dir = endpointDir(ep);
+ EndpointMode mode = endpointMode(ep);
+ log(Log::DebugLevel::LowLevel, QString("write to endpoint %1 (%2 - %3) %4 chars: \"%5\"")
+ .arg(toHexLabel(ep, 2)).arg(ENDPOINT_MODE_NAMES[mode]).arg(IO_DIR_NAMES[dir]).arg(size).arg(toPrintable(data, size, PrintEscapeAll)));
+ Q_ASSERT( dir==Out );
+ QTime time;
+ time.start();
+ int todo = size;
+ for (;;) {
+ int res = 0;
+ //qDebug("write ep=%i todo=%i/%i", ep, todo, size);
+ if ( mode==Interrupt ) res = usb_interrupt_write(_handle, ep, (char *)data + size - todo, todo, timeout(todo));
+ else res = usb_bulk_write(_handle, ep, (char *)data + size - todo, todo, timeout(todo));
+ //qDebug("res: %i", res);
+ if ( res==todo ) break;
+ if ( uint(time.elapsed())>3000 ) { // 3 s
+ if ( res<0 ) setSystemError(i18n("Error sending data (ep=%1 res=%2)").arg(toHexLabel(ep, 2)).arg(res));
+ else log(Log::LineType::Error, i18n("Timeout: only some data sent (%1/%2 bytes).").arg(size-todo).arg(size));
+ return false;
+ }
+ if ( res==0 ) log(Log::DebugLevel::Normal, i18n("Nothing sent: retrying..."));
+ if ( res>0 ) todo -= res;
+ msleep(100);
+ }
+#else
+ Q_UNUSED(ep); Q_UNUSED(data); Q_UNUSED(size);
+#endif
+ return true;
+}
+
+bool Port::USB::read(uint ep, char *data, uint size, bool *poll)
+{
+ if ( hasError() ) return false;
+#ifdef HAVE_USB
+ IODir dir = endpointDir(ep);
+ EndpointMode mode = endpointMode(ep);
+ log(Log::DebugLevel::LowLevel, QString("read from endpoint %1 (%2 - %3) %4 chars")
+ .arg(toHexLabel(ep, 2)).arg(ENDPOINT_MODE_NAMES[mode]).arg(IO_DIR_NAMES[dir]).arg(size));
+ Q_ASSERT( dir==In );
+ QTime time;
+ time.start();
+ int todo = size;
+ for (;;) {
+ int res = 0;
+ //qDebug("read ep=%i size=%i", ep, todo);
+ if ( mode==Interrupt ) res = usb_interrupt_read(_handle, ep, data + size - todo, todo, timeout(todo));
+ else res = usb_bulk_read(_handle, ep, data + size - todo, todo, timeout(todo));
+ //qDebug("res: %i", res);
+ if ( res==todo ) break;
+ if ( uint(time.elapsed())>3000 ) { // 3 s: seems to help icd2 in some case (?)
+ if ( res<0 ) setSystemError(i18n("Error receiving data (ep=%1 res=%2)").arg(toHexLabel(ep, 2)).arg(res));
+ else log(Log::LineType::Error, i18n("Timeout: only some data received (%1/%2 bytes).").arg(size-todo).arg(size));
+ return false;
+ }
+ if ( res==0 ) {
+ if (poll) {
+ *poll = false;
+ return true;
+ } else log(Log::DebugLevel::Normal, i18n("Nothing received: retrying..."));
+ }
+ if ( res>0 ) todo -= res;
+ msleep(100);
+ }
+ if (poll) *poll = true;
+#else
+ Q_UNUSED(ep); Q_UNUSED(data); Q_UNUSED(size);
+#endif
+ return true;
+}
diff --git a/src/common/port/usb_port.h b/src/common/port/usb_port.h
new file mode 100644
index 0000000..73961cc
--- /dev/null
+++ b/src/common/port/usb_port.h
@@ -0,0 +1,70 @@
+/***************************************************************************
+ * Copyright (C) 2005 Lorenz Mösenlechner & Matthias Kranz *
+ * <icd2linux@hcilab.org> *
+ * Copyright (C) 2005 Nicolas Hadacek <hadacek@kde.org> *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ ***************************************************************************/
+#ifndef USB_PORT_H
+#define USB_PORT_H
+
+#include "port_base.h"
+#if defined(Q_OS_WIN)
+#undef interface
+#endif
+struct usb_dev_handle;
+struct usb_device;
+struct usb_bus;
+
+namespace Port
+{
+
+class USB : public Base
+{
+public:
+ USB(Log::Base &base, uint vendorId, uint productId, uint config, uint interface);
+ virtual ~USB();
+ virtual Description description() const { return Description(PortType::USB, QString::null); }
+
+ static struct usb_device *findDevice(uint vendorId, uint productId);
+ static bool isAvailable();
+ static QStringList probedDeviceList();
+
+ struct ControlMessageData {
+ int type, request, value;
+ const char *bytes;
+ };
+ bool sendControlMessage(const ControlMessageData &data);
+ enum EndpointMode { Bulk = 0, Interrupt, Control, Isochronous, Nb_EndpointModes };
+ static const char * const ENDPOINT_MODE_NAMES[Nb_EndpointModes];
+ EndpointMode endpointMode(uint ep) const;
+ IODir endpointDir(uint ep) const;
+
+protected:
+ bool write(uint endPoint, const char *data, uint size);
+ bool read(uint endPoint, char *data, uint size, bool *poll = 0);
+
+private:
+ class Private;
+ Private *_private;
+ uint _vendorId, _productId, _config, _interface;
+ usb_dev_handle *_handle;
+ usb_device *_device;
+
+ virtual bool internalOpen();
+ virtual void internalClose();
+ virtual void setSystemError(const QString &message);
+ void tryToDetachDriver();
+
+ static bool _initialized;
+ static void initialize();
+ static usb_bus *getBusses();
+ static bool findBulk(const struct usb_device *device);
+};
+
+} // namespace
+
+#endif