1
0
mirror of https://bitbucket.org/librepilot/librepilot.git synced 2024-12-03 11:24:10 +01:00

Merge branch 'usb_fixes' into next

This commit is contained in:
James Cotton 2012-09-11 10:18:45 -05:00
commit 2488b48f50
12 changed files with 519 additions and 610 deletions

View File

@ -22,8 +22,7 @@ QextSerialEnumerator::~QextSerialEnumerator( )
// static // static
QList<QextPortInfo> QextSerialEnumerator::getPorts() QList<QextPortInfo> QextSerialEnumerator::getPorts()
{ { QList<QextPortInfo> infoList;
QList<QextPortInfo> infoList;
io_iterator_t serialPortIterator = 0; io_iterator_t serialPortIterator = 0;
kern_return_t kernResult = KERN_FAILURE; kern_return_t kernResult = KERN_FAILURE;
CFMutableDictionaryRef matchingDictionary; CFMutableDictionaryRef matchingDictionary;
@ -44,18 +43,6 @@ QList<QextPortInfo> QextSerialEnumerator::getPorts()
IOObjectRelease(serialPortIterator); IOObjectRelease(serialPortIterator);
serialPortIterator = 0; serialPortIterator = 0;
if( !(matchingDictionary = IOServiceNameMatching("AppleUSBCDC")) ) {
qWarning("IOServiceNameMatching returned a NULL dictionary.");
return infoList;
}
if( IOServiceGetMatchingServices(kIOMasterPortDefault, matchingDictionary, &serialPortIterator) != KERN_SUCCESS ) {
qCritical() << "IOServiceGetMatchingServices failed, returned" << kernResult;
return infoList;
}
iterateServicesOSX(serialPortIterator, infoList);
IOObjectRelease(serialPortIterator);
return infoList; return infoList;
} }

View File

@ -44,25 +44,19 @@ namespace Core {
ConnectionManager::ConnectionManager(Internal::MainWindow *mainWindow, QTabWidget *modeStack) : ConnectionManager::ConnectionManager(Internal::MainWindow *mainWindow, QTabWidget *modeStack) :
QWidget(mainWindow), // Pip QWidget(mainWindow),
m_availableDevList(0), m_availableDevList(0),
m_connectBtn(0), m_connectBtn(0),
m_ioDev(NULL), m_ioDev(NULL),
m_mainWindow(mainWindow) polling(true),
m_mainWindow(mainWindow)
{ {
// Q_UNUSED(mainWindow);
/* QVBoxLayout *top = new QVBoxLayout;
top->setSpacing(0);
top->setMargin(0);*/
QHBoxLayout *layout = new QHBoxLayout; QHBoxLayout *layout = new QHBoxLayout;
layout->setSpacing(5); layout->setSpacing(5);
layout->setContentsMargins(5,5,5,5); layout->setContentsMargins(5,5,5,5);
layout->addWidget(new QLabel(tr("Connections:"))); layout->addWidget(new QLabel(tr("Connections:")));
m_availableDevList = new QComboBox; m_availableDevList = new QComboBox;
//m_availableDevList->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
m_availableDevList->setMinimumWidth(100); m_availableDevList->setMinimumWidth(100);
m_availableDevList->setMaximumWidth(150); m_availableDevList->setMaximumWidth(150);
m_availableDevList->setContextMenuPolicy(Qt::CustomContextMenu); m_availableDevList->setContextMenuPolicy(Qt::CustomContextMenu);
@ -72,30 +66,25 @@ ConnectionManager::ConnectionManager(Internal::MainWindow *mainWindow, QTabWidge
m_connectBtn->setEnabled(false); m_connectBtn->setEnabled(false);
layout->addWidget(m_connectBtn); layout->addWidget(m_connectBtn);
/* Utils::StyledBar *bar = new Utils::StyledBar;
bar->setLayout(layout);
top->addWidget(bar);*/
setLayout(layout); setLayout(layout);
// modeStack->insertCornerWidget(modeStack->cornerWidgetCount()-1, this);
modeStack->setCornerWidget(this, Qt::TopRightCorner); modeStack->setCornerWidget(this, Qt::TopRightCorner);
QObject::connect(m_connectBtn, SIGNAL(pressed()), this, SLOT(onConnectPressed())); QObject::connect(m_connectBtn, SIGNAL(pressed()), this, SLOT(onConnectPressed()));
} }
ConnectionManager::~ConnectionManager() ConnectionManager::~ConnectionManager()
{ {
disconnectDevice(); // Pip disconnectDevice();
suspendPolling(); // Pip suspendPolling();
} }
void ConnectionManager::init() void ConnectionManager::init()
{ {
//register to the plugin manager so we can receive //register to the plugin manager so we can receive
//new connection object from plugins //new connection object from plugins
QObject::connect(ExtensionSystem::PluginManager::instance(), SIGNAL(objectAdded(QObject*)), this, SLOT(objectAdded(QObject*))); QObject::connect(ExtensionSystem::PluginManager::instance(), SIGNAL(objectAdded(QObject*)), this, SLOT(objectAdded(QObject*)));
QObject::connect(ExtensionSystem::PluginManager::instance(), SIGNAL(aboutToRemoveObject(QObject*)), this, SLOT(aboutToRemoveObject(QObject*))); QObject::connect(ExtensionSystem::PluginManager::instance(), SIGNAL(aboutToRemoveObject(QObject*)), this, SLOT(aboutToRemoveObject(QObject*)));
} }
/** /**
@ -103,11 +92,11 @@ void ConnectionManager::init()
*/ */
bool ConnectionManager::connectDevice() bool ConnectionManager::connectDevice()
{ {
devListItem connection_device = findDevice(m_availableDevList->itemData(m_availableDevList->currentIndex(),Qt::ToolTipRole).toString()); DevListItem connection_device = findDevice(m_availableDevList->itemData(m_availableDevList->currentIndex(),Qt::ToolTipRole).toString());
if (!connection_device.connection) if (!connection_device.connection)
return false; return false;
QIODevice *io_dev = connection_device.connection->openDevice(connection_device.Name); QIODevice *io_dev = connection_device.connection->openDevice(connection_device.device.name);
if (!io_dev) if (!io_dev)
return false; return false;
@ -115,17 +104,6 @@ bool ConnectionManager::connectDevice()
// check if opening the device worked // check if opening the device worked
if (!io_dev->isOpen()) { if (!io_dev->isOpen()) {
qDebug() << "Error: io_dev->isOpen() returned FALSE .. could not open connection to " << connection_device.devName
<< ": " << io_dev->errorString();
// close the device
// EDOUARD: why do we close if we could not open ???
try {
connection_device.connection->closeDevice(connection_device.devName);
}
catch (...) { // handle exception
qDebug() << "Exception: connection_device.connection->closeDevice(" << connection_device.devName << ")";
}
return false; return false;
} }
@ -162,10 +140,11 @@ bool ConnectionManager::disconnectDevice()
emit deviceAboutToDisconnect(); emit deviceAboutToDisconnect();
try { try {
if (m_connectionDevice.connection) if (m_connectionDevice.connection) {
m_connectionDevice.connection->closeDevice(m_connectionDevice.devName); m_connectionDevice.connection->closeDevice(m_connectionDevice.getConName());
}
} catch (...) { // handle exception } catch (...) { // handle exception
qDebug() << "Exception: m_connectionDevice.connection->closeDevice(" << m_connectionDevice.devName << ")"; qDebug() << "Exception: m_connectionDevice.connection->closeDevice(" << m_connectionDevice.getConName() << ")";
} }
m_connectionDevice.connection = NULL; m_connectionDevice.connection = NULL;
@ -184,10 +163,7 @@ void ConnectionManager::objectAdded(QObject *obj)
{ {
//Check if a plugin added a connection object to the pool //Check if a plugin added a connection object to the pool
IConnection *connection = Aggregation::query<IConnection>(obj); IConnection *connection = Aggregation::query<IConnection>(obj);
if (!connection) return; if (!connection) return;
//qDebug() << "Connection object registered:" << connection->connectionName();
//qDebug() << connection->availableDevices();
//register devices and populate CB //register devices and populate CB
devChanged(connection); devChanged(connection);
@ -196,30 +172,30 @@ void ConnectionManager::objectAdded(QObject *obj)
// to do things // to do things
m_connectionsList.append(connection); m_connectionsList.append(connection);
QObject::connect(connection, SIGNAL(availableDevChanged(IConnection *)), this, SLOT(devChanged(IConnection *))); QObject::connect(connection, SIGNAL(availableDevChanged(IConnection *)), this, SLOT(devChanged(IConnection *)));
} }
void ConnectionManager::aboutToRemoveObject(QObject *obj) void ConnectionManager::aboutToRemoveObject(QObject *obj)
{ {
//Check if a plugin added a connection object to the pool //Check if a plugin added a connection object to the pool
IConnection *connection = Aggregation::query<IConnection>(obj); IConnection *connection = Aggregation::query<IConnection>(obj);
if (!connection) return; if (!connection) return;
if (m_connectionDevice.connection && m_connectionDevice.connection == connection) // Pip if (m_connectionDevice.connection && m_connectionDevice.connection == connection)
{ // we are currently using the one that is about to be removed { // we are currently using the one that is about to be removed
m_connectionDevice.connection = NULL; disconnectDevice();
m_ioDev = NULL; m_connectionDevice.connection = NULL;
} m_ioDev = NULL;
}
if (m_connectionsList.contains(connection)) if (m_connectionsList.contains(connection))
m_connectionsList.removeAt(m_connectionsList.indexOf(connection)); m_connectionsList.removeAt(m_connectionsList.indexOf(connection));
} }
void ConnectionManager::onConnectionDestroyed(QObject *obj) // Pip void ConnectionManager::onConnectionDestroyed(QObject *obj)
{ {
Q_UNUSED(obj) Q_UNUSED(obj)
//onConnectionClosed(obj);
disconnectDevice(); disconnectDevice();
} }
@ -242,17 +218,17 @@ void ConnectionManager::onConnectPressed()
/** /**
* Find a device by its displayed (visible on screen) name * Find a device by its displayed (visible on screen) name
*/ */
devListItem ConnectionManager::findDevice(const QString &devName) DevListItem ConnectionManager::findDevice(const QString &devName)
{ {
foreach (devListItem d, m_devList) foreach (DevListItem d, m_devList)
{ {
if (d.devName == devName) if (d.getConName() == devName)
return d; return d;
} }
qDebug() << "findDevice: cannot find " << devName << " in device list"; qDebug() << "findDevice: cannot find " << devName << " in device list";
devListItem d; DevListItem d;
d.connection = NULL; d.connection = NULL;
return d; return d;
} }
@ -264,13 +240,14 @@ devListItem ConnectionManager::findDevice(const QString &devName)
*/ */
void ConnectionManager::suspendPolling() void ConnectionManager::suspendPolling()
{ {
foreach (IConnection *cnx, m_connectionsList) foreach (IConnection *cnx, m_connectionsList)
{ {
cnx->suspendPolling(); cnx->suspendPolling();
} }
m_connectBtn->setEnabled(false); m_connectBtn->setEnabled(false);
m_availableDevList->setEnabled(false); m_availableDevList->setEnabled(false);
polling = false;
} }
/** /**
@ -279,36 +256,57 @@ void ConnectionManager::suspendPolling()
*/ */
void ConnectionManager::resumePolling() void ConnectionManager::resumePolling()
{ {
foreach (IConnection *cnx, m_connectionsList) foreach (IConnection *cnx, m_connectionsList)
{ {
cnx->resumePolling(); cnx->resumePolling();
} }
m_connectBtn->setEnabled(true); m_connectBtn->setEnabled(true);
m_availableDevList->setEnabled(true); m_availableDevList->setEnabled(true);
polling = true;
} }
/** /**
* Unregister all devices from one connection plugin * Synchronize the list of connections displayed with those physically
* \param[in] connection Connection type that you want to forget about :) * present
*/ * @param[in] connection Connection type that you want to forget about :)
void ConnectionManager::unregisterAll(IConnection *connection) */
void ConnectionManager::updateConnectionList(IConnection *connection)
{ {
for (QLinkedList<devListItem>::iterator iter = m_devList.begin(); iter != m_devList.end(); ) // Get the updated list of devices
{ QList <IConnection::device> availableDev = connection->availableDevices();
if (iter->connection == connection)
{
if (m_connectionDevice.connection && m_connectionDevice.connection == connection)
{ // we are currently using the one we are about to erase
//onConnectionClosed(m_connectionDevice.connection);
disconnectDevice();
}
iter = m_devList.erase(iter); // Go through the list of connections of that type. If they are not in the
} // available device list then remove them. If they are connected, then
else // disconnect them.
++iter; for (QLinkedList<DevListItem>::iterator iter = m_devList.begin(); iter != m_devList.end(); )
} {
if (iter->connection != connection) {
++iter;
continue;
}
// See if device exists in the updated availability list
bool found = availableDev.contains(iter->device);
if (!found) {
// we are currently using the one we are about to erase
if (m_connectionDevice.connection && m_connectionDevice.connection == connection && m_connectionDevice.device == iter->device) {
disconnectDevice();
}
iter = m_devList.erase(iter);
} else
++iter;
}
// Add any back to list that don't exist
foreach (IConnection::device dev, availableDev)
{
bool found = m_devList.contains(DevListItem(connection, dev));
if (!found) {
registerDevice(connection,dev);
}
}
} }
/** /**
@ -317,13 +315,9 @@ void ConnectionManager::unregisterAll(IConnection *connection)
* @param disp is the name that is displayed in the dropdown menu * @param disp is the name that is displayed in the dropdown menu
* @param name is the actual device name * @param name is the actual device name
*/ */
void ConnectionManager::registerDevice(IConnection *conn, const QString &devN, const QString &name, const QString &disp) void ConnectionManager::registerDevice(IConnection *conn, IConnection::device device)
{ {
devListItem d; DevListItem d(conn,device);
d.connection = conn;
d.devName = devN;
d.Name = name;
d.displayName=disp;
m_devList.append(d); m_devList.append(d);
} }
@ -344,41 +338,9 @@ void ConnectionManager::devChanged(IConnection *connection)
m_availableDevList->clear(); m_availableDevList->clear();
//remove registered devices of this IConnection from the list //remove registered devices of this IConnection from the list
unregisterAll(connection); updateConnectionList(connection);
//and add them back in the list updateConnectionDropdown();
QList <IConnection::device> availableDev = connection->availableDevices();
foreach (IConnection::device dev, availableDev)
{
QString cbName = connection->shortName() + ": " + dev.name;
QString disp = connection->shortName() + " : " + dev.displayName;
registerDevice(connection,cbName,dev.name,disp);
}
//add all the list again to the combobox
foreach (devListItem d, m_devList)
{
m_availableDevList->addItem(d.displayName);
m_availableDevList->setItemData(m_availableDevList->count()-1,(const QString)d.devName,Qt::ToolTipRole);
if(!m_ioDev && d.displayName.startsWith("USB"))
{
if(m_mainWindow->generalSettings()->autoConnect() || m_mainWindow->generalSettings()->autoSelect())
m_availableDevList->setCurrentIndex(m_availableDevList->count()-1);
if(m_mainWindow->generalSettings()->autoConnect())
{
connectDevice();
qDebug()<<"ConnectionManager::devChanged autoconnected USB device";
}
}
}
if(m_ioDev)//if a device is connected make it the one selected on the dropbox
{
for(int x=0;x<m_availableDevList->count();++x)
{
if(m_connectionDevice.devName==m_availableDevList->itemData(x,Qt::ToolTipRole).toString())
m_availableDevList->setCurrentIndex(x);
}
}
//disable connection button if the liNameif (m_availableDevList->count() > 0) //disable connection button if the liNameif (m_availableDevList->count() > 0)
@ -388,6 +350,33 @@ void ConnectionManager::devChanged(IConnection *connection)
m_connectBtn->setEnabled(false); m_connectBtn->setEnabled(false);
} }
void ConnectionManager::updateConnectionDropdown()
{
//add all the list again to the combobox
foreach (DevListItem d, m_devList)
{
m_availableDevList->addItem(d.getConName());
m_availableDevList->setItemData(m_availableDevList->count()-1, d.getConName(), Qt::ToolTipRole);
if(!m_ioDev && d.getConName().startsWith("USB"))
{
if(m_mainWindow->generalSettings()->autoConnect() || m_mainWindow->generalSettings()->autoSelect())
m_availableDevList->setCurrentIndex(m_availableDevList->count()-1);
if(m_mainWindow->generalSettings()->autoConnect() && polling)
{
qDebug() << "Automatically opening device";
connectDevice();
qDebug()<<"ConnectionManager::devChanged autoconnected USB device";
}
}
}
if(m_ioDev)//if a device is connected make it the one selected on the dropbox
{
for(int x=0;x<m_availableDevList->count();++x)
{
if(m_connectionDevice.getConName()==m_availableDevList->itemData(x,Qt::ToolTipRole).toString())
m_availableDevList->setCurrentIndex(x);
}
}
} }
void Core::ConnectionManager::connectionsCallBack() void Core::ConnectionManager::connectionsCallBack()
@ -398,4 +387,6 @@ void Core::ConnectionManager::connectionsCallBack()
} }
connectionBackup.clear(); connectionBackup.clear();
disconnect(ExtensionSystem::PluginManager::instance(),SIGNAL(pluginsLoadEnded()),this,SLOT(connectionsCallBack())); disconnect(ExtensionSystem::PluginManager::instance(),SIGNAL(pluginsLoadEnded()),this,SLOT(connectionsCallBack()));
}
} //namespace Core } //namespace Core

View File

@ -32,6 +32,7 @@
#include <QWidget> #include <QWidget>
#include "mainwindow.h" #include "mainwindow.h"
#include "generalsettings.h" #include "generalsettings.h"
#include <coreplugin/iconnection.h>
#include <QtCore/QVector> #include <QtCore/QVector>
#include <QtCore/QIODevice> #include <QtCore/QIODevice>
#include <QtCore/QLinkedList> #include <QtCore/QLinkedList>
@ -55,12 +56,26 @@ namespace Internal {
} // namespace Internal } // namespace Internal
struct devListItem class DevListItem
{ {
public:
DevListItem(IConnection *c, IConnection::device d) :
connection(c), device(d) { }
DevListItem() : connection(NULL) { }
QString getConName() {
if (connection == NULL)
return "";
return connection->shortName() + ": " + device.displayName;
}
bool operator==(const DevListItem &rhs) {
return connection == rhs.connection && device == rhs.device;
}
IConnection *connection; IConnection *connection;
QString devName; IConnection::device device;
QString Name;
QString displayName;
}; };
@ -75,15 +90,16 @@ public:
void init(); void init();
QIODevice* getCurrentConnection() { return m_ioDev; } QIODevice* getCurrentConnection() { return m_ioDev; }
devListItem getCurrentDevice() { return m_connectionDevice;} DevListItem getCurrentDevice() { return m_connectionDevice;}
bool disconnectDevice(); bool disconnectDevice();
void suspendPolling(); void suspendPolling();
void resumePolling(); void resumePolling();
protected: protected:
void unregisterAll(IConnection *connection); void updateConnectionList(IConnection *connection);
void registerDevice(IConnection *conn, const QString &devN, const QString &name, const QString &disp); void registerDevice(IConnection *conn, IConnection::device device);
devListItem findDevice(const QString &devName); void updateConnectionDropdown();
DevListItem findDevice(const QString &devName);
signals: signals:
void deviceConnected(QIODevice *dev); void deviceConnected(QIODevice *dev);
@ -96,25 +112,25 @@ private slots:
void onConnectPressed(); void onConnectPressed();
void devChanged(IConnection *connection); void devChanged(IConnection *connection);
// void onConnectionClosed(QObject *obj);
void onConnectionDestroyed(QObject *obj); void onConnectionDestroyed(QObject *obj);
void connectionsCallBack(); //used to call devChange after all the plugins are loaded void connectionsCallBack(); //used to call devChange after all the plugins are loaded
protected: protected:
QComboBox *m_availableDevList; QComboBox *m_availableDevList;
QPushButton *m_connectBtn; QPushButton *m_connectBtn;
QLinkedList<devListItem> m_devList; QLinkedList<DevListItem> m_devList;
QList<IConnection*> m_connectionsList; QList<IConnection*> m_connectionsList;
//currently connected connection plugin //currently connected connection plugin
devListItem m_connectionDevice; DevListItem m_connectionDevice;
//currently connected QIODevice //currently connected QIODevice
QIODevice *m_ioDev; QIODevice *m_ioDev;
private: private:
bool connectDevice(); bool connectDevice();
Internal::MainWindow *m_mainWindow; bool polling;
QList <IConnection *> connectionBackup; Internal::MainWindow *m_mainWindow;
QList <IConnection *> connectionBackup;
}; };

View File

@ -174,7 +174,7 @@ QList <Core::IConnection::device> IPconnectionConnection::availableDevices()
return list; return list;
} }
QIODevice *IPconnectionConnection::openDevice(const QString &deviceName) QIODevice *IPconnectionConnection::openDevice(const QString &)
{ {
Q_UNUSED(deviceName); Q_UNUSED(deviceName);
@ -210,7 +210,7 @@ QIODevice *IPconnectionConnection::openDevice(const QString &deviceName)
return ipSocket; return ipSocket;
} }
void IPconnectionConnection::closeDevice(const QString &deviceName) void IPconnectionConnection::closeDevice(const QString &)
{ {
Q_UNUSED(deviceName); Q_UNUSED(deviceName);

View File

@ -32,11 +32,15 @@
#include <stdlib.h> #include <stdlib.h>
#include <stdint.h> #include <stdint.h>
#include <QDebug> #include <QDebug>
#include <QMutex>
#include <QString> #include <QString>
#include "rawhid_global.h" #include "rawhid_global.h"
#if defined( Q_OS_MAC) #if defined( Q_OS_MAC)
#include <IOKit/IOKitLib.h>
#include <IOKit/hid/IOHIDLib.h>
#include <CoreFoundation/CFString.h>
#elif defined(Q_OS_UNIX) #elif defined(Q_OS_UNIX)
//#elif defined(Q_OS_LINUX) //#elif defined(Q_OS_LINUX)
@ -96,22 +100,43 @@ public:
pjrc_rawhid(); pjrc_rawhid();
~pjrc_rawhid(); ~pjrc_rawhid();
int open(int max, int vid, int pid, int usage_page, int usage); int open(int max, int vid, int pid, int usage_page, int usage);
int receive(int num, void *buf, int len, int timeout); int receive(int, void *buf, int len, int timeout);
void close(int num); void close(int num);
int send(int num, void *buf, int len, int timeout); int send(int num, void *buf, int len, int timeout);
QString getserial(int num); QString getserial(int num);
void mytest(int num);
signals: signals:
void deviceUnplugged(int);//just to make pips changes compile void deviceUnplugged(int);
#if defined( Q_OS_MAC)
#endif
private: private:
#if defined( Q_OS_MAC) #if defined( Q_OS_MAC)
// Static callbacks called by the HID system with handles to the PJRC object
static void attach_callback(void *, IOReturn, void *, IOHIDDeviceRef);
static void dettach_callback(void *, IOReturn, void *hid_mgr, IOHIDDeviceRef dev);
static void input_callback(void *, IOReturn, void *, IOHIDReportType, uint32_t, uint8_t *, CFIndex);
static void timeout_callback(CFRunLoopTimerRef, void *);
// Non static methods to call into
void attach(IOHIDDeviceRef dev);
void dettach(IOHIDDeviceRef dev);
void input(uint8_t *, CFIndex);
// Platform specific handles for the USB device
IOHIDManagerRef hid_manager;
IOHIDDeviceRef dev;
CFRunLoopRef the_correct_runloop;
CFRunLoopRef received_runloop;
static const int BUFFER_SIZE = 64;
uint8_t buffer[BUFFER_SIZE];
int attach_count;
int buffer_count;
bool device_open;
bool unplugged;
QMutex *m_writeMutex;
QMutex *m_readMutex;
#elif defined(Q_OS_UNIX) #elif defined(Q_OS_UNIX)
//#elif defined(Q_OS_LINUX)
hid_t *first_hid; hid_t *first_hid;
hid_t *last_hid; hid_t *last_hid;

View File

@ -40,102 +40,67 @@
#include "pjrc_rawhid.h" #include "pjrc_rawhid.h"
#include <unistd.h> #include <unistd.h>
#include <IOKit/IOKitLib.h>
#include <IOKit/hid/IOHIDLib.h>
#include <CoreFoundation/CFString.h>
#include <QString> #include <QString>
#include <QThread> #include <QThread>
#include <QTimer> #include <QTimer>
#include <QCoreApplication> #include <QCoreApplication>
class delay : public QThread struct timeout_info {
CFRunLoopRef loopRef;
bool timed_out;
};
pjrc_rawhid::pjrc_rawhid() :
device_open(false), hid_manager(NULL), buffer_count(0), unplugged(false)
{ {
public: m_writeMutex = new QMutex();
static void msleep(unsigned long msecs) m_readMutex = new QMutex();
{
QThread::msleep(msecs);
}
};
#define BUFFER_SIZE 64
//#define printf qDebug
#define printf
typedef struct hid_struct hid_t;
typedef struct buffer_struct buffer_t;
static hid_t *first_hid = NULL;
static hid_t *last_hid = NULL;
// Make sure we use the correct runloop
CFRunLoopRef the_correct_runloop = NULL;
struct hid_struct {
IOHIDDeviceRef ref;
int open;
uint8_t buffer[BUFFER_SIZE];
buffer_t *first_buffer;
buffer_t *last_buffer;
struct hid_struct *prev;
struct hid_struct *next;
};
struct buffer_struct {
struct buffer_struct *next;
uint32_t len;
uint8_t buf[BUFFER_SIZE];
};
static void add_hid(hid_t *);
static hid_t * get_hid(int);
static void free_all_hid(void);
static void hid_close(hid_t *);
static void attach_callback(void *, IOReturn, void *, IOHIDDeviceRef);
static void detach_callback(void *, IOReturn, void *hid_mgr, IOHIDDeviceRef dev);
static void input_callback(void *, IOReturn, void *, IOHIDReportType, uint32_t, uint8_t *, CFIndex);
static void output_callback(hid_t *context, IOReturn ret, void *sender, IOHIDReportType type, uint32_t id, uint8_t *data, CFIndex len);
static void timeout_callback(CFRunLoopTimerRef, void *);
pjrc_rawhid::pjrc_rawhid()
{
first_hid = NULL;
last_hid = NULL;
} }
pjrc_rawhid::~pjrc_rawhid() pjrc_rawhid::~pjrc_rawhid()
{ {
if (device_open) {
close(0);
}
if (m_writeMutex) {
delete m_writeMutex;
m_writeMutex = NULL;
}
if (m_readMutex) {
delete m_readMutex;
m_readMutex = NULL;
}
} }
// open - open 1 or more devices /**
// * @brief open - open 1 or more devices
// Inputs: * @param[in] max maximum number of devices to open
// max = maximum number of devices to open * @param[in] vid Vendor ID, or -1 if any
// vid = Vendor ID, or -1 if any * @param[in] pid Product ID, or -1 if any
// pid = Product ID, or -1 if any * @param[in] usage_page top level usage page, or -1 if any
// usage_page = top level usage page, or -1 if any * @param[in] usage top level usage number, or -1 if any
// usage = top level usage number, or -1 if any * @returns actual number of devices opened
// Output: */
// actual number of devices opened
//
int pjrc_rawhid::open(int max, int vid, int pid, int usage_page, int usage) int pjrc_rawhid::open(int max, int vid, int pid, int usage_page, int usage)
{ {
static IOHIDManagerRef hid_manager=NULL;
CFMutableDictionaryRef dict; CFMutableDictionaryRef dict;
CFNumberRef num; CFNumberRef num;
IOReturn ret; IOReturn ret;
hid_t *p;
int count=0;
if (first_hid) free_all_hid(); Q_ASSERT(hid_manager == NULL);
//printf("pjrc_rawhid_open, max=%d\n", max); Q_ASSERT(device_open == false);
if (max < 1) return 0;
attach_count = 0;
// Start the HID Manager // Start the HID Manager
// http://developer.apple.com/technotes/tn2007/tn2187.html hid_manager = IOHIDManagerCreate(kCFAllocatorDefault, kIOHIDOptionsTypeNone);
if (!hid_manager) { if (hid_manager == NULL || CFGetTypeID(hid_manager) != IOHIDManagerGetTypeID()) {
hid_manager = IOHIDManagerCreate(kCFAllocatorDefault, kIOHIDOptionsTypeNone); if (hid_manager) CFRelease(hid_manager);
if (hid_manager == NULL || CFGetTypeID(hid_manager) != IOHIDManagerGetTypeID()) { return 0;
if (hid_manager) CFRelease(hid_manager);
return 0;
}
} }
if (vid > 0 || pid > 0 || usage_page > 0 || usage > 0) { if (vid > 0 || pid > 0 || usage_page > 0 || usage > 0) {
// Tell the HID Manager what type of devices we want // Tell the HID Manager what type of devices we want
dict = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, dict = CFDictionaryCreateMutable(kCFAllocatorDefault, 0,
@ -166,162 +131,147 @@ int pjrc_rawhid::open(int max, int vid, int pid, int usage_page, int usage)
} else { } else {
IOHIDManagerSetDeviceMatching(hid_manager, NULL); IOHIDManagerSetDeviceMatching(hid_manager, NULL);
} }
// Set the run loop reference before configuring the attach callback
the_correct_runloop = CFRunLoopGetCurrent();
// set up a callbacks for device attach & detach // set up a callbacks for device attach & detach
IOHIDManagerScheduleWithRunLoop(hid_manager, CFRunLoopGetCurrent(), IOHIDManagerScheduleWithRunLoop(hid_manager, CFRunLoopGetCurrent(),
kCFRunLoopDefaultMode); kCFRunLoopDefaultMode);
IOHIDManagerRegisterDeviceMatchingCallback(hid_manager, attach_callback, NULL); IOHIDManagerRegisterDeviceMatchingCallback(hid_manager, pjrc_rawhid::attach_callback, this);
IOHIDManagerRegisterDeviceRemovalCallback(hid_manager, detach_callback, NULL); IOHIDManagerRegisterDeviceRemovalCallback(hid_manager, pjrc_rawhid::dettach_callback, this);
ret = IOHIDManagerOpen(hid_manager, kIOHIDOptionsTypeNone); ret = IOHIDManagerOpen(hid_manager, kIOHIDOptionsTypeNone);
if (ret != kIOReturnSuccess) { if (ret != kIOReturnSuccess) {
printf("Could not start IOHIDManager");
IOHIDManagerUnscheduleFromRunLoop(hid_manager, IOHIDManagerUnscheduleFromRunLoop(hid_manager,
CFRunLoopGetCurrent(), kCFRunLoopDefaultMode); CFRunLoopGetCurrent(), kCFRunLoopDefaultMode);
CFRelease(hid_manager); CFRelease(hid_manager);
return 0; return 0;
} }
// Set the run loop reference:
the_correct_runloop = CFRunLoopGetCurrent();
printf("run loop\n");
// let it do the callback for all devices // let it do the callback for all devices
while (CFRunLoopRunInMode(kCFRunLoopDefaultMode, 0, true) == kCFRunLoopRunHandledSource) ; while (CFRunLoopRunInMode(kCFRunLoopDefaultMode, 0, true) == kCFRunLoopRunHandledSource) ;
// count up how many were added by the callback // count up how many were added by the callback
for (p = first_hid; p; p = p->next) count++; return attach_count;
return count;
} }
// recveive - receive a packet /**
// Inputs: * @brief receive - receive a packet
// num = device to receive from (zero based) * @param[in] num device to receive from (unused now)
// buf = buffer to receive packet * @param[in] buf buffer to receive packet
// len = buffer's size * @param[in] len buffer's size
// timeout = time to wait, in milliseconds * @param[in] timeout = time to wait, in milliseconds
// Output: * @returns number of bytes received, or -1 on error
// number of bytes received, or -1 on error */
// int pjrc_rawhid::receive(int, void *buf, int len, int timeout)
int pjrc_rawhid::receive(int num, void *buf, int len, int timeout)
{ {
hid_t *hid; QMutexLocker locker(m_readMutex);
buffer_t *b; Q_UNUSED(locker);
CFRunLoopTimerRef timer=NULL;
CFRunLoopTimerContext context;
int ret=0, timeout_occurred=0;
if (len < 1) return 0; if (!device_open)
hid = get_hid(num); return -1;
if (!hid || !hid->open) return -1;
if ((b = hid->first_buffer) != NULL) {
if (len > b->len) len = b->len;
memcpy(buf, b->buf, len);
hid->first_buffer = b->next;
free(b);
return len;
}
memset(&context, 0, sizeof(context));
context.info = &timeout_occurred;
timer = CFRunLoopTimerCreate(NULL, CFAbsoluteTimeGetCurrent() +
(double)timeout / 1000.0, 0, 0, 0, timeout_callback, &context);
CFRunLoopAddTimer(CFRunLoopGetCurrent(), timer, kCFRunLoopDefaultMode);
the_correct_runloop = CFRunLoopGetCurrent();
//qDebug("--");
while (1) {
//qDebug(".");
CFRunLoopRun(); // Found the problem: somehow the input_callback does not
// stop this CFRunLoopRun because it is hooked to a different run loop !!!
// Hence the use of the "correct_runloop" variable above.
//qDebug(" ..");
if ((b = hid->first_buffer) != NULL) { // Pass information to the callback to stop this run loop and signal if a timeout occurred
if (len > b->len) len = b->len; struct timeout_info info;
memcpy(buf, b->buf, len); info.loopRef = CFRunLoopGetCurrent();;
hid->first_buffer = b->next; info.timed_out = false;
free(b); CFRunLoopTimerContext context;
ret = len; memset(&context, 0, sizeof(context));
//qDebug("*************"); context.info = &info;
break;
} // Set up the timer for the timeout
if (!hid->open) { CFRunLoopTimerRef timer;
printf("pjrc_rawhid_recv, device not open\n"); timer = CFRunLoopTimerCreate(NULL, CFAbsoluteTimeGetCurrent() + (double)timeout / 1000.0, 0, 0, 0, timeout_callback, &context);
ret = -1; CFRunLoopAddTimer(CFRunLoopGetCurrent(), timer, kCFRunLoopDefaultMode);
break;
} received_runloop = CFRunLoopGetCurrent();
if (timeout_occurred)
// Run the CFRunLoop until either a timeout or data is available
while(1) {
if (buffer_count != 0) {
if (len > buffer_count) len = buffer_count;
memcpy(buf, buffer, len);
buffer_count = 0;
break; break;
} } else if (info.timed_out) {
CFRunLoopTimerInvalidate(timer); len = 0;
CFRelease(timer);
return ret;
}
// send - send a packet
// Inputs:
// num = device to transmit to (zero based)
// buf = buffer containing packet to send
// len = number of bytes to transmit
// timeout = time to wait, in milliseconds
// Output:
// number of bytes sent, or -1 on error
//
int pjrc_rawhid::send(int num, void *buf, int len, int timeout)
{
hid_t *hid;
int result=-100;
hid = get_hid(num);
if (!hid || !hid->open) return -1;
#if 1
#warning "Send timeout not implemented on MACOSX"
uint8_t *report_buf = (uint8_t *) malloc(len);
memcpy(&report_buf[0], buf,len);
// Note: packet processing done in OS indepdent code
IOReturn ret = IOHIDDeviceSetReport(hid->ref, kIOHIDReportTypeOutput, 2, (uint8_t *)report_buf, len);
result = (ret == kIOReturnSuccess) ? len : -1;
if (err_get_system(ret) == err_get_system(sys_iokit))
{
// The error was in the I/O Kit system
UInt32 codeValue = err_get_code(ret);
qDebug("Returned: %x", codeValue);
// Can now perform test on error code, display it to user, or whatever.
usleep(1000000);
}
#endif
#if 0
// No matter what I tried this never actually sends an output
// report and output_callback never gets called. Why??
// Did I miss something? This is exactly the same params as
// the sync call that works. Is it an Apple bug?
// (submitted to Apple on 22-sep-2009, problem ID 7245050)
//
IOHIDDeviceScheduleWithRunLoop(hid->ref, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode);
// should already be scheduled with run loop by attach_callback,
// sadly this doesn't make any difference either way
//
IOHIDDeviceSetReportWithCallback(hid->ref, kIOHIDReportTypeOutput,
0, buf, len, (double)timeout / 1000.0, output_callback, &result);
while (1) {
printf("enter run loop (send)\n");
CFRunLoopRun();
printf("leave run loop (send)\n");
if (result > -100) break;
if (!hid->open) {
result = -1;
break; break;
} }
CFRunLoopRun(); // Wait for data
} }
#endif
return result; CFRunLoopTimerInvalidate(timer);
CFRelease(timer);
received_runloop = NULL;
return len;
} }
/**
* @brief Helper class that will workaround the fact
* that the HID send is broken on OSX
*/
class Sender : public QThread
{
public:
Sender(IOHIDDeviceRef d, uint8_t * b, int l) :
dev(d), buf(b), len(l), result(-1) { }
void run() {
ret = IOHIDDeviceSetReport(dev, kIOHIDReportTypeOutput, 2, buf, len);
result = (ret == kIOReturnSuccess) ? len : -1;
}
int result;
IOReturn ret;
private:
IOHIDDeviceRef dev;
uint8_t * buf;
int len;
};
/**
* @brief send - send a packet
* @param[in] num device to transmit to (zero based)
* @param[in] buf buffer containing packet to send
* @param[in] len number of bytes to transmit
* @param[in] timeout = time to wait, in milliseconds
* @returns number of bytes sent, or -1 on error
*/
int pjrc_rawhid::send(int, void *buf, int len, int timeout)
{
// This lock ensures that when closing we don't do it until the
// write has terminated (and then the device_open flag is set to false)
QMutexLocker locker(m_writeMutex);
Q_UNUSED(locker);
if(!device_open || unplugged) {
return -1;
}
uint8_t *report_buf = (uint8_t *) malloc(len);
memcpy(&report_buf[0], buf,len);
QEventLoop el;
Sender sender(dev, report_buf, len);
connect(&sender, SIGNAL(finished()), &el, SLOT(quit()));
sender.start();
QTimer::singleShot(timeout, &el, SLOT(quit()));
el.exec();
return sender.result;
}
//! Get the serial number for a HID device
QString pjrc_rawhid::getserial(int num) { QString pjrc_rawhid::getserial(int num) {
hid_t *hid; QMutexLocker locker(m_readMutex);
char buf[128]; Q_UNUSED(locker);
hid = get_hid(num); if (!device_open || unplugged)
return "";
if (!hid || !hid->open) return QString("Error"); CFTypeRef serialnum = IOHIDDeviceGetProperty(dev, CFSTR(kIOHIDSerialNumberKey));
CFTypeRef serialnum = IOHIDDeviceGetProperty(hid->ref, CFSTR(kIOHIDSerialNumberKey));
if(serialnum && CFGetTypeID(serialnum) == CFStringGetTypeID()) if(serialnum && CFGetTypeID(serialnum) == CFStringGetTypeID())
{ {
//Note: I'm not sure it will always succeed if encoded as MacRoman but that //Note: I'm not sure it will always succeed if encoded as MacRoman but that
@ -334,149 +284,106 @@ QString pjrc_rawhid::getserial(int num) {
return QString("Error"); return QString("Error");
} }
// close - close a device //! Close the HID device
// void pjrc_rawhid::close(int)
// Inputs:
// num = device to close (zero based)
// Output
// (nothing)
//
void pjrc_rawhid::close(int num)
{ {
hid_t *hid; // Make sure any pending locks are done
QMutexLocker lock(m_writeMutex);
hid = get_hid(num); if (device_open) {
if (!hid || !hid->open) return; device_open = false;
hid_close(hid); CFRunLoopStop(the_correct_runloop);
hid->open = 0;
if (!unplugged) {
IOHIDDeviceUnscheduleFromRunLoop(dev, the_correct_runloop, kCFRunLoopDefaultMode);
IOHIDDeviceRegisterInputReportCallback(dev, buffer, sizeof(buffer), NULL, NULL);
IOHIDDeviceClose(dev, kIOHIDOptionsTypeNone);
}
IOHIDManagerRegisterDeviceRemovalCallback(hid_manager, NULL, NULL);
IOHIDManagerClose(hid_manager, 0);
dev = NULL;
hid_manager = NULL;
}
} }
// /**
// * @brief input Called to add input data to the buffer
// Private Functions * @param[in] id Report id
// * @param[in] data The data buffer
// * @param[in] len The report length
static void input_callback(void *context, IOReturn ret, void *sender, IOHIDReportType type, uint32_t id, uint8_t *data, CFIndex len) */
void pjrc_rawhid::input(uint8_t *data, CFIndex len)
{ {
buffer_t *n; if (!device_open)
hid_t *hid; return;
//qDebug("input_callback, ret: %i - report id: %i buf: %x %x, len: %d\n", ret, id, data[0], data[1], len);
if (ret != kIOReturnSuccess || len < 1) return;
hid = (hid_t*)context;
if (!hid || hid->ref != sender) return;
printf("Processing packet");
n = (buffer_t *)malloc(sizeof(buffer_t));
if (!n) return;
if (len > BUFFER_SIZE) len = BUFFER_SIZE; if (len > BUFFER_SIZE) len = BUFFER_SIZE;
// Note: packet preprocessing done in OS independent code // Note: packet preprocessing done in OS independent code
memcpy(n->buf, &data[0], len); memcpy(buffer, &data[0], len);
n->len = len; buffer_count = len;
n->next = NULL;
if (!hid->first_buffer || !hid->last_buffer) { if (received_runloop)
hid->first_buffer = hid->last_buffer = n; CFRunLoopStop(received_runloop);
} else {
hid->last_buffer->next = n;
hid->last_buffer = n;
}
//qDebug() << "Stop CFRunLoop from input_callback" << CFRunLoopGetCurrent();
CFRunLoopStop(the_correct_runloop);
} }
static void timeout_callback(CFRunLoopTimerRef timer, void *info) //! Callback for the HID driver on an input report
void pjrc_rawhid::input_callback(void *c, IOReturn ret, void *sender, IOHIDReportType type, uint32_t id, uint8_t *data, CFIndex len)
{ {
//qDebug("timeout_callback\n"); if (ret != kIOReturnSuccess || len < 1) return;
*(int *)info = 1;
//qDebug() << "Stop CFRunLoop from timeout_callback" << CFRunLoopGetCurrent(); pjrc_rawhid *context = (pjrc_rawhid *) c;
CFRunLoopStop(CFRunLoopGetCurrent()); context->input(data, len);
} }
static void add_hid(hid_t *h) //! Timeout used for the
void pjrc_rawhid::timeout_callback(CFRunLoopTimerRef, void *i)
{ {
if (!first_hid || !last_hid) { struct timeout_info *info = (struct timeout_info *) i;
first_hid = last_hid = h; info->timed_out = true;
h->next = h->prev = NULL; CFRunLoopStop(info->loopRef);
return;
}
last_hid->next = h;
h->prev = last_hid;
h->next = NULL;
last_hid = h;
} }
//! Called on a dettach event
static hid_t * get_hid(int num) void pjrc_rawhid::dettach(IOHIDDeviceRef d)
{ {
hid_t *p; unplugged = true;
for (p = first_hid; p && num > 0; p = p->next, num--) ; if (d == dev)
return p; emit deviceUnplugged(0);
} }
//! Called from the USB system and forwarded to the instance (context)
static void free_all_hid(void) void pjrc_rawhid::dettach_callback(void *context, IOReturn, void *, IOHIDDeviceRef dev)
{ {
hid_t *p, *q; pjrc_rawhid *p = (pjrc_rawhid*) context;
p->dettach(dev);
for (p = first_hid; p; p = p->next) {
hid_close(p);
}
p = first_hid;
while (p) {
q = p;
p = p->next;
free(q);
}
first_hid = last_hid = NULL;
} }
/**
static void hid_close(hid_t *hid) * @brief Called by the USB system
* @param dev The device that was attached
*/
void pjrc_rawhid::attach(IOHIDDeviceRef d)
{ {
if (!hid || !hid->open || !hid->ref) return; // Store the device handle
IOHIDDeviceUnscheduleFromRunLoop(hid->ref, CFRunLoopGetCurrent( ), kCFRunLoopDefaultMode); dev = d;
IOHIDDeviceClose(hid->ref, kIOHIDOptionsTypeNone);
hid->ref = NULL;
}
static void detach_callback(void *context, IOReturn r, void *hid_mgr, IOHIDDeviceRef dev)
{
hid_t *p;
printf("detach callback\n");
for (p = first_hid; p; p = p->next) {
if (p->ref == dev) {
p->open = 0;
CFRunLoopStop(CFRunLoopGetCurrent());
return;
}
}
}
static void attach_callback(void *context, IOReturn r, void *hid_mgr, IOHIDDeviceRef dev)
{
struct hid_struct *h;
printf("attach callback\n");
if (IOHIDDeviceOpen(dev, kIOHIDOptionsTypeNone) != kIOReturnSuccess) return; if (IOHIDDeviceOpen(dev, kIOHIDOptionsTypeNone) != kIOReturnSuccess) return;
h = (hid_t *)malloc(sizeof(hid_t)); // Disconnect the attach callback since we don't want to automatically reconnect
if (!h) return; IOHIDManagerRegisterDeviceMatchingCallback(hid_manager, NULL, NULL);
memset(h, 0, sizeof(hid_t)); IOHIDDeviceScheduleWithRunLoop(dev, the_correct_runloop, kCFRunLoopDefaultMode);
IOHIDDeviceScheduleWithRunLoop(dev, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode); IOHIDDeviceRegisterInputReportCallback(dev, buffer, sizeof(buffer), pjrc_rawhid::input_callback, this);
IOHIDDeviceRegisterInputReportCallback(dev, h->buffer, sizeof(h->buffer), input_callback, h);
h->ref = dev; attach_count++;
h->open = 1; device_open = true;
add_hid(h); unplugged = false;
} }
static void output_callback(hid_t *context, IOReturn ret, void *sender, IOHIDReportType type, uint32_t id, uint8_t *data, CFIndex len) //! Called from the USB system and forwarded to the instance (context)
void pjrc_rawhid::attach_callback(void *context, IOReturn r, void *hid_mgr, IOHIDDeviceRef dev)
{ {
printf("output_callback, r=%d\n", ret); pjrc_rawhid *p = (pjrc_rawhid*) context;
if (ret == kIOReturnSuccess) { p->attach(dev);
*(int *)context = len;
} else {
// timeout if not success?
*(int *)context = 0;
}
CFRunLoopStop(CFRunLoopGetCurrent());
} }

View File

@ -138,19 +138,20 @@ RawHIDReadThread::RawHIDReadThread(RawHID *hid)
hidno(hid->m_deviceNo), hidno(hid->m_deviceNo),
m_running(true) m_running(true)
{ {
hid->m_startedMutex->lock();
} }
RawHIDReadThread::~RawHIDReadThread() RawHIDReadThread::~RawHIDReadThread()
{ {
m_running = false; m_running = false;
//wait for the thread to terminate //wait for the thread to terminate
if(wait(1000) == false) if(wait(10000) == false)
qDebug() << "Cannot terminate RawHIDReadThread"; qDebug() << "Cannot terminate RawHIDReadThread";
} }
void RawHIDReadThread::run() void RawHIDReadThread::run()
{ {
qDebug() << "Read thread started"; m_running = m_hid->openDevice();
while(m_running) while(m_running)
{ {
//here we use a temporary buffer so we don't need to lock //here we use a temporary buffer so we don't need to lock
@ -182,6 +183,7 @@ void RawHIDReadThread::run()
m_running=false; m_running=false;
} }
} }
m_hid->closeDevice();
} }
int RawHIDReadThread::getReadData(char *data, int size) int RawHIDReadThread::getReadData(char *data, int size)
@ -216,13 +218,12 @@ RawHIDWriteThread::~RawHIDWriteThread()
{ {
m_running = false; m_running = false;
//wait for the thread to terminate //wait for the thread to terminate
if(wait(1000) == false) if(wait(10000) == false)
qDebug() << "Cannot terminate RawHIDReadThread"; qDebug() << "Cannot terminate RawHIDReadThread";
} }
void RawHIDWriteThread::run() void RawHIDWriteThread::run()
{ {
qDebug() << "Write thread started";
while(m_running) while(m_running)
{ {
char buffer[WRITE_SIZE] = {0}; char buffer[WRITE_SIZE] = {0};
@ -300,54 +301,66 @@ RawHID::RawHID(const QString &deviceName)
m_mutex(NULL) m_mutex(NULL)
{ {
m_mutex = new QMutex(QMutex::Recursive); m_mutex = new QMutex(QMutex::Recursive);
m_startedMutex = new QMutex();
// detect if the USB device is unplugged // detect if the USB device is unplugged
QObject::connect(&dev, SIGNAL(deviceUnplugged(int)), this, SLOT(onDeviceUnplugged(int))); QObject::connect(&dev, SIGNAL(deviceUnplugged(int)), this, SLOT(onDeviceUnplugged(int)));
int opened = dev.open(USB_MAX_DEVICES, USBMonitor::idVendor_OpenPilot, -1, USB_USAGE_PAGE, USB_USAGE);
for (int i =0; i< opened; i++) {
if (deviceName == dev.getserial(i))
m_deviceNo = i;
else
dev.close(i);
}
/*
// TODO: NOT WORKING FOR MULTIPLE DEVICES with the same PID!
QList<USBPortInfo> devices = USBMonitor::instance()->availableDevices(USBMonitor::idVendor_OpenPilot,-1,-1,USBMonitor::Running);
foreach( USBPortInfo device, devices) {
if (deviceName == device.serialNumber) {
opened = dev.open(1,device.vendorID, device.productID,USB_USAGE_PAGE,USB_USAGE);
break;
}
}
*/
//didn't find the device we are trying to open (shouldnt happen)
if (opened < 0)
{
qDebug() << "Error: cannot open device " << deviceName;
return;
}
//m_deviceNo = 0;
m_readThread = new RawHIDReadThread(this);
m_writeThread = new RawHIDWriteThread(this); m_writeThread = new RawHIDWriteThread(this);
// Starting the read thread will lock the m_startexMutex until the
// device is opened (which happens in that thread).
m_readThread = new RawHIDReadThread(this);
m_readThread->start(); m_readThread->start();
m_startedMutex->lock();
}
/**
* @brief RawHID::openDevice This method opens the USB connection
* It is uses as a callback from the read thread so that the USB
* system code is registered in that thread instead of the calling
* thread (usually UI)
*/
bool RawHID::openDevice() {
int opened = dev.open(USB_MAX_DEVICES, USBMonitor::idVendor_OpenPilot, -1, USB_USAGE_PAGE, USB_USAGE);
for (int i =0; i< opened; i++) {
if (serialNumber == dev.getserial(i))
m_deviceNo = i;
else
dev.close(i);
}
// Now things are opened or not (from read thread) allow the constructor to complete
m_startedMutex->unlock();
//didn't find the device we are trying to open (shouldnt happen)
device_open = opened >= 0;
if (opened < 0)
{
return false;
}
m_writeThread->start(); m_writeThread->start();
return true;
}
/**
* @brief RawHID::closeDevice This method closes the USB connection
* It is uses as a callback from the read thread so that the USB
* system code is unregistered from that thread\
*/
bool RawHID::closeDevice() {
dev.close(m_deviceNo);
} }
RawHID::~RawHID() RawHID::~RawHID()
{ {
dev.close(m_deviceNo); // If the read thread exists then the device is open
if (m_readThread)
if (m_readThread) close();
delete m_readThread;
if (m_writeThread)
delete m_writeThread;
} }
void RawHID::onDeviceUnplugged(int num) void RawHID::onDeviceUnplugged(int num)
@ -356,7 +369,6 @@ void RawHID::onDeviceUnplugged(int num)
return; return;
// the USB device has been unplugged // the USB device has been unplugged
close(); close();
} }
@ -369,43 +381,38 @@ bool RawHID::open(OpenMode mode)
QIODevice::open(mode); QIODevice::open(mode);
if (!m_readThread) Q_ASSERT(m_readThread);
m_readThread = new RawHIDReadThread(this); Q_ASSERT(m_writeThread);
if (m_readThread) m_readThread->start();
if (!m_writeThread) if (m_writeThread) m_writeThread->start();
m_writeThread = new RawHIDWriteThread(this);
if (m_readThread) m_readThread->start(); // Pip
if (m_writeThread) m_writeThread->start(); // Pip
return true; return true;
} }
void RawHID::close() void RawHID::close()
{ {
emit aboutToClose(); qDebug() << "RawHID::close()";
emit aboutToClose();
if (m_writeThread)
{
qDebug() << "About to terminate write thread";
m_writeThread->terminate();
delete m_writeThread;
m_writeThread = NULL;
qDebug() << "Write thread terminated";
}
m_mutex->lock();
if (m_readThread) if (m_readThread)
{ {
m_readThread->terminate(); qDebug() << "About to terminate read thread";
delete m_readThread; // calls wait m_readThread->terminate();
m_readThread = NULL; delete m_readThread; // calls wait
} m_readThread = NULL;
qDebug() << "Read thread terminated";
}
if (m_writeThread) emit closed();
{
m_writeThread->terminate();
delete m_writeThread;
m_writeThread = NULL;
}
dev.close(m_deviceNo);
m_mutex->unlock();
emit closed();
QIODevice::close(); QIODevice::close();
} }

View File

@ -74,15 +74,23 @@ protected:
virtual qint64 bytesAvailable() const; virtual qint64 bytesAvailable() const;
virtual qint64 bytesToWrite() const; virtual qint64 bytesToWrite() const;
//! Callback from the read thread to open the device
bool openDevice();
//! Callback from teh read thread to close the device
bool closeDevice();
QString serialNumber; QString serialNumber;
int m_deviceNo; int m_deviceNo;
pjrc_rawhid dev; pjrc_rawhid dev;
bool device_open;
RawHIDReadThread *m_readThread; RawHIDReadThread *m_readThread;
RawHIDWriteThread *m_writeThread; RawHIDWriteThread *m_writeThread;
QMutex *m_mutex; QMutex *m_mutex;
QMutex *m_startedMutex;
}; };
#endif // RAWHID_H #endif // RAWHID_H

View File

@ -72,6 +72,7 @@ void RawHIDConnection::onDeviceConnected()
*/ */
void RawHIDConnection::onDeviceDisconnected() void RawHIDConnection::onDeviceDisconnected()
{ {
qDebug() << "onDeviceDisconnected()";
if (enablePolling) if (enablePolling)
emit availableDevChanged(this); emit availableDevChanged(this);
} }
@ -110,15 +111,13 @@ QIODevice *RawHIDConnection::openDevice(const QString &deviceName)
void RawHIDConnection::closeDevice(const QString &deviceName) void RawHIDConnection::closeDevice(const QString &deviceName)
{ {
Q_UNUSED(deviceName); Q_UNUSED(deviceName);
//added by andrew...
if (RawHidHandle) if (RawHidHandle)
{ {
qDebug() << "Closing the device here";
RawHidHandle->close(); RawHidHandle->close();
delete RawHidHandle; delete RawHidHandle;
RawHidHandle = NULL; RawHidHandle = NULL;
} }
//end added by andrew
} }
QString RawHIDConnection::connectionName() QString RawHIDConnection::connectionName()

View File

@ -31,7 +31,6 @@
#include "rawhid_global.h" #include "rawhid_global.h"
#include "rawhid.h" #include "rawhid.h"
#include "usbmonitor.h" #include "usbmonitor.h"
#include "usbsignalfilter.h"
#include "coreplugin/iconnection.h" #include "coreplugin/iconnection.h"
#include <extensionsystem/iplugin.h> #include <extensionsystem/iplugin.h>

View File

@ -87,11 +87,13 @@ DFUObject::DFUObject(bool _debug,bool _use_serial,QString portname):
m_eventloop.exec(); m_eventloop.exec();
QList<USBPortInfo> devices; QList<USBPortInfo> devices;
devices = USBMonitor::instance()->availableDevices(0x20a0,-1,-1,USBMonitor::Bootloader); devices = USBMonitor::instance()->availableDevices(0x20a0,-1,-1,USBMonitor::Bootloader);
if (devices.length()==1 && hidHandle.open(1,devices.first().vendorID,devices.first().productID,0,0)==1) { if (devices.length()==1) {
qDebug()<<"OP_DFU detected first time"; if (hidHandle.open(1,devices.first().vendorID,devices.first().productID,0,0)==1) {
mready=true; mready=true;
QTimer::singleShot(200,&m_eventloop, SLOT(quit())); QTimer::singleShot(200,&m_eventloop, SLOT(quit()));
m_eventloop.exec(); m_eventloop.exec();
} else
hidHandle.close(0);
} else { } else {
// Wait for the board to appear on the USB bus: // Wait for the board to appear on the USB bus:
USBSignalFilter filter(0x20a0,-1,-1,USBMonitor::Bootloader); USBSignalFilter filter(0x20a0,-1,-1,USBMonitor::Bootloader);
@ -106,17 +108,20 @@ DFUObject::DFUObject(bool _debug,bool _use_serial,QString portname):
QTimer::singleShot(2000,&m_eventloop, SLOT(quit())); QTimer::singleShot(2000,&m_eventloop, SLOT(quit()));
m_eventloop.exec(); m_eventloop.exec();
devices = USBMonitor::instance()->availableDevices(0x20a0,-1,-1,USBMonitor::Bootloader); devices = USBMonitor::instance()->availableDevices(0x20a0,-1,-1,USBMonitor::Bootloader);
qDebug() << "Devices length: " << devices.length();
if (devices.length()==1) { if (devices.length()==1) {
if(hidHandle.open(1,devices.first().vendorID,devices.first().productID,0,0)==1) qDebug() << "Opening device";
if(hidHandle.open(1,devices.first().vendorID,devices.first().productID,0,0)==1)
{ {
QTimer::singleShot(200,&m_eventloop, SLOT(quit())); QTimer::singleShot(200,&m_eventloop, SLOT(quit()));
m_eventloop.exec(); m_eventloop.exec();
qDebug()<<"OP_DFU detected after delay"; qDebug()<<"OP_DFU detected after delay";
mready=true; mready=true;
qDebug() << "Detected";
break; break;
} } else
} hidHandle.close(0);
else { } else {
qDebug() << devices.length() << " device(s) detected, don't know what to do!"; qDebug() << devices.length() << " device(s) detected, don't know what to do!";
mready = false; mready = false;
} }
@ -171,7 +176,6 @@ bool DFUObject::enterDFU(int const &devNumber)
buf[9] = 1; //DFU Data3 buf[9] = 1; //DFU Data3
int result = sendData(buf, BUF_LEN); int result = sendData(buf, BUF_LEN);
// int result = hidHandle.send(0,buf, BUF_LEN, 500);
if(result<1) if(result<1)
return false; return false;
if(debug) if(debug)
@ -216,7 +220,6 @@ bool DFUObject::StartUpload(qint32 const & numberOfBytes, TransferTypes const &
int result = sendData(buf, BUF_LEN); int result = sendData(buf, BUF_LEN);
delay::msleep(1000); delay::msleep(1000);
// int result = hidHandle.send(0,buf, BUF_LEN, 5000);
if(debug) if(debug)
qDebug() << result << " bytes sent"; qDebug() << result << " bytes sent";
@ -441,7 +444,6 @@ bool DFUObject::StartDownloadT(QByteArray *fw, qint32 const & numberOfBytes, Tra
buf[9] = 1; //DFU Data3 buf[9] = 1; //DFU Data3
int result = sendData(buf, BUF_LEN); int result = sendData(buf, BUF_LEN);
//int result = hidHandle.send(0,buf, BUF_LEN, 500);
if(debug) if(debug)
qDebug() << "StartDownload:"<<numberOfPackets<<"packets"<<" Last Packet Size="<<lastPacketCount<<" "<<result << " bytes sent"; qDebug() << "StartDownload:"<<numberOfPackets<<"packets"<<" Last Packet Size="<<lastPacketCount<<" "<<result << " bytes sent";
float percentage; float percentage;
@ -457,7 +459,6 @@ bool DFUObject::StartDownloadT(QByteArray *fw, qint32 const & numberOfBytes, Tra
laspercentage=(int)percentage; laspercentage=(int)percentage;
result = receiveData(buf,BUF_LEN); result = receiveData(buf,BUF_LEN);
//result = hidHandle.receive(0,buf,BUF_LEN,5000);
if(debug) if(debug)
qDebug() << result << " bytes received"<<" Count="<<x<<"-"<<(int)buf[2]<<";"<<(int)buf[3]<<";"<<(int)buf[4]<<";"<<(int)buf[5]<<" Data="<<(int)buf[6]<<";"<<(int)buf[7]<<";"<<(int)buf[8]<<";"<<(int)buf[9]; qDebug() << result << " bytes received"<<" Count="<<x<<"-"<<(int)buf[2]<<";"<<(int)buf[3]<<";"<<(int)buf[4]<<";"<<(int)buf[5]<<" Data="<<(int)buf[6]<<";"<<(int)buf[7]<<";"<<(int)buf[8]<<";"<<(int)buf[9];
if(x==numberOfPackets-1) if(x==numberOfPackets-1)
@ -508,7 +509,6 @@ int DFUObject::AbortOperation(void)
buf[9] = 0; buf[9] = 0;
return sendData(buf, BUF_LEN); return sendData(buf, BUF_LEN);
//return hidHandle.send(0,buf, BUF_LEN, 500);
} }
/** /**
@ -538,7 +538,6 @@ int DFUObject::JumpToApp(bool safeboot)
} }
return sendData(buf, BUF_LEN); return sendData(buf, BUF_LEN);
//return hidHandle.send(0,buf, BUF_LEN, 500);
} }
OP_DFU::Status DFUObject::StatusRequest() OP_DFU::Status DFUObject::StatusRequest()
@ -556,11 +555,9 @@ OP_DFU::Status DFUObject::StatusRequest()
buf[9] = 0; buf[9] = 0;
int result = sendData(buf, BUF_LEN); int result = sendData(buf, BUF_LEN);
//int result = hidHandle.send(0,buf, BUF_LEN, 10000);
if(debug) if(debug)
qDebug() << "StatusRequest: " << result << " bytes sent"; qDebug() << "StatusRequest: " << result << " bytes sent";
result = receiveData(buf,BUF_LEN); result = receiveData(buf,BUF_LEN);
// result = hidHandle.receive(0,buf,BUF_LEN,10000);
if(debug) if(debug)
qDebug() << "StatusRequest: " << result << " bytes received"; qDebug() << "StatusRequest: " << result << " bytes received";
if(buf[1]==OP_DFU::Status_Rep) if(buf[1]==OP_DFU::Status_Rep)
@ -590,22 +587,17 @@ bool DFUObject::findDevices()
buf[9] = 0; buf[9] = 0;
int result = sendData(buf, BUF_LEN); int result = sendData(buf, BUF_LEN);
//int result = hidHandle.send(0,buf, BUF_LEN, 5000); if (result < 1)
if(result<1)
{
return false; return false;
}
result = receiveData(buf,BUF_LEN); result = receiveData(buf,BUF_LEN);
//result = hidHandle.receive(0,buf,BUF_LEN,5000); if (result < 1)
if(result<1)
{
return false; return false;
}
numberOfDevices=buf[7]; numberOfDevices=buf[7];
RWFlags=buf[8]; RWFlags=buf[8];
RWFlags=RWFlags<<8 | buf[9]; RWFlags=RWFlags<<8 | buf[9];
if(buf[1]==OP_DFU::Rep_Capabilities) if(buf[1]==OP_DFU::Rep_Capabilities)
{ {
for(int x=0;x<numberOfDevices;++x) for(int x=0;x<numberOfDevices;++x)
@ -626,9 +618,6 @@ bool DFUObject::findDevices()
buf[9] = 0; buf[9] = 0;
int result = sendData(buf, BUF_LEN); int result = sendData(buf, BUF_LEN);
result = receiveData(buf,BUF_LEN); result = receiveData(buf,BUF_LEN);
// int result = hidHandle.send(0,buf, BUF_LEN, 5000);
// result = hidHandle.receive(0,buf,BUF_LEN,5000);
//devices[x].ID=buf[9];
devices[x].ID=buf[14]; devices[x].ID=buf[14];
devices[x].ID=devices[x].ID<<8 | (quint8)buf[15]; devices[x].ID=devices[x].ID<<8 | (quint8)buf[15];
devices[x].BL_Version=buf[7]; devices[x].BL_Version=buf[7];
@ -684,8 +673,6 @@ bool DFUObject::EndOperation()
buf[9] = 0; buf[9] = 0;
int result = sendData(buf, BUF_LEN); int result = sendData(buf, BUF_LEN);
// int result = hidHandle.send(0,buf, BUF_LEN, 5000);
// hidHandle.receive(0,buf,BUF_LEN,5000);
if(debug) if(debug)
qDebug() << result << " bytes sent"; qDebug() << result << " bytes sent";
if(result>0) if(result>0)

View File

@ -249,8 +249,8 @@ void UploaderGadgetWidget::goToBootloader(UAVObject* callerObj, bool success)
// The board is now reset: we have to disconnect telemetry // The board is now reset: we have to disconnect telemetry
Core::ConnectionManager *cm = Core::ICore::instance()->connectionManager(); Core::ConnectionManager *cm = Core::ICore::instance()->connectionManager();
QString dli = cm->getCurrentDevice().Name; QString dli = cm->getCurrentDevice().getConName();
QString dlj = cm->getCurrentDevice().devName; QString dlj = cm->getCurrentDevice().getConName();
cm->disconnectDevice(); cm->disconnectDevice();
QTimer::singleShot(200, &m_eventloop, SLOT(quit())); QTimer::singleShot(200, &m_eventloop, SLOT(quit()));
m_eventloop.exec(); m_eventloop.exec();
@ -378,7 +378,8 @@ void UploaderGadgetWidget::systemSafeBoot()
} }
/** /**
Tells the system to boot (from Bootloader state) * Tells the system to boot (from Bootloader state)
* @param[in] safeboot Indicates whether the firmware should use the stock HWSettings
*/ */
void UploaderGadgetWidget::commonSystemBoot(bool safeboot) void UploaderGadgetWidget::commonSystemBoot(bool safeboot)
{ {
@ -456,7 +457,7 @@ void UploaderGadgetWidget::systemRescue()
delete dfu; delete dfu;
dfu = NULL; dfu = NULL;
} }
// Avoid dumb users pressing Rescue twice. It can happen. // Avoid users pressing Rescue twice.
m_config->rescueButton->setEnabled(false); m_config->rescueButton->setEnabled(false);
// Now we're good to go: // Now we're good to go:
@ -523,25 +524,6 @@ void UploaderGadgetWidget::systemRescue()
m_config->rescueButton->setEnabled(true); m_config->rescueButton->setEnabled(true);
return; return;
} }
if ((eBoardCC != dfu->GetBoardType(0)) && (QMessageBox::question(this,tr("OpenPilot Uploader"),tr("If you want to search for other boards connect power now and press Yes"),QMessageBox::Yes,QMessageBox::No)==QMessageBox::Yes))
{
log("\nWaiting...");
QTimer::singleShot(3000, &m_eventloop, SLOT(quit()));
m_eventloop.exec();
log("Detecting second board...");
repaint();
if(!dfu->findDevices())
{
// We will only end up here in case somehow all the boards
// disappeared, including the one we detected earlier.
log("Could not detect any board, aborting!");
delete dfu;
dfu = NULL;
cm->resumePolling();
m_config->rescueButton->setEnabled(true);
return;
}
}
log(QString("Found ") + QString::number(dfu->numberOfDevices) + QString(" device(s).")); log(QString("Found ") + QString::number(dfu->numberOfDevices) + QString(" device(s)."));
if (dfu->numberOfDevices > 5) { if (dfu->numberOfDevices > 5) {
log("Inconsistent number of devices, aborting!"); log("Inconsistent number of devices, aborting!");
@ -566,6 +548,7 @@ void UploaderGadgetWidget::systemRescue()
m_config->rescueButton->setEnabled(false); m_config->rescueButton->setEnabled(false);
currentStep = IAP_STATE_BOOTLOADER; // So that we can boot from the GUI afterwards. currentStep = IAP_STATE_BOOTLOADER; // So that we can boot from the GUI afterwards.
} }
void UploaderGadgetWidget::perform() void UploaderGadgetWidget::perform()
{ {
if(m_progress->value()==19) if(m_progress->value()==19)