diff --git a/ground/src/plugins/coreplugin/connectionmanager.cpp b/ground/src/plugins/coreplugin/connectionmanager.cpp new file mode 100644 index 000000000..14f58b6c2 --- /dev/null +++ b/ground/src/plugins/coreplugin/connectionmanager.cpp @@ -0,0 +1,224 @@ +/** + ****************************************************************************** + * + * @file connectionmanager.cpp + * @author The OpenPilot Team, http://www.openpilot.org Copyright (C) 2010. + * Parts by Nokia Corporation (qt-info@nokia.com) Copyright (C) 2009. + * @brief + * @see The GNU Public License (GPL) Version 3 + * @defgroup coreplugin + * @{ + * + *****************************************************************************/ +/* + * 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 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "connectionmanager.h" + +#include +#include +#include + +#include + +#include "fancytabwidget.h" +#include "fancyactionbar.h" +#include "mainwindow.h" + +#include +#include +#include +#include + +namespace Core { + + +ConnectionManager::ConnectionManager(Internal::MainWindow *mainWindow, Internal::FancyTabWidget *modeStack) + : m_availableDevList(0), + m_connectBtn(0), + m_deviceConnected(false) +{ + QVBoxLayout *top = new QVBoxLayout; + top->setSpacing(0); + top->setMargin(0); + + QHBoxLayout *layout = new QHBoxLayout; + layout->setSpacing(0); + layout->setContentsMargins(5,0,5,0); + layout->addWidget(new QLabel("Connections: ")); + + m_availableDevList = new QComboBox; + //m_availableDevList->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); + m_availableDevList->setMinimumWidth(100); + m_availableDevList->setMaximumWidth(150); + m_availableDevList->setContextMenuPolicy(Qt::CustomContextMenu); + layout->addWidget(m_availableDevList); + + m_connectBtn = new QPushButton("Connect"); + m_connectBtn->setEnabled(false); + layout->addWidget(m_connectBtn); + + Utils::StyledBar *bar = new Utils::StyledBar; + bar->setLayout(layout); + + top->addWidget(bar); + setLayout(top); + + modeStack->insertCornerWidget(modeStack->cornerWidgetCount()-1, this); + + QObject::connect(m_connectBtn, SIGNAL(pressed()), + this, SLOT(onConnectPressed())); +} + +ConnectionManager::~ConnectionManager() +{ +} + +void ConnectionManager::init() +{ + //register to the plugin manager so we can receive + //new connection object from plugins + QObject::connect(ExtensionSystem::PluginManager::instance(), SIGNAL(objectAdded(QObject*)), + this, SLOT(objectAdded(QObject*))); + QObject::connect(ExtensionSystem::PluginManager::instance(), SIGNAL(aboutToRemoveObject(QObject*)), + this, SLOT(aboutToRemoveObject(QObject*))); +} + +void ConnectionManager::objectAdded(QObject *obj) +{ + //Check if a plugin added a connection object to the pool + IConnection *connection = Aggregation::query(obj); + if (!connection) + return; + + //qDebug() << "Connection object registered:" << connection->connectionName(); + //qDebug() << connection->availableDevices(); + + //register devices and populate CB + devChanged(connection); + + QObject::connect(connection, SIGNAL(availableDevChanged(IConnection *)), + this, SLOT(devChanged(IConnection*))); +} + +void ConnectionManager::aboutToRemoveObject(QObject *obj) +{ +} + +void ConnectionManager::onConnectPressed() +{ + if(!m_deviceConnected) + { + m_connectBtn->setText("Disconnect"); + m_availableDevList->setEnabled(false); + + m_deviceConnected = true; + m_connectionDevice = findDevice(m_availableDevList->currentText()); + + if(m_connectionDevice.connection) + { + QIODevice *ioDev = m_connectionDevice.connection->openDevice(m_connectionDevice.devName); + + if(ioDev) + { + emit deviceConnected(ioDev); + return; + } + else + { + m_connectionDevice.connection = NULL; + } + } + } + + //both in case of error and disconnection, we fall back here + m_connectBtn->setText("Connect"); + m_availableDevList->setEnabled(true); + + m_deviceConnected = false; + + if(m_connectionDevice.connection) + m_connectionDevice.connection->closeDevice(m_connectionDevice.devName); + + m_connectionDevice.connection = NULL; + emit deviceDisconnected(); +} + +devListItem ConnectionManager::findDevice(const QString &displayedName) +{ + foreach(devListItem d, m_devList) + { + if(d.displayedName == displayedName) + { + return d; + } + } + qDebug() << "findDevice: cannot find " << displayedName << " in device list"; + devListItem d; + d.connection = NULL; + return d; +} + +void ConnectionManager::unregisterAll(IConnection *connection) +{ + for(QLinkedList::iterator iter = m_devList.begin(); + iter != m_devList.end(); ++iter) + { + if(iter->connection == connection) + { + iter = m_devList.erase(iter); + } + } +} + +void ConnectionManager::registerDevice(IConnection *conn, const QString &devN, const QString &disp) +{ + devListItem d; + d.connection = conn; + d.devName = devN; + d.displayedName = disp; + + m_devList.append(d); +} + +void ConnectionManager::devChanged(IConnection *connection) +{ + //remove all registered devices + m_availableDevList->clear(); + unregisterAll(connection); + + //and add them back in the list + foreach(QString dev, connection->availableDevices()) + { + QString cbName = connection->shortName() + ": " + dev; + registerDevice(connection, dev, cbName); + } + + //add all the list to the combobox + foreach(devListItem d, m_devList) + { + m_availableDevList->addItem(d.displayedName); + } + + //disable connection button if the list is empty + if(m_availableDevList->count() > 0) + m_connectBtn->setEnabled(true); + else + m_connectBtn->setEnabled(false); +} + + +} //namespace Core diff --git a/ground/src/plugins/coreplugin/connectionmanager.h b/ground/src/plugins/coreplugin/connectionmanager.h new file mode 100644 index 000000000..cd0c40011 --- /dev/null +++ b/ground/src/plugins/coreplugin/connectionmanager.h @@ -0,0 +1,99 @@ +/** + ****************************************************************************** + * + * @file connectionmanager.h + * @author The OpenPilot Team, http://www.openpilot.org Copyright (C) 2010. + * Parts by Nokia Corporation (qt-info@nokia.com) Copyright (C) 2009. + * @brief + * @see The GNU Public License (GPL) Version 3 + * @defgroup coreplugin + * @{ + * + *****************************************************************************/ +/* + * 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 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef CONNECTIONMANAGER_H +#define CONNECTIONMANAGER_H + +#include + +#include +#include +#include +#include +#include + +#include "core_global.h" + +namespace Core { + + class IConnection; + +namespace Internal { + class FancyTabWidget; + class FancyActionBar; + class MainWindow; +} // namespace Internal + + +struct devListItem +{ + IConnection *connection; + QString devName; + QString displayedName; +}; + + +class CORE_EXPORT ConnectionManager : public QWidget +{ + Q_OBJECT + +public: + ConnectionManager(Internal::MainWindow *mainWindow, Internal::FancyTabWidget *modeStack); + virtual ~ConnectionManager(); + + void init(); + + QIODevice *getCurrentConnection(); + +protected: + void unregisterAll(IConnection *connection); + void registerDevice(IConnection *conn, const QString &devN, const QString &disp); + devListItem findDevice(const QString &displayedName); + +signals: + void deviceConnected(QIODevice *dev); + void deviceDisconnected(); + +private slots: + void objectAdded(QObject *obj); + void aboutToRemoveObject(QObject *obj); + + void onConnectPressed(); + void devChanged(IConnection *connection); + +protected: + QComboBox *m_availableDevList; + QPushButton *m_connectBtn; + QLinkedList m_devList; + devListItem m_connectionDevice; + bool m_deviceConnected; +}; + +} //namespace Core + +#endif // CONNECTIONMANAGER_H diff --git a/ground/src/plugins/coreplugin/coreimpl.cpp b/ground/src/plugins/coreplugin/coreimpl.cpp index 87584c1d3..8e578edaf 100644 --- a/ground/src/plugins/coreplugin/coreimpl.cpp +++ b/ground/src/plugins/coreplugin/coreimpl.cpp @@ -87,6 +87,11 @@ MessageManager *CoreImpl::messageManager() const return m_mainwindow->messageManager(); } +ConnectionManager *CoreImpl::connectionManager() const +{ + return m_mainwindow->connectionManager(); +} + //UAVGadgetManager *CoreImpl::uavGadgetManager() const //{ // return m_mainwindow->uavGadgetManager(); diff --git a/ground/src/plugins/coreplugin/coreimpl.h b/ground/src/plugins/coreplugin/coreimpl.h index b1c0f3a75..54a4f3f04 100644 --- a/ground/src/plugins/coreplugin/coreimpl.h +++ b/ground/src/plugins/coreplugin/coreimpl.h @@ -55,6 +55,7 @@ public: ActionManager *actionManager() const; UniqueIDManager *uniqueIDManager() const; MessageManager *messageManager() const; + ConnectionManager *connectionManager() const; // UAVGadgetManager *uavGadgetManager() const; VariableManager *variableManager() const; ModeManager *modeManager() const; diff --git a/ground/src/plugins/coreplugin/coreplugin.pro b/ground/src/plugins/coreplugin/coreplugin.pro index 3de21b4a5..9b9052731 100644 --- a/ground/src/plugins/coreplugin/coreplugin.pro +++ b/ground/src/plugins/coreplugin/coreplugin.pro @@ -54,7 +54,9 @@ SOURCES += mainwindow.cpp \ dialogs/ioptionspage.cpp \ dialogs/iwizard.cpp \ settingsdatabase.cpp \ - eventfilteringmainwindow.cpp + eventfilteringmainwindow.cpp \ + connectionmanager.cpp \ + iconnection.cpp HEADERS += mainwindow.h \ tabpositionindicator.h \ fancyactionbar.h \ @@ -108,7 +110,9 @@ HEADERS += mainwindow.h \ sidebar.h \ mimedatabase.h \ settingsdatabase.h \ - eventfilteringmainwindow.h + eventfilteringmainwindow.h \ + connectionmanager.h \ + iconnection.h FORMS += dialogs/settingsdialog.ui \ dialogs/shortcutsettings.ui \ generalsettings.ui diff --git a/ground/src/plugins/coreplugin/fancytabwidget.cpp b/ground/src/plugins/coreplugin/fancytabwidget.cpp index 0627475c1..1f0958471 100644 --- a/ground/src/plugins/coreplugin/fancytabwidget.cpp +++ b/ground/src/plugins/coreplugin/fancytabwidget.cpp @@ -305,11 +305,11 @@ FancyTabWidget::FancyTabWidget(QWidget *parent) selectionLayout->setMargin(0); Utils::StyledBar *bar = new Utils::StyledBar; - QHBoxLayout *layout = new QHBoxLayout(bar); + QVBoxLayout *layout = new QVBoxLayout(bar); layout->setMargin(0); layout->setSpacing(0); layout->addWidget(new FancyColorButton(this)); - //selectionLayout->addWidget(bar); + selectionLayout->addWidget(bar); selectionLayout->addWidget(m_tabBar, 1); m_selectionWidget->setLayout(selectionLayout); @@ -319,13 +319,13 @@ FancyTabWidget::FancyTabWidget(QWidget *parent) m_cornerWidgetContainer->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Minimum); m_cornerWidgetContainer->setAutoFillBackground(false); - QVBoxLayout *cornerWidgetLayout = new QVBoxLayout; + QHBoxLayout *cornerWidgetLayout = new QHBoxLayout; cornerWidgetLayout->setSpacing(0); cornerWidgetLayout->setMargin(0); cornerWidgetLayout->addStretch(); m_cornerWidgetContainer->setLayout(cornerWidgetLayout); - //selectionLayout->addWidget(m_cornerWidgetContainer, 0); + selectionLayout->addWidget(m_cornerWidgetContainer, 0); m_modesStack = new QStackedLayout; m_statusBar = new QStatusBar; @@ -383,7 +383,7 @@ void FancyTabWidget::paintEvent(QPaintEvent *event) void FancyTabWidget::insertCornerWidget(int pos, QWidget *widget) { - QVBoxLayout *layout = static_cast(m_cornerWidgetContainer->layout()); + QHBoxLayout *layout = static_cast(m_cornerWidgetContainer->layout()); layout->insertWidget(pos, widget); } diff --git a/ground/src/plugins/coreplugin/iconnection.cpp b/ground/src/plugins/coreplugin/iconnection.cpp new file mode 100644 index 000000000..eb10b78d7 --- /dev/null +++ b/ground/src/plugins/coreplugin/iconnection.cpp @@ -0,0 +1,30 @@ +/** + ****************************************************************************** + * + * @file iconnection.cpp + * @author The OpenPilot Team, http://www.openpilot.org Copyright (C) 2010. + * Parts by Nokia Corporation (qt-info@nokia.com) Copyright (C) 2009. + * @brief + * @see The GNU Public License (GPL) Version 3 + * @defgroup coreplugin + * @{ + * + *****************************************************************************/ +/* + * 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 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "iconnection.h" + diff --git a/ground/src/plugins/coreplugin/iconnection.h b/ground/src/plugins/coreplugin/iconnection.h new file mode 100644 index 000000000..6d72018d1 --- /dev/null +++ b/ground/src/plugins/coreplugin/iconnection.h @@ -0,0 +1,58 @@ +/** + ****************************************************************************** + * + * @file iconnection.h + * @author The OpenPilot Team, http://www.openpilot.org Copyright (C) 2010. + * Parts by Nokia Corporation (qt-info@nokia.com) Copyright (C) 2009. + * @brief + * @see The GNU Public License (GPL) Version 3 + * @defgroup coreplugin + * @{ + * + *****************************************************************************/ +/* + * 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 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef ICONNECTION_H +#define ICONNECTION_H + +#include +#include +#include + +#include "core_global.h" + +namespace Core { + +class CORE_EXPORT IConnection : public QObject +{ + Q_OBJECT + +public: + virtual QStringList availableDevices() = 0; + virtual QIODevice *openDevice(const QString &deviceName) = 0; + virtual void closeDevice(const QString &deviceName) {}; + + virtual QString connectionName() = 0; + virtual QString shortName() {return connectionName();} + +signals: + void availableDevChanged(IConnection *); +}; + +} //namespace Core + +#endif // ICONNECTION_H diff --git a/ground/src/plugins/coreplugin/icore.h b/ground/src/plugins/coreplugin/icore.h index cd26a3a52..8aaa4b693 100644 --- a/ground/src/plugins/coreplugin/icore.h +++ b/ground/src/plugins/coreplugin/icore.h @@ -43,6 +43,7 @@ namespace Core { class ActionManager; class IContext; class IWizard; +class ConnectionManager; class MessageManager; class MimeDatabase; class ModeManager; @@ -76,6 +77,7 @@ public: virtual MessageManager *messageManager() const = 0; virtual VariableManager *variableManager() const = 0; virtual ModeManager *modeManager() const = 0; + virtual ConnectionManager *connectionManager() const = 0; // virtual UAVGadgetManager *uavGadgetManager() const = 0; virtual MimeDatabase *mimeDatabase() const = 0; diff --git a/ground/src/plugins/coreplugin/mainwindow.cpp b/ground/src/plugins/coreplugin/mainwindow.cpp index 0a6767071..aa5d2b5a7 100644 --- a/ground/src/plugins/coreplugin/mainwindow.cpp +++ b/ground/src/plugins/coreplugin/mainwindow.cpp @@ -42,6 +42,7 @@ #include "plugindialog.h" #include "shortcutsettings.h" #include "uavgadgetmanager.h" +#include "connectionmanager.h" #include "settingsdialog.h" #include "variablemanager.h" @@ -113,6 +114,7 @@ MainWindow::MainWindow() : m_viewManager(0), m_modeManager(0), m_uavGadgetManager(0), + m_connectionManager(0), m_mimeDatabase(new MimeDatabase), // m_rightPaneWidget(0), m_versionDialog(0), @@ -163,11 +165,11 @@ MainWindow::MainWindow() : registerDefaultContainers(); registerDefaultActions(); -// m_rightPaneWidget = new RightPaneWidget(); - m_modeStack = new FancyTabWidget(this); m_modeManager = new ModeManager(this, m_modeStack); - //m_modeManager->addWidget(m_progressManager->progressView()); + + m_connectionManager = new ConnectionManager(this, m_modeStack); + m_viewManager = new ViewManager(this); m_messageManager = new MessageManager; // m_uavGadgetManager = new UAVGadgetManager(m_coreImpl, this); @@ -227,6 +229,7 @@ bool MainWindow::init(QString *errorMessage) pm->addObject(m_coreImpl); m_viewManager->init(); m_modeManager->init(); + m_connectionManager->init(); pm->addObject(m_generalSettings); pm->addObject(m_shortcutSettings); @@ -725,6 +728,12 @@ VariableManager *MainWindow::variableManager() const return m_variableManager; } + +ConnectionManager *MainWindow::connectionManager() const +{ + return m_connectionManager; +} + UAVGadgetManager *MainWindow::uavGadgetManager() const { return m_uavGadgetManager; diff --git a/ground/src/plugins/coreplugin/mainwindow.h b/ground/src/plugins/coreplugin/mainwindow.h index fcaa68dd7..0512b81a8 100644 --- a/ground/src/plugins/coreplugin/mainwindow.h +++ b/ground/src/plugins/coreplugin/mainwindow.h @@ -47,7 +47,9 @@ class ActionManager; class BaseMode; class BaseView; class IContext; +class IMode; class IWizard; +class ConnectionManager; class MessageManager; class MimeDatabase; class ModeManager; @@ -56,7 +58,6 @@ class SettingsDatabase; class UniqueIDManager; class VariableManager; class ViewManagerInterface; -class IMode; class UAVGadgetManager; namespace Internal { @@ -92,6 +93,7 @@ public: Core::UniqueIDManager *uniqueIDManager() const; Core::MessageManager *messageManager() const; Core::UAVGadgetManager *uavGadgetManager() const; + Core::ConnectionManager *connectionManager() const; Core::VariableManager *variableManager() const; Core::ModeManager *modeManager() const; Core::MimeDatabase *mimeDatabase() const; @@ -165,6 +167,7 @@ private: ViewManager *m_viewManager; ModeManager *m_modeManager; UAVGadgetManager *m_uavGadgetManager; + ConnectionManager *m_connectionManager; MimeDatabase *m_mimeDatabase; FancyTabWidget *m_modeStack; // RightPaneWidget *m_rightPaneWidget; diff --git a/ground/src/plugins/plugins.pro b/ground/src/plugins/plugins.pro index bc7c4bb04..4fbeab02c 100644 --- a/ground/src/plugins/plugins.pro +++ b/ground/src/plugins/plugins.pro @@ -3,8 +3,7 @@ TEMPLATE = subdirs -SUBDIRS = plugin_coreplugin \ - plugin_welcome \ +SUBDIRS = plugin_coreplugin # Blank Template Plugin, not compiled by default #SUBDIRS += plugin_donothing @@ -17,6 +16,7 @@ plugin_coreplugin.subdir = coreplugin # Welcome Plugin plugin_welcome.subdir = welcome plugin_welcome.depends = plugin_coreplugin +SUBDIRS += plugin_welcome # RawHID plug-in SUBDIRS += plugin_rawhid @@ -40,13 +40,13 @@ plugin_emptygadget.depends = plugin_coreplugin SUBDIRS += plugin_emptygadget # Map UAVGadget -#plugin_map.subdir = map -#plugin_map.depends = plugin_coreplugin -#SUBDIRS += plugin_map +plugin_map.subdir = map +plugin_map.depends = plugin_coreplugin +SUBDIRS += plugin_map # Scope UAVGadget -#plugin_scope.subdir = scope -#plugin_scope.depends = plugin_coreplugin -#SUBDIRS += plugin_scope +plugin_scope.subdir = scope +plugin_scope.depends = plugin_coreplugin +SUBDIRS += plugin_scope diff --git a/ground/src/plugins/rawhid/pjrc_rawhid.h b/ground/src/plugins/rawhid/pjrc_rawhid.h new file mode 100644 index 000000000..155d1a82b --- /dev/null +++ b/ground/src/plugins/rawhid/pjrc_rawhid.h @@ -0,0 +1,23 @@ +#ifndef PJRC_RAWHID_H +#define PJRC_RAWHID_H + +#include +#include +#include +#include + +class pjrc_rawhid +{ +public: + pjrc_rawhid(); + int open(int max, int vid, int pid, int usage_page, int usage); + int receive(int num, void *buf, int len, int timeout); + void close(int num); + int send(int num, void *buf, int len, int timeout); + int getserial(int num, char *buf); + +private: + +}; + +#endif // PJRC_RAWHID_H diff --git a/ground/src/plugins/rawhid/pjrc_rawhid_mac.cpp b/ground/src/plugins/rawhid/pjrc_rawhid_mac.cpp new file mode 100644 index 000000000..143382184 --- /dev/null +++ b/ground/src/plugins/rawhid/pjrc_rawhid_mac.cpp @@ -0,0 +1,423 @@ +/* Simple Raw HID functions for Linux - for use with Teensy RawHID example + * http://www.pjrc.com/teensy/rawhid.html + * Copyright (c) 2009 PJRC.COM, LLC + * + * rawhid_open - open 1 or more devices + * rawhid_recv - receive a packet + * rawhid_send - send a packet + * rawhid_close - close a device + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above description, website URL and copyright notice and this permission + * notice shall be included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + * Version 1.0: Initial Release + */ + +#include "pjrc_rawhid.h" + +#include +#include +#include +#include + +#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; +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 timeout_callback(CFRunLoopTimerRef, void *); +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); + +pjrc_rawhid::pjrc_rawhid() +{ + first_hid = NULL; + last_hid = NULL; +} + +// open - open 1 or more devices +// +// Inputs: +// max = maximum number of devices to open +// vid = Vendor ID, or -1 if any +// pid = Product ID, or -1 if any +// usage_page = top level usage page, or -1 if any +// usage = top level usage number, or -1 if any +// Output: +// actual number of devices opened +// +int pjrc_rawhid::open(int max, int vid, int pid, int usage_page, int usage) +{ + static IOHIDManagerRef hid_manager=NULL; + CFMutableDictionaryRef dict; + CFNumberRef num; + IOReturn ret; + hid_t *p; + int count=0; + + if (first_hid) free_all_hid(); + printf("pjrc_rawhid_open, max=%d\n", max); + if (max < 1) return 0; + // Start the HID Manager + // http://developer.apple.com/technotes/tn2007/tn2187.html + if (!hid_manager) { + hid_manager = IOHIDManagerCreate(kCFAllocatorDefault, kIOHIDOptionsTypeNone); + if (hid_manager == NULL || CFGetTypeID(hid_manager) != IOHIDManagerGetTypeID()) { + if (hid_manager) CFRelease(hid_manager); + return 0; + } + } + if (vid > 0 || pid > 0 || usage_page > 0 || usage > 0) { + // Tell the HID Manager what type of devices we want + dict = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, + &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); + if (!dict) return 0; + if (vid > 0) { + num = CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &vid); + CFDictionarySetValue(dict, CFSTR(kIOHIDVendorIDKey), num); + CFRelease(num); + } + if (pid > 0) { + num = CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &pid); + CFDictionarySetValue(dict, CFSTR(kIOHIDProductIDKey), num); + CFRelease(num); + } + if (usage_page > 0) { + num = CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &usage_page); + CFDictionarySetValue(dict, CFSTR(kIOHIDPrimaryUsagePageKey), num); + CFRelease(num); + } + if (usage > 0) { + num = CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &usage); + CFDictionarySetValue(dict, CFSTR(kIOHIDPrimaryUsageKey), num); + CFRelease(num); + } + IOHIDManagerSetDeviceMatching(hid_manager, dict); + CFRelease(dict); + } else { + IOHIDManagerSetDeviceMatching(hid_manager, NULL); + } + // set up a callbacks for device attach & detach + IOHIDManagerScheduleWithRunLoop(hid_manager, CFRunLoopGetCurrent(), + kCFRunLoopDefaultMode); + IOHIDManagerRegisterDeviceMatchingCallback(hid_manager, attach_callback, NULL); + IOHIDManagerRegisterDeviceRemovalCallback(hid_manager, detach_callback, NULL); + ret = IOHIDManagerOpen(hid_manager, kIOHIDOptionsTypeNone); + if (ret != kIOReturnSuccess) { + IOHIDManagerUnscheduleFromRunLoop(hid_manager, + CFRunLoopGetCurrent(), kCFRunLoopDefaultMode); + CFRelease(hid_manager); + return 0; + } + printf("run loop\n"); + // let it do the callback for all devices + while (CFRunLoopRunInMode(kCFRunLoopDefaultMode, 0, true) == kCFRunLoopRunHandledSource) ; + // count up how many were added by the callback + for (p = first_hid; p; p = p->next) count++; + return count; +} + +// recveive - receive a packet +// Inputs: +// num = device to receive from (zero based) +// buf = buffer to receive packet +// len = buffer's size +// timeout = time to wait, in milliseconds +// Output: +// number of bytes received, or -1 on error +// +int pjrc_rawhid::receive(int num, void *buf, int len, int timeout) +{ + hid_t *hid; + buffer_t *b; + CFRunLoopTimerRef timer=NULL; + CFRunLoopTimerContext context; + int ret=0, timeout_occurred=0; + + if (len < 1) return 0; + hid = get_hid(num); + 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); + while (1) { + CFRunLoopRun(); + 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); + ret = len; + break; + } + if (!hid->open) { + printf("pjrc_rawhid_recv, device not open\n"); + ret = -1; + break; + } + if (timeout_occurred) break; + } + CFRunLoopTimerInvalidate(timer); + 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" + IOReturn ret = IOHIDDeviceSetReport(hid->ref, kIOHIDReportTypeOutput, 0, (uint8_t *)buf, len); + result = (ret == kIOReturnSuccess) ? len : -1; +#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; + } + } +#endif + return result; +} + +int pjrc_rawhid::getserial(int num, char *buf) { + hid_t *hid; + + hid = get_hid(num); + if (!hid || !hid->open) return -1; + + CFTypeRef serialnum = IOHIDDeviceGetProperty(hid->ref, CFSTR(kIOHIDSerialNumberKey)); + if(CFGetTypeID(serialnum) == CFStringGetTypeID()) + { + /* For some reason the first 8 bytes of 'serialnum' are useless (a struct?) */ + char *strptr = (char *)serialnum; + for(int i = 9; i < 33; i++) { + *(buf++) = strptr[i]; + } + return 0; + } + + return -1; +} + +// close - close a device +// +// Inputs: +// num = device to close (zero based) +// Output +// (nothing) +// +void pjrc_rawhid::close(int num) +{ + hid_t *hid; + + hid = get_hid(num); + if (!hid || !hid->open) return; + hid_close(hid); + hid->open = 0; +} + +// +// +// Private Functions +// +// +static void input_callback(void *context, IOReturn ret, void *sender, IOHIDReportType type, uint32_t id, uint8_t *data, CFIndex len) +{ + buffer_t *n; + hid_t *hid; + + printf("input_callback\n"); + if (ret != kIOReturnSuccess || len < 1) return; + hid = (hid_t*)context; + if (!hid || hid->ref != sender) return; + n = (buffer_t *)malloc(sizeof(buffer_t)); + if (!n) return; + if (len > BUFFER_SIZE) len = BUFFER_SIZE; + memcpy(n->buf, data, len); + n->len = len; + n->next = NULL; + if (!hid->first_buffer || !hid->last_buffer) { + hid->first_buffer = hid->last_buffer = n; + } else { + hid->last_buffer->next = n; + hid->last_buffer = n; + } + CFRunLoopStop(CFRunLoopGetCurrent()); +} + +static void timeout_callback(CFRunLoopTimerRef timer, void *info) +{ + printf("timeout_callback\n"); + *(int *)info = 1; + CFRunLoopStop(CFRunLoopGetCurrent()); +} + +static void add_hid(hid_t *h) +{ + if (!first_hid || !last_hid) { + first_hid = last_hid = h; + h->next = h->prev = NULL; + return; + } + last_hid->next = h; + h->prev = last_hid; + h->next = NULL; + last_hid = h; +} + + +static hid_t * get_hid(int num) +{ + hid_t *p; + for (p = first_hid; p && num > 0; p = p->next, num--) ; + return p; +} + + +static void free_all_hid(void) +{ + hid_t *p, *q; + + 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) +{ + if (!hid || !hid->open || !hid->ref) return; + IOHIDDeviceUnscheduleFromRunLoop(hid->ref, CFRunLoopGetCurrent( ), kCFRunLoopDefaultMode); + 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; + h = (hid_t *)malloc(sizeof(hid_t)); + if (!h) return; + memset(h, 0, sizeof(hid_t)); + IOHIDDeviceScheduleWithRunLoop(dev, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode); + IOHIDDeviceRegisterInputReportCallback(dev, h->buffer, sizeof(h->buffer), input_callback, h); + h->ref = dev; + h->open = 1; + add_hid(h); +} + +static void output_callback(hid_t *context, IOReturn ret, void *sender, IOHIDReportType type, uint32_t id, uint8_t *data, CFIndex len) +{ + printf("output_callback, r=%d\n", ret); + if (ret == kIOReturnSuccess) { + *(int *)context = len; + } else { + // timeout if not success? + *(int *)context = 0; + } + CFRunLoopStop(CFRunLoopGetCurrent()); +} + diff --git a/ground/src/plugins/rawhid/pjrc_rawhid_unix.cpp b/ground/src/plugins/rawhid/pjrc_rawhid_unix.cpp new file mode 100644 index 000000000..519ca3625 --- /dev/null +++ b/ground/src/plugins/rawhid/pjrc_rawhid_unix.cpp @@ -0,0 +1,355 @@ +/* Simple Raw HID functions for Linux - for use with Teensy RawHID example + * http://www.pjrc.com/teensy/rawhid.html + * Copyright (c) 2009 PJRC.COM, LLC + * + * rawhid_open - open 1 or more devices + * rawhid_recv - receive a packet + * rawhid_send - send a packet + * rawhid_close - close a device + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above description, website URL and copyright notice and this permission + * notice shall be included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + * Version 1.0: Initial Release + */ + +#include "pjrc_rawhid.h" +#include +#include + +typedef struct hid_struct hid_t; +static hid_t *first_hid; +static hid_t *last_hid; +struct hid_struct { + usb_dev_handle *usb; + int open; + int iface; + int ep_in; + int ep_out; + struct hid_struct *prev; + struct hid_struct *next; +}; + +static void add_hid(hid_t *h); +static hid_t * get_hid(int num); +static void free_all_hid(void); +static void hid_close(hid_t *hid); +static int hid_parse_item(uint32_t *val, uint8_t **data, const uint8_t *end); + +#define printf qDebug + +pjrc_rawhid::pjrc_rawhid() +{ + first_hid = NULL; + last_hid = NULL; +} + +// open - open 1 or more devices +// +// Inputs: +// max = maximum number of devices to open +// vid = Vendor ID, or -1 if any +// pid = Product ID, or -1 if any +// usage_page = top level usage page, or -1 if any +// usage = top level usage number, or -1 if any +// Output: +// actual number of devices opened +// +int pjrc_rawhid::open(int max, int vid, int pid, int usage_page, int usage) +{ + struct usb_bus *bus; + struct usb_device *dev; + struct usb_interface *iface; + struct usb_interface_descriptor *desc; + struct usb_endpoint_descriptor *ep; + usb_dev_handle *u; + uint8_t buf[1024], *p; + int i, n, len, tag, ep_in, ep_out, count=0, claimed; + uint32_t val=0, parsed_usage, parsed_usage_page; + hid_t *hid; + + if (first_hid) free_all_hid(); + printf("pjrc_rawhid_open, max=%d\n", max); + if (max < 1) return 0; + usb_init(); + usb_find_busses(); + usb_find_devices(); + for (bus = usb_get_busses(); bus; bus = bus->next) { + for (dev = bus->devices; dev; dev = dev->next) { + if (vid > 0 && dev->descriptor.idVendor != vid) continue; + if (pid > 0 && dev->descriptor.idProduct != pid) continue; + if (!dev->config) continue; + if (dev->config->bNumInterfaces < 1) continue; + printf("device: vid=%04X, pic=%04X, with %d iface\n", + dev->descriptor.idVendor, + dev->descriptor.idProduct, + dev->config->bNumInterfaces); + iface = dev->config->interface; + u = NULL; + claimed = 0; + for (i=0; iconfig->bNumInterfaces && iface; i++, iface++) { + desc = iface->altsetting; + if (!desc) continue; + printf(" type %d, %d, %d\n", desc->bInterfaceClass, + desc->bInterfaceSubClass, desc->bInterfaceProtocol); + if (desc->bInterfaceClass != 3) continue; + if (desc->bInterfaceSubClass != 0) continue; + if (desc->bInterfaceProtocol != 0) continue; + ep = desc->endpoint; + ep_in = ep_out = 0; + for (n = 0; n < desc->bNumEndpoints; n++, ep++) { + if (ep->bEndpointAddress & 0x80) { + if (!ep_in) ep_in = ep->bEndpointAddress & 0x7F; + printf(" IN endpoint %d\n", ep_in); + } else { + if (!ep_out) ep_out = ep->bEndpointAddress; + printf(" OUT endpoint %d\n", ep_out); + } + } + if (!ep_in) continue; + if (!u) { + u = usb_open(dev); + if (!u) { + printf(" unable to open device\n"); + break; + } + } + printf(" hid interface (generic)\n"); + if (usb_get_driver_np(u, i, (char *)buf, sizeof(buf)) >= 0) { + printf(" in use by driver \"%s\"\n", buf); + if (usb_detach_kernel_driver_np(u, i) < 0) { + printf(" unable to detach from kernel\n"); + continue; + } + } + if (usb_claim_interface(u, i) < 0) { + printf(" unable claim interface %d\n", i); + continue; + } + len = usb_control_msg(u, 0x81, 6, 0x2200, i, (char *)buf, sizeof(buf), 250); + printf(" descriptor, len=%d\n", len); + if (len < 2) { + usb_release_interface(u, i); + continue; + } + p = buf; + parsed_usage_page = parsed_usage = 0; + while ((tag = hid_parse_item(&val, &p, buf + len)) >= 0) { + printf(" tag: %X, val %X\n", tag, val); + if (tag == 4) parsed_usage_page = val; + if (tag == 8) parsed_usage = val; + if (parsed_usage_page && parsed_usage) break; + } + if ((!parsed_usage_page) || (!parsed_usage) || + (usage_page > 0 && parsed_usage_page != usage_page) || + (usage > 0 && parsed_usage != usage)) { + usb_release_interface(u, i); + continue; + } + hid = (struct hid_struct *)malloc(sizeof(struct hid_struct)); + if (!hid) { + usb_release_interface(u, i); + continue; + } + hid->usb = u; + hid->iface = i; + hid->ep_in = ep_in; + hid->ep_out = ep_out; + hid->open = 1; + add_hid(hid); + claimed++; + count++; + if (count >= max) return count; + } + if (u && !claimed) usb_close(u); + } + } + return count; +} + +// recveive - receive a packet +// Inputs: +// num = device to receive from (zero based) +// buf = buffer to receive packet +// len = buffer's size +// timeout = time to wait, in milliseconds +// Output: +// number of bytes received, or -1 on error +// +int pjrc_rawhid::receive(int num, void *buf, int len, int timeout) +{ + hid_t *hid; + int r; + + hid = get_hid(num); + if (!hid || !hid->open) return -1; + r = usb_interrupt_read(hid->usb, hid->ep_in, (char *)buf, len, timeout); + if (r >= 0) return r; + if (r == -110) return 0; // timeout + return -1; +} + +// 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; + + hid = get_hid(num); + if (!hid || !hid->open) return -1; + if (hid->ep_out) { + return usb_interrupt_write(hid->usb, hid->ep_out, (char *)buf, len, timeout); + } else { + return usb_control_msg(hid->usb, 0x21, 9, 0, hid->iface, (char *)buf, len, timeout); + } +} + +// getserial - get the serialnumber of the device +// +// Inputs: +// num = device to close (zero based) +// buf = buffer to read the serialnumber into +// Output +// number of bytes in found, or -1 on error +// +int pjrc_rawhid::getserial(int num, char *buf) { + hid_t *hid; + + hid = get_hid(num); + if (!hid || !hid->open) return -1; + + return usb_get_string_simple(hid->usb, 3, (char *)buf, 25); +} + +// close - close a device +// +// Inputs: +// num = device to close (zero based) +// Output +// (nothing) +// +void pjrc_rawhid::close(int num) +{ + hid_t *hid; + + hid = get_hid(num); + if (!hid || !hid->open) return; + hid_close(hid); +} + +// +// +// Private Functions +// +// + +// Chuck Robey wrote a real HID report parser +// (chuckr@telenix.org) chuckr@chuckr.org +// http://people.freebsd.org/~chuckr/code/python/uhidParser-0.2.tbz +// this tiny thing only needs to extract the top-level usage page +// and usage, and even then is may not be truly correct, but it does +// work with the Teensy Raw HID example. +static int hid_parse_item(uint32_t *val, uint8_t **data, const uint8_t *end) +{ + const uint8_t *p = *data; + uint8_t tag; + int table[4] = {0, 1, 2, 4}; + int len; + + if (p >= end) return -1; + if (p[0] == 0xFE) { + // long item, HID 1.11, 6.2.2.3, page 27 + if (p + 5 >= end || p + p[1] >= end) return -1; + tag = p[2]; + *val = 0; + len = p[1] + 5; + } else { + // short item, HID 1.11, 6.2.2.2, page 26 + tag = p[0] & 0xFC; + len = table[p[0] & 0x03]; + if (p + len + 1 >= end) return -1; + switch (p[0] & 0x03) { + case 3: *val = p[1] | (p[2] << 8) | (p[3] << 16) | (p[4] << 24); break; + case 2: *val = p[1] | (p[2] << 8); break; + case 1: *val = p[1]; break; + case 0: *val = 0; break; + } + } + *data += len + 1; + return tag; +} + + +static void add_hid(hid_t *h) +{ + if (!first_hid || !last_hid) { + first_hid = last_hid = h; + h->next = h->prev = NULL; + return; + } + last_hid->next = h; + h->prev = last_hid; + h->next = NULL; + last_hid = h; +} + + +static hid_t * get_hid(int num) +{ + hid_t *p; + for (p = first_hid; p && num > 0; p = p->next, num--) ; + return p; +} + + +static void free_all_hid(void) +{ + hid_t *p, *q; + + 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) +{ + hid_t *p; + int others=0; + + usb_release_interface(hid->usb, hid->iface); + for (p = first_hid; p; p = p->next) { + if (p->open && p->usb == hid->usb) others++; + } + if (!others) usb_close(hid->usb); + hid->usb = NULL; +} diff --git a/ground/src/plugins/rawhid/pjrc_rawhid_win.cpp b/ground/src/plugins/rawhid/pjrc_rawhid_win.cpp new file mode 100644 index 000000000..e1f84ba82 --- /dev/null +++ b/ground/src/plugins/rawhid/pjrc_rawhid_win.cpp @@ -0,0 +1,351 @@ +/* Simple Raw HID functions for Windows - for use with Teensy RawHID example + * http://www.pjrc.com/teensy/rawhid.html + * Copyright (c) 2009 PJRC.COM, LLC + * + * rawhid_open - open 1 or more devices + * rawhid_recv - receive a packet + * rawhid_send - send a packet + * rawhid_close - close a device + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above description, website URL and copyright notice and this permission + * notice shall be included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + * Version 1.0: Initial Release + */ +/* See: http://msdn.microsoft.com/en-us/library/ms794141.aspx */ + +#include "pjrc_rawhid.h" + +#include +#include +#include +#include + +#define printf //qDebug + +typedef struct hid_struct hid_t; +struct hid_struct { + HANDLE handle; + int open; + struct hid_struct *prev; + struct hid_struct *next; +}; +hid_t *first_hid; +hid_t *last_hid; +HANDLE rx_event; +HANDLE tx_event; +CRITICAL_SECTION rx_mutex; +CRITICAL_SECTION tx_mutex; + +static void add_hid(hid_t *h); +static hid_t* get_hid(int num); +static void free_all_hid(void); +static void hid_close(hid_t *hid); +static void print_win32_err(void); + +pjrc_rawhid::pjrc_rawhid() +{ + first_hid = NULL; + last_hid = NULL; + rx_event = NULL; + tx_event = NULL; +} + +// open - open 1 or more devices +// +// Inputs: +// max = maximum number of devices to open +// vid = Vendor ID, or -1 if any +// pid = Product ID, or -1 if any +// usage_page = top level usage page, or -1 if any +// usage = top level usage number, or -1 if any +// Output: +// actual number of devices opened +// +int pjrc_rawhid::open(int max, int vid, int pid, int usage_page, int usage) +{ + GUID guid; + HDEVINFO info; + DWORD index=0, reqd_size; + SP_DEVICE_INTERFACE_DATA iface; + SP_DEVICE_INTERFACE_DETAIL_DATA *details; + HIDD_ATTRIBUTES attrib; + PHIDP_PREPARSED_DATA hid_data; + HIDP_CAPS capabilities; + HANDLE h; + BOOL ret; + hid_t *hid; + int count=0; + + if (first_hid) free_all_hid(); + if (max < 1) return 0; + if (!rx_event) { + rx_event = CreateEvent(NULL, TRUE, TRUE, NULL); + tx_event = CreateEvent(NULL, TRUE, TRUE, NULL); + InitializeCriticalSection(&rx_mutex); + InitializeCriticalSection(&tx_mutex); + } + HidD_GetHidGuid(&guid); + info = SetupDiGetClassDevs(&guid, NULL, NULL, DIGCF_PRESENT | DIGCF_DEVICEINTERFACE); + if (info == INVALID_HANDLE_VALUE) return 0; + for (index=0; 1 ;index++) { + iface.cbSize = sizeof(SP_DEVICE_INTERFACE_DATA); + ret = SetupDiEnumDeviceInterfaces(info, NULL, &guid, index, &iface); + if (!ret) return count; + SetupDiGetInterfaceDeviceDetail(info, &iface, NULL, 0, &reqd_size, NULL); + details = (SP_DEVICE_INTERFACE_DETAIL_DATA *)malloc(reqd_size); + if (details == NULL) continue; + + memset(details, 0, reqd_size); + details->cbSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA); + ret = SetupDiGetDeviceInterfaceDetail(info, &iface, details, + reqd_size, NULL, NULL); + if (!ret) { + free(details); + continue; + } + h = CreateFile(details->DevicePath, GENERIC_READ|GENERIC_WRITE, + FILE_SHARE_READ|FILE_SHARE_WRITE, NULL, + OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL); + free(details); + if (h == INVALID_HANDLE_VALUE) continue; + attrib.Size = sizeof(HIDD_ATTRIBUTES); + ret = HidD_GetAttributes(h, &attrib); + printf("vid: %4x\n", attrib.VendorID); + if (!ret || (vid > 0 && attrib.VendorID != vid) || + (pid > 0 && attrib.ProductID != pid) || + !HidD_GetPreparsedData(h, &hid_data)) { + CloseHandle(h); + continue; + } + if (!HidP_GetCaps(hid_data, &capabilities) || + (usage_page > 0 && capabilities.UsagePage != usage_page) || + (usage > 0 && capabilities.Usage != usage)) { + HidD_FreePreparsedData(hid_data); + CloseHandle(h); + continue; + } + HidD_FreePreparsedData(hid_data); + hid = (struct hid_struct *)malloc(sizeof(struct hid_struct)); + if (!hid) { + CloseHandle(h); + continue; + } + hid->handle = h; + hid->open = 1; + add_hid(hid); + count++; + if (count >= max) return count; + } + return count; +} + +// recveive - receive a packet +// Inputs: +// num = device to receive from (zero based) +// buf = buffer to receive packet +// len = buffer's size +// timeout = time to wait, in milliseconds +// Output: +// number of bytes received, or -1 on error +// +int pjrc_rawhid::receive(int num, void *buf, int len, int timeout) +{ + hid_t *hid; + unsigned char tmpbuf[516]; + OVERLAPPED ov; + DWORD n, r; + + if (sizeof(tmpbuf) < len + 1) return -1; + hid = get_hid(num); + if (!hid || !hid->open) return -1; + EnterCriticalSection(&rx_mutex); + ResetEvent(&rx_event); + memset(&ov, 0, sizeof(ov)); + ov.hEvent = rx_event; + if (!ReadFile(hid->handle, tmpbuf, len + 1, NULL, &ov)) { + if (GetLastError() != ERROR_IO_PENDING) goto return_error; + r = WaitForSingleObject(rx_event, timeout); + if (r == WAIT_TIMEOUT) goto return_timeout; + if (r != WAIT_OBJECT_0) goto return_error; + } + if (!GetOverlappedResult(hid->handle, &ov, &n, FALSE)) goto return_error; + LeaveCriticalSection(&rx_mutex); + if (n <= 0) return -1; + n--; + if (n > len) n = len; + memcpy(buf, tmpbuf + 1, n); + return n; +return_timeout: + CancelIo(hid->handle); + LeaveCriticalSection(&rx_mutex); + return 0; +return_error: + print_win32_err(); + LeaveCriticalSection(&rx_mutex); + return -1; +} + +// 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; + unsigned char tmpbuf[516]; + OVERLAPPED ov; + DWORD n, r; + + if (sizeof(tmpbuf) < len + 1) return -1; + hid = get_hid(num); + if (!hid || !hid->open) return -1; + EnterCriticalSection(&tx_mutex); + ResetEvent(&tx_event); + memset(&ov, 0, sizeof(ov)); + ov.hEvent = tx_event; + tmpbuf[0] = 0; + memcpy(tmpbuf + 1, buf, len); + if (!WriteFile(hid->handle, tmpbuf, len + 1, NULL, &ov)) { + if (GetLastError() != ERROR_IO_PENDING) goto return_error; + r = WaitForSingleObject(tx_event, timeout); + if (r == WAIT_TIMEOUT) goto return_timeout; + if (r != WAIT_OBJECT_0) goto return_error; + } + if (!GetOverlappedResult(hid->handle, &ov, &n, FALSE)) goto return_error; + LeaveCriticalSection(&tx_mutex); + if (n <= 0) return -1; + return n - 1; +return_timeout: + CancelIo(hid->handle); + LeaveCriticalSection(&tx_mutex); + return 0; +return_error: + print_win32_err(); + LeaveCriticalSection(&tx_mutex); + return -1; +} + +int pjrc_rawhid::getserial(int num, char *buf) +{ + hid_t *hid; + char temp[126]; + char buf2[24]; + char *bufptr = (char *)buf; + + hid = get_hid(num); + if (!hid || !hid->open) return -1; + + /* Should we do some "critical section" stuff here?? */ + if(!HidD_GetSerialNumberString(hid->handle, temp, sizeof(temp))) { + print_win32_err(); + return -1; + } + + /* Is there a better way to do this? */ + /* Every second char in temp is a NULL */ + for(int i = 0; i < 48; i++) { + char temp2 = temp[i++]; + if(temp2 == 0) break; + buf2[i/2] = temp2; + *(bufptr++) = temp2; + } + + return 0; +} + +// close - close a device +// +// Inputs: +// num = device to close (zero based) +// Output +// (nothing) +// +void pjrc_rawhid::close(int num) +{ + hid_t *hid; + + hid = get_hid(num); + if (!hid || !hid->open) return; + hid_close(hid); +} + +// +// +// Private Functions +// +// +static void add_hid(hid_t *h) +{ + if (!first_hid || !last_hid) { + first_hid = last_hid = h; + h->next = h->prev = NULL; + return; + } + last_hid->next = h; + h->prev = last_hid; + h->next = NULL; + last_hid = h; +} + + +static hid_t * get_hid(int num) +{ + hid_t *p; + for (p = first_hid; p && num > 0; p = p->next, num--) ; + return p; +} + + +static void free_all_hid(void) +{ + hid_t *p, *q; + + 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) +{ + CloseHandle(hid->handle); + hid->handle = NULL; +} + +static void print_win32_err(void) +{ + char buf[256]; + DWORD err; + + err = GetLastError(); + FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, NULL, err, 0, (WCHAR*)buf, sizeof(buf), NULL); + printf("err %ld: %s\n", err, buf); +} diff --git a/ground/src/plugins/rawhid/rawhid.cpp b/ground/src/plugins/rawhid/rawhid.cpp new file mode 100644 index 000000000..957b88e1f --- /dev/null +++ b/ground/src/plugins/rawhid/rawhid.cpp @@ -0,0 +1,109 @@ +/** + ****************************************************************************** + * + * @file rawhid.cpp + * @author The OpenPilot Team, http://www.openpilot.org Copyright (C) 2010. + * @brief QIODevice interface for USB RawHID + * @see The GNU Public License (GPL) Version 3 + * @defgroup rawhid_plugin + * @{ + * + *****************************************************************************/ +/* + * 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 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "rawhid.h" + +#include "rawhid_const.h" + +//timeout value used when we want to return directly without waiting +static const int IMMEDIATE_READ_TIMEOUT = 10; +static const int IMMEDIATE_WRITE_TIMEOUT = 50; + +#if 0 +static const int MAX_RX_LENGTH = 63; +static const int MAX_TX_LENGTH = 63; + +static const int READ_TIMEOUT = 200; +static const int WRITE_TIMEOUT = 100; +#endif + +RawHID::RawHID() + :QIODevice() +{ +} + +RawHID::RawHID(const QString &deviceName) + :QIODevice(), + serialNumber(deviceName), + m_deviceNo(-1) +{ + //find the device the user want to open and close the other + int opened = dev.open(MAX_DEVICES, VID, PID, USAGE_PAGE, USAGE); + + //for each devices found, get serial number and close + for(int i=0; i(data), maxSize, IMMEDIATE_WRITE_TIMEOUT); +} + + diff --git a/ground/src/plugins/rawhid/rawhid.h b/ground/src/plugins/rawhid/rawhid.h new file mode 100644 index 000000000..cf2056921 --- /dev/null +++ b/ground/src/plugins/rawhid/rawhid.h @@ -0,0 +1,56 @@ +/** + ****************************************************************************** + * + * @file rawhid.h + * @author The OpenPilot Team, http://www.openpilot.org Copyright (C) 2010. + * @brief + * @see The GNU Public License (GPL) Version 3 + * @defgroup rawhid_plugin + * @{ + * + *****************************************************************************/ +/* + * 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 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef RAWHID_H +#define RAWHID_H + +#include "rawhid_global.h" +#include + +#include "pjrc_rawhid.h" + +class RAWHID_EXPORT RawHID : public QIODevice +{ +public: + RawHID(); + RawHID(const QString &deviceName); + virtual ~RawHID(); + + virtual bool open(OpenMode mode); + virtual void close(); + virtual bool isSequential() const; + +protected: + virtual qint64 readData(char *data, qint64 maxSize); + virtual qint64 writeData(const char *data, qint64 maxSize); + + QString serialNumber; + int m_deviceNo; + pjrc_rawhid dev; +}; + +#endif // RAWHID_H diff --git a/ground/src/plugins/rawhid/rawhid.pro b/ground/src/plugins/rawhid/rawhid.pro index b28c8d97d..e895de4fc 100644 --- a/ground/src/plugins/rawhid/rawhid.pro +++ b/ground/src/plugins/rawhid/rawhid.pro @@ -2,14 +2,40 @@ TEMPLATE = lib TARGET = RawHID include(../../openpilotgcsplugin.pri) include(rawhid_dependencies.pri) - -HEADERS += rawhidplugin.h - -SOURCES += rawhidplugin.cpp - +HEADERS += rawhid_global.h \ + rawhidplugin.h \ + rawhid.h \ + pjrc_rawhid.h \ + rawhid_const.h +SOURCES += rawhidplugin.cpp \ + rawhid.cpp FORMS += - RESOURCES += - DEFINES += RAWHID_LIBRARY OTHER_FILES += RawHID.pluginspec + +# Platform Specific USB HID Stuff +win32 { + SOURCES += pjrc_rawhid_win.cpp + LIBS += -lhid \ + -lsetupapi +} +macx { + SOURCES += pjrc_rawhid_mac.cpp + SDK = /Developer/SDKs/MacOSX10.5.sdk + ARCH = -mmacosx-version-min=10.5 \ + -arch \ + ppc \ + -arch \ + i386 + LIBS += $(ARCH) \ + -Wl,-syslibroot,$(SDK) \ + -framework \ + IOKit \ + -framework \ + CoreFoundation +} +linux-g++ { + SOURCES += pjrc_rawhid_unix.cpp + LIBS += -lusb +} diff --git a/ground/src/plugins/rawhid/rawhid_const.h b/ground/src/plugins/rawhid/rawhid_const.h new file mode 100644 index 000000000..c50d6cbc4 --- /dev/null +++ b/ground/src/plugins/rawhid/rawhid_const.h @@ -0,0 +1,42 @@ +/** + ****************************************************************************** + * + * @file rawhid_const.h + * @author The OpenPilot Team, http://www.openpilot.org Copyright (C) 2010. + * @brief + * @see The GNU Public License (GPL) Version 3 + * @defgroup rawhid_plugin + * @{ + * + *****************************************************************************/ +/* + * 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 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef RAWHID_CONST_H +#define RAWHID_CONST_H + + +static const int MAX_DEVICES = 10; + +static const int VID = 0x20A0; +static const int PID = 0x4117; + +static const int USAGE_PAGE = 0xFF9C; +static const int USAGE = 0x0001; + +static const int DEV_SERIAL_LEN = 24; + +#endif // RAWHID_CONST_H diff --git a/ground/src/plugins/rawhid/rawhid_global.h b/ground/src/plugins/rawhid/rawhid_global.h new file mode 100644 index 000000000..840f7b2b7 --- /dev/null +++ b/ground/src/plugins/rawhid/rawhid_global.h @@ -0,0 +1,39 @@ +/** + ****************************************************************************** + * + * @file rawhid_global.h + * @author The OpenPilot Team, http://www.openpilot.org Copyright (C) 2010. + * @brief + * @see The GNU Public License (GPL) Version 3 + * @defgroup rawhid_plugin + * @{ + * + *****************************************************************************/ +/* + * 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 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef RAWHID_GLOBAL_H +#define RAWHID_GLOBAL_H + +#include + +#if defined(RAWHID_LIBRARY) +# define RAWHID_EXPORT Q_DECL_EXPORT +#else +# define RAWHID_EXPORT Q_DECL_IMPORT +#endif + +#endif // RAWHID_GLOBAL_H diff --git a/ground/src/plugins/rawhid/rawhid_win.cpp b/ground/src/plugins/rawhid/rawhid_win.cpp new file mode 100644 index 000000000..909ab5ea4 --- /dev/null +++ b/ground/src/plugins/rawhid/rawhid_win.cpp @@ -0,0 +1,351 @@ +/* Simple Raw HID functions for Windows - for use with Teensy RawHID example + * http://www.pjrc.com/teensy/rawhid.html + * Copyright (c) 2009 PJRC.COM, LLC + * + * rawhid_open - open 1 or more devices + * rawhid_recv - receive a packet + * rawhid_send - send a packet + * rawhid_close - close a device + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above description, website URL and copyright notice and this permission + * notice shall be included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + * Version 1.0: Initial Release + */ +/* See: http://msdn.microsoft.com/en-us/library/ms794141.aspx */ + +#include "rawhid.h" + +#include +#include +#include +#include + +#define printf //qDebug + +typedef struct hid_struct hid_t; +struct hid_struct { + HANDLE handle; + int open; + struct hid_struct *prev; + struct hid_struct *next; +}; +hid_t *first_hid; +hid_t *last_hid; +HANDLE rx_event; +HANDLE tx_event; +CRITICAL_SECTION rx_mutex; +CRITICAL_SECTION tx_mutex; + +static void add_hid(hid_t *h); +static hid_t* get_hid(int num); +static void free_all_hid(void); +static void hid_close(hid_t *hid); +static void print_win32_err(void); + +rawhid::rawhid() +{ + first_hid = NULL; + last_hid = NULL; + rx_event = NULL; + tx_event = NULL; +} + +// open - open 1 or more devices +// +// Inputs: +// max = maximum number of devices to open +// vid = Vendor ID, or -1 if any +// pid = Product ID, or -1 if any +// usage_page = top level usage page, or -1 if any +// usage = top level usage number, or -1 if any +// Output: +// actual number of devices opened +// +int rawhid::open(int max, int vid, int pid, int usage_page, int usage) +{ + GUID guid; + HDEVINFO info; + DWORD index=0, reqd_size; + SP_DEVICE_INTERFACE_DATA iface; + SP_DEVICE_INTERFACE_DETAIL_DATA *details; + HIDD_ATTRIBUTES attrib; + PHIDP_PREPARSED_DATA hid_data; + HIDP_CAPS capabilities; + HANDLE h; + BOOL ret; + hid_t *hid; + int count=0; + + if (first_hid) free_all_hid(); + if (max < 1) return 0; + if (!rx_event) { + rx_event = CreateEvent(NULL, TRUE, TRUE, NULL); + tx_event = CreateEvent(NULL, TRUE, TRUE, NULL); + InitializeCriticalSection(&rx_mutex); + InitializeCriticalSection(&tx_mutex); + } + HidD_GetHidGuid(&guid); + info = SetupDiGetClassDevs(&guid, NULL, NULL, DIGCF_PRESENT | DIGCF_DEVICEINTERFACE); + if (info == INVALID_HANDLE_VALUE) return 0; + for (index=0; 1 ;index++) { + iface.cbSize = sizeof(SP_DEVICE_INTERFACE_DATA); + ret = SetupDiEnumDeviceInterfaces(info, NULL, &guid, index, &iface); + if (!ret) return count; + SetupDiGetInterfaceDeviceDetail(info, &iface, NULL, 0, &reqd_size, NULL); + details = (SP_DEVICE_INTERFACE_DETAIL_DATA *)malloc(reqd_size); + if (details == NULL) continue; + + memset(details, 0, reqd_size); + details->cbSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA); + ret = SetupDiGetDeviceInterfaceDetail(info, &iface, details, + reqd_size, NULL, NULL); + if (!ret) { + free(details); + continue; + } + h = CreateFile(details->DevicePath, GENERIC_READ|GENERIC_WRITE, + FILE_SHARE_READ|FILE_SHARE_WRITE, NULL, + OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL); + free(details); + if (h == INVALID_HANDLE_VALUE) continue; + attrib.Size = sizeof(HIDD_ATTRIBUTES); + ret = HidD_GetAttributes(h, &attrib); + printf("vid: %4x\n", attrib.VendorID); + if (!ret || (vid > 0 && attrib.VendorID != vid) || + (pid > 0 && attrib.ProductID != pid) || + !HidD_GetPreparsedData(h, &hid_data)) { + CloseHandle(h); + continue; + } + if (!HidP_GetCaps(hid_data, &capabilities) || + (usage_page > 0 && capabilities.UsagePage != usage_page) || + (usage > 0 && capabilities.Usage != usage)) { + HidD_FreePreparsedData(hid_data); + CloseHandle(h); + continue; + } + HidD_FreePreparsedData(hid_data); + hid = (struct hid_struct *)malloc(sizeof(struct hid_struct)); + if (!hid) { + CloseHandle(h); + continue; + } + hid->handle = h; + hid->open = 1; + add_hid(hid); + count++; + if (count >= max) return count; + } + return count; +} + +// recveive - receive a packet +// Inputs: +// num = device to receive from (zero based) +// buf = buffer to receive packet +// len = buffer's size +// timeout = time to wait, in milliseconds +// Output: +// number of bytes received, or -1 on error +// +int rawhid::receive(int num, void *buf, int len, int timeout) +{ + hid_t *hid; + unsigned char tmpbuf[516]; + OVERLAPPED ov; + DWORD n, r; + + if (sizeof(tmpbuf) < len + 1) return -1; + hid = get_hid(num); + if (!hid || !hid->open) return -1; + EnterCriticalSection(&rx_mutex); + ResetEvent(&rx_event); + memset(&ov, 0, sizeof(ov)); + ov.hEvent = rx_event; + if (!ReadFile(hid->handle, tmpbuf, len + 1, NULL, &ov)) { + if (GetLastError() != ERROR_IO_PENDING) goto return_error; + r = WaitForSingleObject(rx_event, timeout); + if (r == WAIT_TIMEOUT) goto return_timeout; + if (r != WAIT_OBJECT_0) goto return_error; + } + if (!GetOverlappedResult(hid->handle, &ov, &n, FALSE)) goto return_error; + LeaveCriticalSection(&rx_mutex); + if (n <= 0) return -1; + n--; + if (n > len) n = len; + memcpy(buf, tmpbuf + 1, n); + return n; +return_timeout: + CancelIo(hid->handle); + LeaveCriticalSection(&rx_mutex); + return 0; +return_error: + print_win32_err(); + LeaveCriticalSection(&rx_mutex); + return -1; +} + +// 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 rawhid::send(int num, void *buf, int len, int timeout) +{ + hid_t *hid; + unsigned char tmpbuf[516]; + OVERLAPPED ov; + DWORD n, r; + + if (sizeof(tmpbuf) < len + 1) return -1; + hid = get_hid(num); + if (!hid || !hid->open) return -1; + EnterCriticalSection(&tx_mutex); + ResetEvent(&tx_event); + memset(&ov, 0, sizeof(ov)); + ov.hEvent = tx_event; + tmpbuf[0] = 0; + memcpy(tmpbuf + 1, buf, len); + if (!WriteFile(hid->handle, tmpbuf, len + 1, NULL, &ov)) { + if (GetLastError() != ERROR_IO_PENDING) goto return_error; + r = WaitForSingleObject(tx_event, timeout); + if (r == WAIT_TIMEOUT) goto return_timeout; + if (r != WAIT_OBJECT_0) goto return_error; + } + if (!GetOverlappedResult(hid->handle, &ov, &n, FALSE)) goto return_error; + LeaveCriticalSection(&tx_mutex); + if (n <= 0) return -1; + return n - 1; +return_timeout: + CancelIo(hid->handle); + LeaveCriticalSection(&tx_mutex); + return 0; +return_error: + print_win32_err(); + LeaveCriticalSection(&tx_mutex); + return -1; +} + +int rawhid::getserial(int num, char *buf) +{ + hid_t *hid; + char temp[126]; + char buf2[24]; + char *bufptr = (char *)buf; + + hid = get_hid(num); + if (!hid || !hid->open) return -1; + + /* Should we do some "critical section" stuff here?? */ + if(!HidD_GetSerialNumberString(hid->handle, temp, sizeof(temp))) { + print_win32_err(); + return -1; + } + + /* Is there a better way to do this? */ + /* Every second char in temp is a NULL */ + for(int i = 0; i < 48; i++) { + char temp2 = temp[i++]; + if(temp2 == 0) break; + buf2[i/2] = temp2; + *(bufptr++) = temp2; + } + + return 0; +} + +// close - close a device +// +// Inputs: +// num = device to close (zero based) +// Output +// (nothing) +// +void rawhid::close(int num) +{ + hid_t *hid; + + hid = get_hid(num); + if (!hid || !hid->open) return; + hid_close(hid); +} + +// +// +// Private Functions +// +// +static void add_hid(hid_t *h) +{ + if (!first_hid || !last_hid) { + first_hid = last_hid = h; + h->next = h->prev = NULL; + return; + } + last_hid->next = h; + h->prev = last_hid; + h->next = NULL; + last_hid = h; +} + + +static hid_t * get_hid(int num) +{ + hid_t *p; + for (p = first_hid; p && num > 0; p = p->next, num--) ; + return p; +} + + +static void free_all_hid(void) +{ + hid_t *p, *q; + + 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) +{ + CloseHandle(hid->handle); + hid->handle = NULL; +} + +static void print_win32_err(void) +{ + char buf[256]; + DWORD err; + + err = GetLastError(); + FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, NULL, err, 0, (WCHAR*)buf, sizeof(buf), NULL); + printf("err %ld: %s\n", err, buf); +} diff --git a/ground/src/plugins/rawhid/rawhidplugin.cpp b/ground/src/plugins/rawhid/rawhidplugin.cpp index b2fd5df4c..56c6d6d63 100644 --- a/ground/src/plugins/rawhid/rawhidplugin.cpp +++ b/ground/src/plugins/rawhid/rawhidplugin.cpp @@ -3,10 +3,9 @@ * * @file rawhidplugin.cpp * @author The OpenPilot Team, http://www.openpilot.org Copyright (C) 2010. - * Parts by Nokia Corporation (qt-info@nokia.com) Copyright (C) 2009. - * @brief + * @brief Register connection object for the core connection manager * @see The GNU Public License (GPL) Version 3 - * @defgroup welcomeplugin + * @defgroup rawhid_plugin * @{ * *****************************************************************************/ @@ -27,14 +26,119 @@ */ #include "rawhidplugin.h" - +#include "rawhid.h" #include #include +#include + +#include "pjrc_rawhid.h" + +#include "rawhid_const.h" + + + +RawHIDEnumerationThread::RawHIDEnumerationThread(RawHIDConnection *rawhid) + : m_rawhid(rawhid), + m_running(true) +{ +} + +RawHIDEnumerationThread::~RawHIDEnumerationThread() +{ + m_running = false; + //wait for the thread to terminate + if(wait(1000) == false) + qDebug() << "Cannot terminate rawhid thread"; +} + +void RawHIDEnumerationThread::run() +{ + QStringList devices = m_rawhid->availableDevices(); + + while(m_running) + { + if(!m_rawhid->deviceOpened()) + { + QStringList newDev = m_rawhid->availableDevices(); + if(devices != newDev) + { + devices = newDev; + emit enumerationChanged(); + } + } + + msleep(500); //update available devices twice per second + } +} + + +RawHIDConnection::RawHIDConnection() + : m_enumerateThread(this) +{ + QObject::connect(&m_enumerateThread, SIGNAL(enumerationChanged()), + this, SLOT(onEnumerationChanged())); + m_enumerateThread.start(); +} + +RawHIDConnection::~RawHIDConnection() +{} + +void RawHIDConnection::onEnumerationChanged() +{ + emit availableDevChanged(this); +} + +QStringList RawHIDConnection::availableDevices() +{ + QMutexLocker locker(&m_enumMutex); + + QStringList devices; + pjrc_rawhid dev; + + //open all device we can + int opened = dev.open(MAX_DEVICES, VID, PID, USAGE_PAGE, USAGE); + + //for each devices found, get serial number and close it back + for(int i=0; i +#include +#include -class RawHIDPlugin - : public ExtensionSystem::IPlugin +class IConnection; +class RawHIDConnection; + +class RAWHID_EXPORT RawHIDEnumerationThread : public QThread +{ + Q_OBJECT +public: + RawHIDEnumerationThread(RawHIDConnection *rawhid); + virtual ~RawHIDEnumerationThread(); + + virtual void run(); + +signals: + void enumerationChanged(); + +protected: + RawHIDConnection *m_rawhid; + bool m_running; +}; + +class RAWHID_EXPORT RawHIDConnection + : public Core::IConnection +{ + Q_OBJECT +public: + RawHIDConnection(); + virtual ~RawHIDConnection(); + + virtual QStringList availableDevices(); + virtual QIODevice *openDevice(const QString &deviceName); + virtual void closeDevice(const QString &deviceName); + + virtual QString connectionName(); + virtual QString shortName(); + + bool deviceOpened() {return m_deviceOpened;} + +protected slots: + void onEnumerationChanged(); + +protected: + QMutex m_enumMutex; + RawHIDEnumerationThread m_enumerateThread; + bool m_deviceOpened; +}; + +class RAWHID_EXPORT RawHIDPlugin + : public ExtensionSystem::IPlugin { Q_OBJECT @@ -41,9 +91,8 @@ public: RawHIDPlugin(); ~RawHIDPlugin(); - bool initialize(const QStringList &arguments, QString *error_message); - - void extensionsInitialized(); + virtual bool initialize(const QStringList &arguments, QString *error_message); + virtual void extensionsInitialized(); };