From ab0f938bd5e83b2300d9cfb8799c69599f603f9c Mon Sep 17 00:00:00 2001 From: Simon Newton Date: Sun, 9 Nov 2014 16:32:52 -0800 Subject: [PATCH 01/34] Partial commit of the new libusb DMX plugin. --- include/olad/Plugin.h | 6 +- plugins/usbdmx/AnymaDevice.cpp | 55 +-- plugins/usbdmx/AnymaDevice.h | 35 +- plugins/usbdmx/AnymaDeviceManager.cpp | 83 +++++ plugins/usbdmx/AnymaDeviceManager.h | 59 +++ plugins/usbdmx/AnymaOutputPort.cpp | 143 +------- plugins/usbdmx/AnymaOutputPort.h | 48 +-- plugins/usbdmx/AnymaWidget.cpp | 210 +++++++++++ plugins/usbdmx/AnymaWidget.h | 102 ++++++ plugins/usbdmx/AsyncPluginImpl.cpp | 290 +++++++++++++++ plugins/usbdmx/AsyncPluginImpl.h | 118 ++++++ plugins/usbdmx/EuroliteProDevice.cpp | 36 +- plugins/usbdmx/EuroliteProDevice.h | 31 +- plugins/usbdmx/EuroliteProDeviceManager.cpp | 88 +++++ plugins/usbdmx/EuroliteProDeviceManager.h | 55 +++ plugins/usbdmx/EuroliteProOutputPort.cpp | 273 +------------- plugins/usbdmx/EuroliteProOutputPort.h | 56 +-- plugins/usbdmx/EuroliteProWidget.cpp | 272 ++++++++++++++ plugins/usbdmx/EuroliteProWidget.h | 109 ++++++ plugins/usbdmx/FirmwareLoader.h | 6 +- plugins/usbdmx/LibUsbAdaptor.cpp | 36 ++ plugins/usbdmx/LibUsbAdaptor.h | 47 +++ plugins/usbdmx/LibUsbHelper.cpp | 126 +++++++ plugins/usbdmx/LibUsbHelper.h | 97 +++++ plugins/usbdmx/Makefile.mk | 25 ++ plugins/usbdmx/PluginImplInterface.h | 56 +++ plugins/usbdmx/SunliteDevice.cpp | 25 +- plugins/usbdmx/SunliteDevice.h | 28 +- plugins/usbdmx/SunliteDeviceManager.cpp | 67 ++++ plugins/usbdmx/SunliteDeviceManager.h | 54 +++ plugins/usbdmx/SunliteFirmwareLoader.h | 21 +- plugins/usbdmx/SunliteOutputPort.cpp | 173 +-------- plugins/usbdmx/SunliteOutputPort.h | 51 +-- plugins/usbdmx/SunliteWidget.cpp | 245 +++++++++++++ plugins/usbdmx/SunliteWidget.h | 105 ++++++ plugins/usbdmx/SyncPluginImpl.cpp | 331 +++++++++++++++++ plugins/usbdmx/SyncPluginImpl.h | 107 ++++++ plugins/usbdmx/ThreadedUsbSender.cpp | 98 +++++ plugins/usbdmx/ThreadedUsbSender.h | 73 ++++ plugins/usbdmx/UsbDevice.h | 6 +- plugins/usbdmx/UsbDeviceManagerInterface.h | 110 ++++++ plugins/usbdmx/UsbDmxPlugin.cpp | 380 ++------------------ plugins/usbdmx/UsbDmxPlugin.h | 83 ++--- plugins/usbdmx/VellemanDevice.h | 16 +- plugins/usbdmx/VellemanOutputPort.h | 3 + 45 files changed, 3231 insertions(+), 1207 deletions(-) create mode 100644 plugins/usbdmx/AnymaDeviceManager.cpp create mode 100644 plugins/usbdmx/AnymaDeviceManager.h create mode 100644 plugins/usbdmx/AnymaWidget.cpp create mode 100644 plugins/usbdmx/AnymaWidget.h create mode 100644 plugins/usbdmx/AsyncPluginImpl.cpp create mode 100644 plugins/usbdmx/AsyncPluginImpl.h create mode 100644 plugins/usbdmx/EuroliteProDeviceManager.cpp create mode 100644 plugins/usbdmx/EuroliteProDeviceManager.h create mode 100644 plugins/usbdmx/EuroliteProWidget.cpp create mode 100644 plugins/usbdmx/EuroliteProWidget.h create mode 100644 plugins/usbdmx/LibUsbAdaptor.cpp create mode 100644 plugins/usbdmx/LibUsbAdaptor.h create mode 100644 plugins/usbdmx/LibUsbHelper.cpp create mode 100644 plugins/usbdmx/LibUsbHelper.h create mode 100644 plugins/usbdmx/PluginImplInterface.h create mode 100644 plugins/usbdmx/SunliteDeviceManager.cpp create mode 100644 plugins/usbdmx/SunliteDeviceManager.h create mode 100644 plugins/usbdmx/SunliteWidget.cpp create mode 100644 plugins/usbdmx/SunliteWidget.h create mode 100644 plugins/usbdmx/SyncPluginImpl.cpp create mode 100644 plugins/usbdmx/SyncPluginImpl.h create mode 100644 plugins/usbdmx/ThreadedUsbSender.cpp create mode 100644 plugins/usbdmx/ThreadedUsbSender.h create mode 100644 plugins/usbdmx/UsbDeviceManagerInterface.h diff --git a/include/olad/Plugin.h b/include/olad/Plugin.h index 80c3ad37ae..5b0bfea0aa 100644 --- a/include/olad/Plugin.h +++ b/include/olad/Plugin.h @@ -121,7 +121,11 @@ class Plugin: public AbstractPlugin { virtual bool DefaultMode() const { return true; } virtual ola_plugin_id Id() const = 0; - // return the prefix used to identify this plugin + /** + * @brief The prefix to use for storing configuration files + * @returns A unique prefix used to identify the configuration file for this + * plugin. + */ virtual std::string PluginPrefix() const = 0; // by default we don't conflict with any other plugins diff --git a/plugins/usbdmx/AnymaDevice.cpp b/plugins/usbdmx/AnymaDevice.cpp index e42b4b8b56..c65830d066 100644 --- a/plugins/usbdmx/AnymaDevice.cpp +++ b/plugins/usbdmx/AnymaDevice.cpp @@ -14,67 +14,32 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * * AnymaDevice.cpp - * The Anyma usb driver + * The Anyma uDMX device. * Copyright (C) 2010 Simon Newton */ -#include -#include -#include +#include "plugins/usbdmx/AnymaDevice.h" +#include #include "ola/Logging.h" -#include "plugins/usbdmx/AnymaDevice.h" +#include "plugins/usbdmx/AnymaOutputPort.h" namespace ola { namespace plugin { namespace usbdmx { -using std::string; - -const char AnymaDevice::EXPECTED_MANUFACTURER[] = "www.anyma.ch"; -const char AnymaDevice::EXPECTED_PRODUCT[] = "uDMX"; - - -/** - * New AnymaDevice. - * @param owner the plugin that owns this device - * @param usb_device a USB device - * @param usb_handle a claimed handle to the device. Ownership is transferred. - * @param serial the serial number, may be empty. - */ AnymaDevice::AnymaDevice(ola::AbstractPlugin *owner, - libusb_device *usb_device, - libusb_device_handle *usb_handle, - const string &serial) - : UsbDevice(owner, "Anyma USB Device", usb_device), - m_output_port(new AnymaOutputPort(this, 0, usb_handle, serial)) { + AnymaWidgetInterface *widget, + const std::string &serial) + : Device(owner, "Anyma USB Device"), + m_device_id("anyma-" + serial), + m_port(new AnymaOutputPort(this, 0, widget)) { } - -/* - * Start this device. - */ bool AnymaDevice::StartHook() { - if (!m_output_port->Start()) { - delete m_output_port; - m_output_port = NULL; - return false; - } - AddPort(m_output_port); + AddPort(m_port.release()); return true; } - - -/* - * Get the device id - */ -string AnymaDevice::DeviceId() const { - if (m_output_port) { - return "anyma-" + m_output_port->SerialNumber(); - } else { - return ""; - } -} } // namespace usbdmx } // namespace plugin } // namespace ola diff --git a/plugins/usbdmx/AnymaDevice.h b/plugins/usbdmx/AnymaDevice.h index fce746221f..5b651fa565 100644 --- a/plugins/usbdmx/AnymaDevice.h +++ b/plugins/usbdmx/AnymaDevice.h @@ -14,42 +14,43 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * * AnymaDevice.h - * Interface for the Anyma device + * The Anyma uDMX device. * Copyright (C) 2010 Simon Newton */ #ifndef PLUGINS_USBDMX_ANYMADEVICE_H_ #define PLUGINS_USBDMX_ANYMADEVICE_H_ -#include +#include #include -#include "plugins/usbdmx/UsbDevice.h" -#include "plugins/usbdmx/AnymaOutputPort.h" +#include "ola/base/Macro.h" +#include "olad/Device.h" namespace ola { namespace plugin { namespace usbdmx { -/* - * A Anyma device +/** + * @brief An Anyma device. */ -class AnymaDevice: public UsbDevice { +class AnymaDevice: public Device { public: - AnymaDevice(ola::AbstractPlugin *owner, - libusb_device *usb_device, - libusb_device_handle *usb_handle, - const std::string &serial); - - std::string DeviceId() const; + AnymaDevice(ola::AbstractPlugin *owner, + class AnymaWidgetInterface *widget, + const std::string &serial); - static const char EXPECTED_MANUFACTURER[]; - static const char EXPECTED_PRODUCT[]; + std::string DeviceId() const { + return m_device_id; + } protected: - bool StartHook(); + bool StartHook(); private: - AnymaOutputPort *m_output_port; + const std::string m_device_id; + std::auto_ptr m_port; + + DISALLOW_COPY_AND_ASSIGN(AnymaDevice); }; } // namespace usbdmx } // namespace plugin diff --git a/plugins/usbdmx/AnymaDeviceManager.cpp b/plugins/usbdmx/AnymaDeviceManager.cpp new file mode 100644 index 0000000000..14a5a1036d --- /dev/null +++ b/plugins/usbdmx/AnymaDeviceManager.cpp @@ -0,0 +1,83 @@ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * 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 Library 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * AnymaDeviceManager.cpp + * The Anyma Device Manager + * Copyright (C) 2014 Simon Newton + */ + +#include "plugins/usbdmx/AnymaDeviceManager.h" + +#include "ola/Logging.h" +#include "plugins/usbdmx/AnymaDevice.h" +#include "plugins/usbdmx/AnymaWidget.h" +#include "plugins/usbdmx/LibUsbHelper.h" + +namespace ola { +namespace plugin { +namespace usbdmx { + +const uint16_t AnymaDeviceManager::ANYMA_VENDOR_ID = 0x16C0; + +bool AnymaDeviceManager::DeviceAdded( + libusb_device *usb_device, + const struct libusb_device_descriptor &descriptor) { + if (descriptor.idVendor != ANYMA_VENDOR_ID || + descriptor.idProduct != 0x05DC || + HasDevice(usb_device)) { + return false; + } + + OLA_INFO << "Found a new Anyma device"; + LibUsbHelper::DeviceInformation info; + if (!LibUsbHelper::GetDeviceInfo(usb_device, descriptor, &info)) { + return false; + } + + if (!LibUsbHelper::CheckManufacturer( + AnymaWidgetInterface::EXPECTED_MANUFACTURER, info.manufacturer)) { + return false; + } + + if (!LibUsbHelper::CheckProduct(AnymaWidgetInterface::EXPECTED_PRODUCT, + info.product)) { + return false; + } + + if (info.serial.empty()) { + if (m_missing_serial_number) { + OLA_WARN << "Failed to read serial number or serial number empty. " + << "We can only support one device without a serial number."; + return false; + } else { + OLA_WARN << "Failed to read serial number from " << info.manufacturer + << " : " << info.product + << " the device probably doesn't have one"; + m_missing_serial_number = true; + } + } + + AsynchronousAnymaWidget *widget = new AsynchronousAnymaWidget(usb_device); + if (!widget->Init()) { + delete widget; + return false; + } + + AnymaDevice *device = new AnymaDevice(ParentPlugin(), widget, info.serial); + return RegisterDevice(usb_device, device); +} +} // namespace usbdmx +} // namespace plugin +} // namespace ola diff --git a/plugins/usbdmx/AnymaDeviceManager.h b/plugins/usbdmx/AnymaDeviceManager.h new file mode 100644 index 0000000000..efface5b07 --- /dev/null +++ b/plugins/usbdmx/AnymaDeviceManager.h @@ -0,0 +1,59 @@ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * 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 Library 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * AnymaDeviceManager.h + * The Anyma Device Manager. + * Copyright (C) 2014 Simon Newton + */ + +#ifndef PLUGINS_USBDMX_ANYMADEVICEMANAGER_H_ +#define PLUGINS_USBDMX_ANYMADEVICEMANAGER_H_ + +#include "ola/base/Macro.h" +#include "plugins/usbdmx/UsbDeviceManagerInterface.h" +#include "plugins/usbdmx/AnymaDevice.h" + +namespace ola { +namespace plugin { +namespace usbdmx { + +/** + * @brief Manages Anyma Devices + */ +class AnymaDeviceManager : public BaseDeviceFactory { + public: + AnymaDeviceManager(PluginAdaptor *plugin_adaptor, + Plugin *plugin) + : BaseDeviceFactory(plugin_adaptor, plugin), + m_missing_serial_number(false) {} + + bool DeviceAdded( + libusb_device *device, + const struct libusb_device_descriptor &descriptor); + + private: + // Some Anyma devices don't have serial numbers. Since there isn't another + // good way to uniquely identify a USB device, we only support one of these + // types of devices. + bool m_missing_serial_number; + + static const uint16_t ANYMA_VENDOR_ID; + + DISALLOW_COPY_AND_ASSIGN(AnymaDeviceManager); +}; +} // namespace usbdmx +} // namespace plugin +} // namespace ola +#endif // PLUGINS_USBDMX_ANYMADEVICEMANAGER_H_ diff --git a/plugins/usbdmx/AnymaOutputPort.cpp b/plugins/usbdmx/AnymaOutputPort.cpp index 7abb908524..e9b7c7dcee 100644 --- a/plugins/usbdmx/AnymaOutputPort.cpp +++ b/plugins/usbdmx/AnymaOutputPort.cpp @@ -14,157 +14,36 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * * AnymaOutputPort.cpp - * Thread for the Anyma Output Port + * The Anyma uDMX output port. * Copyright (C) 2010 Simon Newton */ -#include -#include -#include +#include "plugins/usbdmx/AnymaOutputPort.h" #include "ola/Logging.h" -#include "plugins/usbdmx/AnymaOutputPort.h" #include "plugins/usbdmx/AnymaDevice.h" - +#include "plugins/usbdmx/AnymaWidget.h" namespace ola { namespace plugin { namespace usbdmx { -using std::string; - - -/* - * Create a new AnymaOutputPort object - */ AnymaOutputPort::AnymaOutputPort(AnymaDevice *parent, unsigned int id, - libusb_device_handle *usb_handle, - const string &serial) + AnymaWidgetInterface *widget) : BasicOutputPort(parent, id), - m_term(false), - m_serial(serial), - m_usb_handle(usb_handle) { + m_widget(widget) { } - -/* - * Cleanup - */ AnymaOutputPort::~AnymaOutputPort() { - { - ola::thread::MutexLocker locker(&m_term_mutex); - m_term = true; - } - Join(); + // TODO(simon): stop the thread here?? + OLA_INFO << "AnymaOutputPort::~AnymaOutputPort()"; + delete m_widget; } - -/* - * Start this thread - */ -bool AnymaOutputPort::Start() { - bool ret = ola::thread::Thread::Start(); - if (!ret) { - OLA_WARN << "Failed to start sender thread"; - libusb_release_interface(m_usb_handle, 0); - libusb_close(m_usb_handle); - return false; - } - return true; -} - - -/* - * Run this thread - */ -void *AnymaOutputPort::Run() { - DmxBuffer buffer; - if (!m_usb_handle) - return NULL; - - while (1) { - { - ola::thread::MutexLocker locker(&m_term_mutex); - if (m_term) - break; - } - - { - ola::thread::MutexLocker locker(&m_data_mutex); - buffer.Set(m_buffer); - } - - if (buffer.Size()) { - if (!SendDMX(buffer)) { - OLA_WARN << "Send failed, stopping thread..."; - break; - } - } else { - // sleep for a bit - usleep(40000); - } - } - libusb_release_interface(m_usb_handle, 0); - libusb_close(m_usb_handle); - return NULL; -} - - -/* - * Store the data in the shared buffer - */ -bool AnymaOutputPort::WriteDMX(const DmxBuffer &buffer, uint8_t priority) { - ola::thread::MutexLocker locker(&m_data_mutex); - m_buffer.Set(buffer); - return true; - (void) priority; -} - - -/* - * Send the dmx out the widget - * @return true on success, false on failure - */ -bool AnymaOutputPort::SendDMX(const DmxBuffer &buffer) { - int r = libusb_control_transfer(m_usb_handle, - LIBUSB_REQUEST_TYPE_VENDOR | LIBUSB_RECIPIENT_DEVICE | - LIBUSB_ENDPOINT_OUT, - UDMX_SET_CHANNEL_RANGE, - buffer.Size(), - 0, - // the suck - const_cast(buffer.GetRaw()), - buffer.Size(), - URB_TIMEOUT_MS); - // Sometimes we get PIPE errors here, those are non-fatal - return r > 0 || r == LIBUSB_ERROR_PIPE; -} - - -/* - * Return a string descriptor - * @param usb_handle the usb handle to the device - * @param desc_index the index of the descriptor - * @param data where to store the output string - * @returns true if we got the value, false otherwise - */ -bool AnymaOutputPort::GetDescriptorString(libusb_device_handle *usb_handle, - uint8_t desc_index, - string *data) { - enum { buffer_size = 32 }; // static arrays FTW! - unsigned char buffer[buffer_size]; - int r = libusb_get_string_descriptor_ascii( - usb_handle, - desc_index, - buffer, - buffer_size); - - if (r <= 0) { - OLA_INFO << "libusb_get_string_descriptor_ascii returned " << r; - return false; - } - data->assign(reinterpret_cast(buffer)); +bool AnymaOutputPort::WriteDMX(const DmxBuffer &buffer, + OLA_UNUSED uint8_t priority) { + m_widget->SendDMX(buffer); return true; } } // namespace usbdmx diff --git a/plugins/usbdmx/AnymaOutputPort.h b/plugins/usbdmx/AnymaOutputPort.h index 1e745b5318..7c5b16b023 100644 --- a/plugins/usbdmx/AnymaOutputPort.h +++ b/plugins/usbdmx/AnymaOutputPort.h @@ -14,21 +14,16 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * * AnymaOutputPort.h - * The output port for a Anyma device. + * The Anyma uDMX output port. * Copyright (C) 2010 Simon Newton - * - * It takes around 21ms to send one universe of data. so we do this in a - * separate thread. */ #ifndef PLUGINS_USBDMX_ANYMAOUTPUTPORT_H_ #define PLUGINS_USBDMX_ANYMAOUTPUTPORT_H_ -#include -#include #include +#include "ola/base/Macro.h" #include "ola/DmxBuffer.h" -#include "ola/thread/Thread.h" #include "olad/Port.h" namespace ola { @@ -37,36 +32,27 @@ namespace usbdmx { class AnymaDevice; -class AnymaOutputPort: public BasicOutputPort, ola::thread::Thread { +class AnymaOutputPort: public BasicOutputPort { public: - AnymaOutputPort(AnymaDevice *parent, - unsigned int id, - libusb_device_handle *usb_handle, - const std::string &serial); - ~AnymaOutputPort(); - std::string SerialNumber() const { return m_serial; } + /** + * @brief Create a new AnymaOutputPort. + */ + AnymaOutputPort(AnymaDevice *parent, + unsigned int id, + class AnymaWidgetInterface *widget); + /** + * @brief Cleanup. + */ + ~AnymaOutputPort(); - bool Start(); - void *Run(); + bool WriteDMX(const DmxBuffer &buffer, uint8_t priority); - bool WriteDMX(const DmxBuffer &buffer, uint8_t priority); - std::string Description() const { return ""; } + std::string Description() const { return ""; } private: - static const unsigned int URB_TIMEOUT_MS = 500; - static const unsigned int UDMX_SET_CHANNEL_RANGE = 0x0002; - - bool m_term; - std::string m_serial; - libusb_device_handle *m_usb_handle; - DmxBuffer m_buffer; - ola::thread::Mutex m_data_mutex; - ola::thread::Mutex m_term_mutex; + class AnymaWidgetInterface* const m_widget; - bool SendDMX(const DmxBuffer &buffer_old); - bool GetDescriptorString(libusb_device_handle *usb_handle, - uint8_t desc_index, - std::string *data); + DISALLOW_COPY_AND_ASSIGN(AnymaOutputPort); }; } // namespace usbdmx } // namespace plugin diff --git a/plugins/usbdmx/AnymaWidget.cpp b/plugins/usbdmx/AnymaWidget.cpp new file mode 100644 index 0000000000..21c27d6053 --- /dev/null +++ b/plugins/usbdmx/AnymaWidget.cpp @@ -0,0 +1,210 @@ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * 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 Library 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * AnymaWidget.cpp + * The synchronous and asynchronous Anyma widgets. + * Copyright (C) 2014 Simon Newton + */ + +#include "plugins/usbdmx/AnymaWidget.h" + +#include +#include "ola/Logging.h" +#include "ola/Constants.h" +#include "plugins/usbdmx/LibUsbHelper.h" + +namespace ola { +namespace plugin { +namespace usbdmx { + +const char AnymaWidgetInterface::EXPECTED_MANUFACTURER[] = "www.anyma.ch"; +const char AnymaWidgetInterface::EXPECTED_PRODUCT[] = "uDMX"; + +namespace { + +static const unsigned int URB_TIMEOUT_MS = 500; +static const unsigned int UDMX_SET_CHANNEL_RANGE = 0x0002; + +/* + * Called by the AsynchronousSunliteWidget when the transfer completes. + */ +void AsyncCallback(struct libusb_transfer *transfer) { + AsynchronousAnymaWidget *widget = + reinterpret_cast(transfer->user_data); + widget->TransferComplete(transfer); +} + +} // namespace + +/* + * Sends messages to a Anyma device in a separate thread. + */ +class AnymaThreadedSender: public ThreadedUsbSender { + public: + AnymaThreadedSender(libusb_device *usb_device, + libusb_device_handle *handle); + + private: + bool TransmitBuffer(libusb_device_handle *handle, + const DmxBuffer &buffer); +}; + +AnymaThreadedSender::AnymaThreadedSender( + libusb_device *usb_device, + libusb_device_handle *usb_handle) + : ThreadedUsbSender(usb_device, usb_handle) { +} + +bool AnymaThreadedSender::TransmitBuffer(libusb_device_handle *handle, + const DmxBuffer &buffer) { + int r = libusb_control_transfer( + handle, + LIBUSB_REQUEST_TYPE_VENDOR | LIBUSB_RECIPIENT_DEVICE | + LIBUSB_ENDPOINT_OUT, // bmRequestType + UDMX_SET_CHANNEL_RANGE, // bRequest + buffer.Size(), // wValue + 0, // wIndex + const_cast(buffer.GetRaw()), // data + buffer.Size(), // wLength + URB_TIMEOUT_MS); // timeout + // Sometimes we get PIPE errors here, those are non-fatal + return r > 0 || r == LIBUSB_ERROR_PIPE; +} + + +SynchronousAnymaWidget::SynchronousAnymaWidget(libusb_device *usb_device) + : m_usb_device(usb_device) { +} + +bool SynchronousAnymaWidget::Init() { + libusb_device_handle *usb_handle; + + bool ok = LibUsbHelper::OpenDeviceAndClaimInterface( + m_usb_device, 0, &usb_handle); + if (!ok) { + return false; + } + + std::auto_ptr sender( + new AnymaThreadedSender(m_usb_device, usb_handle)); + if (!sender->Start()) { + return false; + } + m_sender.reset(sender.release()); + return true; +} + +bool SynchronousAnymaWidget::SendDMX(const DmxBuffer &buffer) { + return m_sender.get() ? m_sender->SendDMX(buffer) : false; +} + +AsynchronousAnymaWidget::AsynchronousAnymaWidget( + libusb_device *usb_device) + : m_usb_device(usb_device), + m_usb_handle(NULL), + m_control_setup_buffer(NULL), + m_transfer_state(IDLE) { + m_control_setup_buffer = + new uint8_t[LIBUSB_CONTROL_SETUP_SIZE + DMX_UNIVERSE_SIZE]; + + m_transfer = libusb_alloc_transfer(0); + libusb_ref_device(usb_device); +} + +AsynchronousAnymaWidget::~AsynchronousAnymaWidget() { + bool canceled = false; + OLA_INFO << "AsynchronousAnymaWidget shutdown"; + while (1) { + ola::thread::MutexLocker locker(&m_mutex); + if (m_transfer_state == IDLE) { + break; + } + if (!canceled) { + libusb_cancel_transfer(m_transfer); + canceled = true; + } + } + + libusb_free_transfer(m_transfer); + delete[] m_control_setup_buffer; + libusb_unref_device(m_usb_device); +} + +bool AsynchronousAnymaWidget::Init() { + bool ok = LibUsbHelper::OpenDeviceAndClaimInterface( + m_usb_device, 0, &m_usb_handle); + if (!ok) { + return false; + } + return true; +} + +bool AsynchronousAnymaWidget::SendDMX(const DmxBuffer &buffer) { + OLA_INFO << "Call to AsynchronousAnymaWidget::SendDMX"; + + if (!m_usb_handle) { + OLA_WARN << "AsynchronousAnymaWidget hasn't been initialized"; + return false; + } + ola::thread::MutexLocker locker(&m_mutex); + if (m_transfer_state != IDLE) { + return true; + } + + libusb_fill_control_setup( + m_control_setup_buffer, + LIBUSB_REQUEST_TYPE_VENDOR | LIBUSB_RECIPIENT_DEVICE | + LIBUSB_ENDPOINT_OUT, // bmRequestType + UDMX_SET_CHANNEL_RANGE, // bRequest + buffer.Size(), // wValue + 0, // wIndex + buffer.Size()); // wLength + + unsigned int length = DMX_UNIVERSE_SIZE; + buffer.Get(m_control_setup_buffer + LIBUSB_CONTROL_SETUP_SIZE, &length); + + libusb_fill_control_transfer( + m_transfer, + m_usb_handle, + m_control_setup_buffer, + &AsyncCallback, + this, + URB_TIMEOUT_MS); + + int ret = libusb_submit_transfer(m_transfer); + if (ret) { + OLA_WARN << "libusb_submit_transfer returned " << libusb_error_name(ret); + return false; + } + OLA_INFO << "submit ok"; + m_transfer_state = IN_PROGRESS; + return true; +} + +void AsynchronousAnymaWidget::TransferComplete( + struct libusb_transfer *transfer) { + if (transfer != m_transfer) { + OLA_WARN << "Mismatched libusb transfer: " << transfer << " != " + << m_transfer; + return; + } + + OLA_INFO << "async transfer complete"; + ola::thread::MutexLocker locker(&m_mutex); + m_transfer_state = IDLE; +} +} // namespace usbdmx +} // namespace plugin +} // namespace ola diff --git a/plugins/usbdmx/AnymaWidget.h b/plugins/usbdmx/AnymaWidget.h new file mode 100644 index 0000000000..3db8da9612 --- /dev/null +++ b/plugins/usbdmx/AnymaWidget.h @@ -0,0 +1,102 @@ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * 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 Library 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * AnymaWidget.h + * The synchronous and asynchronous Anyma widgets. + * Copyright (C) 2014 Simon Newton + */ + +#ifndef PLUGINS_USBDMX_ANYMAWIDGET_H_ +#define PLUGINS_USBDMX_ANYMAWIDGET_H_ + +#include +#include "ola/base/Macro.h" +#include "ola/DmxBuffer.h" +#include "plugins/usbdmx/ThreadedUsbSender.h" + +namespace ola { +namespace plugin { +namespace usbdmx { + +/** + * @brief The interface for the Anyma Widgets + */ +class AnymaWidgetInterface { + public: + virtual ~AnymaWidgetInterface() {} + + virtual bool Init() = 0; + + virtual bool SendDMX(const DmxBuffer &buffer) = 0; + + static const char EXPECTED_MANUFACTURER[]; + static const char EXPECTED_PRODUCT[]; +}; + +/** + * @brief An Anyma widget that uses synchronous libusb operations. + * + * Internally this spawns a new thread to avoid blocking SendDMX() calls. + */ +class SynchronousAnymaWidget: public AnymaWidgetInterface { + public: + explicit SynchronousAnymaWidget(libusb_device *usb_device); + + bool Init(); + + bool SendDMX(const DmxBuffer &buffer); + + private: + libusb_device* const m_usb_device; + std::auto_ptr m_sender; + + DISALLOW_COPY_AND_ASSIGN(SynchronousAnymaWidget); +}; + +/** + * @brief An Anyma widget that uses asynchronous libusb operations. + */ +class AsynchronousAnymaWidget : public AnymaWidgetInterface { + public: + explicit AsynchronousAnymaWidget(libusb_device *usb_device); + ~AsynchronousAnymaWidget(); + + bool Init(); + + bool SendDMX(const DmxBuffer &buffer); + + void TransferComplete(struct libusb_transfer *transfer); + + private: + enum TransferState { + IDLE, + IN_PROGRESS, + }; + + libusb_device* const m_usb_device; + libusb_device_handle *m_usb_handle; + uint8_t *m_control_setup_buffer; + + TransferState m_transfer_state; + ola::thread::Mutex m_mutex; + + struct libusb_transfer *m_transfer; + + DISALLOW_COPY_AND_ASSIGN(AsynchronousAnymaWidget); +}; +} // namespace usbdmx +} // namespace plugin +} // namespace ola +#endif // PLUGINS_USBDMX_ANYMAWIDGET_H_ diff --git a/plugins/usbdmx/AsyncPluginImpl.cpp b/plugins/usbdmx/AsyncPluginImpl.cpp new file mode 100644 index 0000000000..b329ca99e6 --- /dev/null +++ b/plugins/usbdmx/AsyncPluginImpl.cpp @@ -0,0 +1,290 @@ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * 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 Library 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * AsyncPluginImpl.cpp + * The asynchronous libusb implementation. + * Copyright (C) 2014 Simon Newton + */ + +#include "plugins/usbdmx/AsyncPluginImpl.h" + +#include +#include +#include + +#include "ola/Logging.h" +#include "ola/StringUtils.h" +#include "ola/stl/STLUtils.h" +#include "olad/PluginAdaptor.h" + +#include "plugins/usbdmx/AnymaDeviceManager.h" +#include "plugins/usbdmx/SunliteDeviceManager.h" +#include "plugins/usbdmx/EuroliteProDeviceManager.h" + +#include "plugins/usbdmx/VellemanDevice.h" + +namespace ola { +namespace plugin { +namespace usbdmx { + +namespace { + +/* + * Called by libusb when a USB device is added / removed. + */ +int hotplug_callback(OLA_UNUSED struct libusb_context *ctx, + struct libusb_device *dev, + libusb_hotplug_event event, + void *user_data) { + AsyncPluginImpl *plugin = reinterpret_cast(user_data); + plugin->HotPlugEvent(dev, event); + return 0; +} +} // namespace + +class LibUsbThread : private ola::thread::Thread { + public: + explicit LibUsbThread(libusb_context *context) + : m_context(context), + m_term(false), + m_device_count(0) { + } + + #ifdef OLA_LIBUSB_HAS_HOTPLUG_API + void HotPlugStart(); + void HotPlugStop(libusb_hotplug_callback_handle handle); + #endif + + void AddDevice(); + void RemoveDevice(libusb_device_handle *handle); + + void *Run(); + + private: + libusb_context *m_context; + bool m_term; // GUARDED_BY(m_term_mutex) + ola::thread::Mutex m_term_mutex; + unsigned int m_device_count; +}; + +#ifdef OLA_LIBUSB_HAS_HOTPLUG_API +void LibUsbThread::HotPlugStart() { + OLA_INFO << "STarting libusb thread"; + Start(); +} + +void LibUsbThread::HotPlugStop(libusb_hotplug_callback_handle handle) { + OLA_INFO << "stopping libusb thread"; + { + ola::thread::MutexLocker locker(&m_term_mutex); + m_term = true; + } + libusb_hotplug_deregister_callback(m_context, handle); + Join(); +} +#endif + +void LibUsbThread::AddDevice() { + m_device_count++; + if (m_device_count == 1) { + Start(); + } +} + +void LibUsbThread::RemoveDevice(libusb_device_handle *handle) { + if (m_device_count == 1) { + { + ola::thread::MutexLocker locker(&m_term_mutex); + m_term = true; + } + } + libusb_close(handle); + if (m_device_count == 1) { + Join(); + } + m_device_count--; +} + +void *LibUsbThread::Run() { + OLA_INFO << "----libusb event thread is running"; + while (1) { + { + ola::thread::MutexLocker locker(&m_term_mutex); + if (m_term) + break; + } + // TODO(simon): If hotplug isn't active, use libusb_handle_events_timeout + // here. + libusb_handle_events(m_context); + } + OLA_INFO << "----libusb thread exiting"; + return NULL; +} + +AsyncPluginImpl::AsyncPluginImpl(PluginAdaptor *plugin_adaptor, + Plugin *plugin, + LibUsbAdaptor *libusb_adaptor) + : m_libusb_adaptor(libusb_adaptor), + m_context(NULL), + m_use_hotplug(false), + m_stopping(false) { + #ifdef OLA_LIBUSB_HAS_HOTPLUG_API + m_hotplug_handle = 0; + #endif + + m_device_managers.push_back(new AnymaDeviceManager(plugin_adaptor, plugin)); + m_device_managers.push_back(new SunliteDeviceManager(plugin_adaptor, plugin)); + m_device_managers.push_back(new EuroliteProDeviceManager(plugin_adaptor, + plugin)); +} + +AsyncPluginImpl::~AsyncPluginImpl() { + STLDeleteElements(&m_device_managers); +} + +bool AsyncPluginImpl::Start() { + if (libusb_init(&m_context)) { + OLA_WARN << "Failed to init libusb"; + return false; + } + + m_libusb_adaptor->SetDebug(m_context); + + m_use_hotplug = SetupHotPlug(); + OLA_INFO << "SetupHotPlug returned " << m_use_hotplug; + if (m_use_hotplug) { + m_usb_thread.reset((new LibUsbThread(m_context))); + m_usb_thread->HotPlugStart(); + } else { + // Either we don't support hotplug or the setup failed. + // As poor man's hotplug, we call libusb_get_device_list periodically to + // check for new devices. + + /* + m_plugin_adaptor->RegisterRepeatingTimeout( + 3500, + NewSingleCallback(this, &AsyncPluginImpl::FindDevices)); + */ + } + + return true; +} + +bool AsyncPluginImpl::Stop() { + m_stopping = true; + + if (m_usb_thread.get()) { + if (m_use_hotplug) { + m_usb_thread->HotPlugStop(m_hotplug_handle); + } + } + m_usb_thread.reset(); + + // I think we need a lock here + DeviceToFactoryMap::iterator iter = m_device_factory_map.begin(); + for (; iter != m_device_factory_map.end(); ++iter) { + iter->second->DeviceRemoved(iter->first); + } + m_device_factory_map.clear(); + + libusb_exit(m_context); + m_context = NULL; + return true; +} + + +#ifdef OLA_LIBUSB_HAS_HOTPLUG_API +void AsyncPluginImpl::HotPlugEvent(struct libusb_device *usb_device, + libusb_hotplug_event event) { + OLA_INFO << "Got USB hotplug event for " << usb_device << " : " + << (event == LIBUSB_HOTPLUG_EVENT_DEVICE_ARRIVED ? "add" : "del"); + if (event == LIBUSB_HOTPLUG_EVENT_DEVICE_ARRIVED) { + DeviceAdded(usb_device); + } else if (event == LIBUSB_HOTPLUG_EVENT_DEVICE_LEFT) { + DeviceRemoved(usb_device); + } +} +#endif + +bool AsyncPluginImpl::SetupHotPlug() { +#if defined(LIBUSB_API_VERSION) && (LIBUSB_API_VERSION >= 0x01000102) + if (libusb_has_capability(LIBUSB_CAP_HAS_HOTPLUG) == 0) { + return false; + } + + OLA_INFO << "Calling libusb_hotplug_register_callback"; + int rc = libusb_hotplug_register_callback( + NULL, + static_cast(LIBUSB_HOTPLUG_EVENT_DEVICE_ARRIVED | + LIBUSB_HOTPLUG_EVENT_DEVICE_LEFT), + LIBUSB_HOTPLUG_ENUMERATE, LIBUSB_HOTPLUG_MATCH_ANY, + LIBUSB_HOTPLUG_MATCH_ANY, LIBUSB_HOTPLUG_MATCH_ANY, + hotplug_callback, this, &m_hotplug_handle); + + if (LIBUSB_SUCCESS != rc) { + OLA_WARN << "Error creating a hotplug callback"; + return false; + } + OLA_INFO << "libusb_hotplug_register_callback passed"; + return true; +#else + return false; +#endif +} + +/* + * Find known devices & register them + */ +void AsyncPluginImpl::FindDevices() { + libusb_device **device_list; + size_t device_count = libusb_get_device_list(NULL, &device_list); + + for (unsigned int i = 0; i < device_count; i++) { + DeviceAdded(device_list[i]); + } + libusb_free_device_list(device_list, 1); // unref devices +} + +void AsyncPluginImpl::DeviceAdded(libusb_device *usb_device) { + struct libusb_device_descriptor device_descriptor; + libusb_get_device_descriptor(usb_device, &device_descriptor); + + DeviceManagers::iterator iter = m_device_managers.begin(); + for (; iter != m_device_managers.end(); ++iter) { + if ((*iter)->DeviceAdded(usb_device, device_descriptor)) { + STLReplacePtr(&m_device_factory_map, usb_device, *iter); + return; + } + } + + // Old style + /* + if (device_descriptor.idVendor == 0x10cf && + device_descriptor.idProduct == 0x8062) { + OLA_INFO << "Found a Velleman USB device"; + device = new VellemanDevice(m_plugin, usb_device); + */ +} + +void AsyncPluginImpl::DeviceRemoved(libusb_device *usb_device) { + UsbDeviceManagerInterface *factory = STLLookupAndRemovePtr( + &m_device_factory_map, usb_device); + if (factory) { + factory->DeviceRemoved(usb_device); + } +} +} // namespace usbdmx +} // namespace plugin +} // namespace ola diff --git a/plugins/usbdmx/AsyncPluginImpl.h b/plugins/usbdmx/AsyncPluginImpl.h new file mode 100644 index 0000000000..3f696ce623 --- /dev/null +++ b/plugins/usbdmx/AsyncPluginImpl.h @@ -0,0 +1,118 @@ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * 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 Library 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * AsyncPluginImpl.h + * The asynchronous libusb implementation. + * Copyright (C) 2014 Simon Newton + */ + +#ifndef PLUGINS_USBDMX_ASYNCPLUGINIMPL_H_ +#define PLUGINS_USBDMX_ASYNCPLUGINIMPL_H_ + +#include +#include +#include +#include +#include +#include + +#include "ola/base/Macro.h" +#include "ola/thread/Thread.h" +#include "plugins/usbdmx/LibUsbAdaptor.h" +#include "plugins/usbdmx/PluginImplInterface.h" +#include "plugins/usbdmx/UsbDeviceManagerInterface.h" + +namespace ola { +namespace plugin { +namespace usbdmx { + +#if defined(LIBUSB_API_VERSION) && (LIBUSB_API_VERSION >= 0x01000102) +#define OLA_LIBUSB_HAS_HOTPLUG_API +#endif + +/** + * @brief The asynchronous libusb implementation. + */ +class AsyncPluginImpl: public PluginImplInterface { + public: + /** + * @brief Create a new AsyncPluginImpl. + * @param plugin_adaptor The PluginAdaptor to use, ownership is not + * transferred. + * @param preferences The Preference object to use, ownership is not + * transferred. + * @param plugin The parent Plugin object which is used when creating + * devices. + * @param libusb_adaptor The adaptor to use when calling libusb. + */ + AsyncPluginImpl(PluginAdaptor *plugin_adaptor, + Plugin *plugin, + LibUsbAdaptor *libusb_adaptor); + ~AsyncPluginImpl(); + + bool Start(); + bool Stop(); + + #ifdef OLA_LIBUSB_HAS_HOTPLUG_API + /** + * @brief Called when a USB hotplug event occurs. + * @param dev the libusb_device the event occurred for. + * @param event indicates if the device was added or removed. + * + * This can be called from either the thread that called + * Start(), or from the libusb thread. It can't be called from both threads at + * once though, since the libusb thread is only started once the initial call + * to libusb_hotplug_register_callback returns. + */ + void HotPlugEvent(struct libusb_device *dev, + libusb_hotplug_event event); + #endif + + private: + typedef std::vector DeviceManagers; + typedef std::map + DeviceToFactoryMap; + + struct USBDeviceInformation { + std::string manufacturer; + std::string product; + std::string serial; + }; + + LibUsbAdaptor* const m_libusb_adaptor; + libusb_context *m_context; + bool m_use_hotplug; + bool m_stopping; + std::auto_ptr m_usb_thread; + + DeviceManagers m_device_managers; + DeviceToFactoryMap m_device_factory_map; + + #ifdef OLA_LIBUSB_HAS_HOTPLUG_API + libusb_hotplug_callback_handle m_hotplug_handle; + #endif + + bool SetupHotPlug(); + void DeviceAdded(libusb_device *device); + void DeviceRemoved(libusb_device *device); + + void FindDevices(); + + DISALLOW_COPY_AND_ASSIGN(AsyncPluginImpl); +}; +} // namespace usbdmx +} // namespace plugin +} // namespace ola +#endif // PLUGINS_USBDMX_ASYNCPLUGINIMPL_H_ diff --git a/plugins/usbdmx/EuroliteProDevice.cpp b/plugins/usbdmx/EuroliteProDevice.cpp index 70e42b2c56..78290c42a7 100644 --- a/plugins/usbdmx/EuroliteProDevice.cpp +++ b/plugins/usbdmx/EuroliteProDevice.cpp @@ -19,42 +19,28 @@ * Eurolite Pro USB DMX ArtNo. 51860120 */ -#include +#include "plugins/usbdmx/EuroliteProDevice.h" +#include #include "ola/Logging.h" -#include "plugins/usbdmx/EuroliteProDevice.h" +#include "plugins/usbdmx/EuroliteProOutputPort.h" namespace ola { namespace plugin { namespace usbdmx { -using std::string; +EuroliteProDevice::EuroliteProDevice(ola::AbstractPlugin *owner, + EuroliteProWidgetInterface *widget, + const std::string &serial) + : Device(owner, "EurolitePro USB Device"), + m_device_id("eurolite-" + serial), + m_port(new EuroliteProOutputPort(this, 0, widget)) { +} -/* - * Start this device. - */ bool EuroliteProDevice::StartHook() { - m_output_port = new EuroliteProOutputPort(this, 0, m_usb_device); - if (!m_output_port->Start()) { - delete m_output_port; - m_output_port = NULL; - return false; - } - AddPort(m_output_port); + AddPort(m_port.release()); return true; } - - -/* - * Get the device id - */ -string EuroliteProDevice::DeviceId() const { - if (m_output_port) { - return "eurolite-" + m_output_port->SerialNumber(); - } else { - return ""; - } -} } // namespace usbdmx } // namespace plugin } // namespace ola diff --git a/plugins/usbdmx/EuroliteProDevice.h b/plugins/usbdmx/EuroliteProDevice.h index 3a36cb232f..f9ef4f962d 100644 --- a/plugins/usbdmx/EuroliteProDevice.h +++ b/plugins/usbdmx/EuroliteProDevice.h @@ -22,33 +22,36 @@ #ifndef PLUGINS_USBDMX_EUROLITEPRODEVICE_H_ #define PLUGINS_USBDMX_EUROLITEPRODEVICE_H_ -#include +#include #include -#include "plugins/usbdmx/UsbDevice.h" -#include "plugins/usbdmx/EuroliteProOutputPort.h" +#include "ola/base/Macro.h" +#include "olad/Device.h" namespace ola { namespace plugin { namespace usbdmx { -/* - * A EurolitePro device +/** + * @brief An EurolitePro device. */ -class EuroliteProDevice: public UsbDevice { +class EuroliteProDevice: public Device { public: - EuroliteProDevice(ola::AbstractPlugin *owner, - libusb_device *usb_device): - UsbDevice(owner, "EurolitePro USB Device", usb_device), - m_output_port(NULL) { - } + EuroliteProDevice(ola::AbstractPlugin *owner, + class EuroliteProWidgetInterface *widget, + const std::string &serial); - std::string DeviceId() const; + std::string DeviceId() const { + return m_device_id; + } protected: - bool StartHook(); + bool StartHook(); private: - EuroliteProOutputPort *m_output_port; + const std::string m_device_id; + std::auto_ptr m_port; + + DISALLOW_COPY_AND_ASSIGN(EuroliteProDevice); }; } // namespace usbdmx } // namespace plugin diff --git a/plugins/usbdmx/EuroliteProDeviceManager.cpp b/plugins/usbdmx/EuroliteProDeviceManager.cpp new file mode 100644 index 0000000000..cfdee67a92 --- /dev/null +++ b/plugins/usbdmx/EuroliteProDeviceManager.cpp @@ -0,0 +1,88 @@ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * 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 Library 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * EuroliteProDeviceManager.cpp + * The EurolitePro Device Manager + * Copyright (C) 2014 Simon Newton + */ + +#include "plugins/usbdmx/EuroliteProDeviceManager.h" + +#include "ola/Logging.h" +#include "plugins/usbdmx/EuroliteProDevice.h" +#include "plugins/usbdmx/EuroliteProWidget.h" +#include "plugins/usbdmx/LibUsbHelper.h" + +namespace ola { +namespace plugin { +namespace usbdmx { + +const uint16_t EuroliteProDeviceManager::EUROLITE_PRODUCT_ID = 0xfa63; +const uint16_t EuroliteProDeviceManager::EUROLITE_VENDOR_ID = 0x04d; + +bool EuroliteProDeviceManager::DeviceAdded( + libusb_device *usb_device, + const struct libusb_device_descriptor &descriptor) { + if (descriptor.idVendor != EUROLITE_VENDOR_ID || + descriptor.idProduct != EUROLITE_PRODUCT_ID || + HasDevice(usb_device)) { + return false; + } + + OLA_INFO << "Found a new EurolitePro device"; + LibUsbHelper::DeviceInformation info; + if (!LibUsbHelper::GetDeviceInfo(usb_device, descriptor, &info)) { + return false; + } + + if (!LibUsbHelper::CheckManufacturer( + EuroliteProWidgetInterface::EXPECTED_MANUFACTURER, info.manufacturer)) { + return false; + } + + if (!LibUsbHelper::CheckProduct(EuroliteProWidgetInterface::EXPECTED_PRODUCT, + info.product)) { + return false; + } + + // The Eurolite doesn't have a serial number, so instead we use the device & + // bus number. + // TODO(simon): check if this supports the SERIAL NUMBER label and use that + // instead. + + // There is no Serialnumber--> work around: bus+device number + int bus_number = libusb_get_bus_number(usb_device); + int device_address = libusb_get_device_address(usb_device); + + OLA_INFO << "Bus_number: " << bus_number << ", Device_address: " << + device_address; + + std::ostringstream serial_str; + serial_str << bus_number << "-" << device_address; + + AsynchronousEuroliteProWidget *widget = new AsynchronousEuroliteProWidget( + usb_device); + if (!widget->Init()) { + delete widget; + return false; + } + + EuroliteProDevice *device = new EuroliteProDevice(ParentPlugin(), widget, + serial_str.str()); + return RegisterDevice(usb_device, device); +} +} // namespace usbdmx +} // namespace plugin +} // namespace ola diff --git a/plugins/usbdmx/EuroliteProDeviceManager.h b/plugins/usbdmx/EuroliteProDeviceManager.h new file mode 100644 index 0000000000..7b6daf630e --- /dev/null +++ b/plugins/usbdmx/EuroliteProDeviceManager.h @@ -0,0 +1,55 @@ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * 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 Library 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * EuroliteProDeviceManager.h + * The EurolitePro Device Manager. + * Copyright (C) 2014 Simon Newton + */ + +#ifndef PLUGINS_USBDMX_EUROLITEPRODEVICEMANAGER_H_ +#define PLUGINS_USBDMX_EUROLITEPRODEVICEMANAGER_H_ + +#include "ola/base/Macro.h" +#include "plugins/usbdmx/UsbDeviceManagerInterface.h" +#include "plugins/usbdmx/EuroliteProDevice.h" + +namespace ola { +namespace plugin { +namespace usbdmx { + +/** + * @brief Manages EurolitePro Devices + */ +class EuroliteProDeviceManager : public BaseDeviceFactory { + public: + EuroliteProDeviceManager(PluginAdaptor *plugin_adaptor, + Plugin *plugin) + : BaseDeviceFactory(plugin_adaptor, plugin) { + } + + bool DeviceAdded( + libusb_device *device, + const struct libusb_device_descriptor &descriptor); + + private: + static const uint16_t EUROLITE_PRODUCT_ID; + static const uint16_t EUROLITE_VENDOR_ID; + + DISALLOW_COPY_AND_ASSIGN(EuroliteProDeviceManager); +}; +} // namespace usbdmx +} // namespace plugin +} // namespace ola +#endif // PLUGINS_USBDMX_EUROLITEPRODEVICEMANAGER_H_ diff --git a/plugins/usbdmx/EuroliteProOutputPort.cpp b/plugins/usbdmx/EuroliteProOutputPort.cpp index 11c6bb4ab4..1b5bda6992 100644 --- a/plugins/usbdmx/EuroliteProOutputPort.cpp +++ b/plugins/usbdmx/EuroliteProOutputPort.cpp @@ -16,288 +16,37 @@ * EuroliteProOutputPort.cpp * Thread for the EurolitePro Output Port * Copyright (C) 2011 Simon Newton & Harry F - * Eurolite Pro USB DMX ArtNo. 51860120 + * Eurolite Pro USB DMX ArtNo. 51860120 */ -#include -#include +#include "plugins/usbdmx/EuroliteProOutputPort.h" -#include "ola/Constants.h" #include "ola/Logging.h" -#include "plugins/usbdmx/EuroliteProOutputPort.h" #include "plugins/usbdmx/EuroliteProDevice.h" +#include "plugins/usbdmx/EuroliteProWidget.h" namespace ola { namespace plugin { namespace usbdmx { -using std::string; - -const char EuroliteProOutputPort::EXPECTED_MANUFACTURER[] = "Eurolite"; -const char EuroliteProOutputPort::EXPECTED_PRODUCT[] = "Eurolite DMX512 Pro"; -const uint8_t EuroliteProOutputPort::DMX_LABEL; -const unsigned char EuroliteProOutputPort::ENDPOINT; -const unsigned int EuroliteProOutputPort::UDMX_SET_CHANNEL_RANGE; -const unsigned int EuroliteProOutputPort::URB_TIMEOUT_MS; - -/* - * Create a new EuroliteProOutputPort object - */ EuroliteProOutputPort::EuroliteProOutputPort(EuroliteProDevice *parent, - unsigned int id, - libusb_device *usb_device) + unsigned int id, + EuroliteProWidgetInterface *widget) : BasicOutputPort(parent, id), - m_term(false), - m_serial(""), - m_usb_device(usb_device), - m_usb_handle(NULL) { + m_widget(widget) { } - -/* - * Cleanup - */ EuroliteProOutputPort::~EuroliteProOutputPort() { - { - ola::thread::MutexLocker locker(&m_term_mutex); - m_term = true; - } - Join(); + // TODO(simon): stop the thread here?? + OLA_INFO << "EuroliteProOutputPort::~EuroliteProOutputPort()"; + delete m_widget; } - -/* - * Start this thread - */ -bool EuroliteProOutputPort::Start() { - libusb_device_handle *usb_handle; - struct libusb_device_descriptor device_descriptor; - libusb_get_device_descriptor(m_usb_device, &device_descriptor); - - if (libusb_open(m_usb_device, &usb_handle)) { - OLA_WARN << "Failed to open Eurolite usb device"; - return false; - } - - string data; - if (!GetDescriptorString(usb_handle, device_descriptor.iManufacturer, - &data)) { - OLA_INFO << "Failed to get manufacturer name"; - libusb_close(usb_handle); - return false; - } - - if (data != EXPECTED_MANUFACTURER) { - OLA_INFO << "Manufacturer mismatch: " << EXPECTED_MANUFACTURER << " != " << - data; - libusb_close(usb_handle); - return false; - } - - if (!GetDescriptorString(usb_handle, device_descriptor.iProduct, &data)) { - OLA_INFO << "Failed to get product name"; - libusb_close(usb_handle); - return false; - } - - if (data != EXPECTED_PRODUCT) { - OLA_INFO << "Product mismatch: " << EXPECTED_PRODUCT << " != " << data; - libusb_close(usb_handle); - return false; - } - - bool ok = LocateInterface(); - - if (!ok) { - libusb_close(usb_handle); - return false; - } - - // The Eurolite doesn't have a serial number, so instead we use the device & - // bus number. - // TODO(simon): check if this supports the SERIAL NUMBER label and use that - // instead. - - // There is no Serialnumber--> work around: bus+device number - int bus_number = libusb_get_bus_number(m_usb_device); - int device_address = libusb_get_device_address(m_usb_device); - - OLA_INFO << "Bus_number: " << bus_number << ", Device_address: " << - device_address; - - std::ostringstream str; - str << bus_number << "-" << device_address; - m_serial = str.str(); - - int error = libusb_claim_interface(usb_handle, m_interface_number); - - if (error) { - if (error == LIBUSB_ERROR_BUSY) { - OLA_WARN << "Eurolite device in use by another program"; - } else { - OLA_WARN << "Failed to claim Eurolite usb interface, error: " << error; - } - libusb_close(usb_handle); - return false; - } - - m_usb_handle = usb_handle; - bool ret = ola::thread::Thread::Start(); - if (!ret) { - OLA_WARN << "pthread create failed"; - libusb_release_interface(m_usb_handle, m_interface_number); - libusb_close(usb_handle); - return false; - } - return true; -} - - -/* - * The main loop for the sender thread. - */ -void *EuroliteProOutputPort::Run() { - DmxBuffer buffer; - - if (!m_usb_handle) - return NULL; - - while (1) { - { - ola::thread::MutexLocker locker(&m_term_mutex); - if (m_term) - break; - } - - { - ola::thread::MutexLocker locker(&m_data_mutex); - buffer.Set(m_buffer); - } - - if (buffer.Size()) { - if (!SendDMX(buffer)) { - OLA_WARN << "Send bufferfailed, stopping thread..."; - break; - } - } else { - // sleep for a bit - usleep(40000); - } - } - libusb_release_interface(m_usb_handle, m_interface_number); - libusb_close(m_usb_handle); - return NULL; -} - - -/* - * Store the data in the shared buffer - */ bool EuroliteProOutputPort::WriteDMX(const DmxBuffer &buffer, - uint8_t priority) { - ola::thread::MutexLocker locker(&m_data_mutex); - m_buffer.Set(buffer); - return true; - (void) priority; -} - - -/* - * Send the dmx out the widget - * @return true on success, false on failure - */ -bool EuroliteProOutputPort::SendDMX(const DmxBuffer &buffer) { - uint8_t usb_data[FRAME_SIZE]; - unsigned int frame_size = buffer.Size(); - - // header - usb_data[0] = 0x7E; // Start message delimiter - usb_data[1] = DMX_LABEL; // Label - usb_data[4] = DMX512_START_CODE; - buffer.Get(usb_data + 5, &frame_size); - usb_data[2] = (DMX_UNIVERSE_SIZE + 1) & 0xff; // Data length LSB. - usb_data[3] = ((DMX_UNIVERSE_SIZE + 1) >> 8); // Data length MSB - memset(usb_data + 5 + frame_size, 0, DMX_UNIVERSE_SIZE - frame_size); - usb_data[FRAME_SIZE - 1] = 0xE7; // End message delimiter - - int transferred = 0; - int ret = libusb_bulk_transfer( - m_usb_handle, - ENDPOINT, - usb_data, - FRAME_SIZE, - &transferred, - URB_TIMEOUT_MS); - - if (ret) - OLA_INFO << "return code was: " << ret << ", transferred bytes " << - transferred; - return ret == 0; -} - - -/* - * Return a string descriptor - * @param usb_handle the usb handle to the device - * @param desc_index the index of the descriptor - * @param data where to store the output string - * @returns true if we got the value, false otherwise - */ -bool EuroliteProOutputPort::GetDescriptorString( - libusb_device_handle *usb_handle, - uint8_t desc_index, - string *data) { - enum { buffer_size = 32 }; - unsigned char buffer[buffer_size]; - int r = libusb_get_string_descriptor_ascii( - usb_handle, - desc_index, - buffer, - buffer_size); - - if (r <= 0) - return false; - data->assign(reinterpret_cast(buffer)); + OLA_UNUSED uint8_t priority) { + m_widget->SendDMX(buffer); return true; } - - -/** - * Find the interface with the endpoint we're after. Usually this is interface - * 1 but we check them all just in case. - */ -bool EuroliteProOutputPort::LocateInterface() { - struct libusb_config_descriptor *device_config; - if (libusb_get_config_descriptor(m_usb_device, 0, &device_config) != 0) { - OLA_WARN << "Failed to get device config descriptor"; - return false; - } - - OLA_DEBUG << static_cast(device_config->bNumInterfaces) << - " interfaces found"; - for (unsigned int i = 0; i < device_config->bNumInterfaces; i++) { - const struct libusb_interface *interface = &device_config->interface[i]; - for (int j = 0; j < interface->num_altsetting; j++) { - const struct libusb_interface_descriptor *iface_descriptor = - &interface->altsetting[j]; - for (uint8_t k = 0; k < iface_descriptor->bNumEndpoints; k++) { - const struct libusb_endpoint_descriptor *endpoint = - &iface_descriptor->endpoint[k]; - OLA_DEBUG << "Interface " << i << ", altsetting " << j << ", endpoint " - << static_cast(k) << ", endpoint address 0x" << std::hex << - static_cast(endpoint->bEndpointAddress); - if (endpoint->bEndpointAddress == ENDPOINT) { - OLA_INFO << "Using interface " << i; - m_interface_number = i; - libusb_free_config_descriptor(device_config); - return true; - } - } - } - } - libusb_free_config_descriptor(device_config); - return false; -} } // namespace usbdmx } // namespace plugin } // namespace ola diff --git a/plugins/usbdmx/EuroliteProOutputPort.h b/plugins/usbdmx/EuroliteProOutputPort.h index e5d878428d..9d0fa974f6 100644 --- a/plugins/usbdmx/EuroliteProOutputPort.h +++ b/plugins/usbdmx/EuroliteProOutputPort.h @@ -16,64 +16,44 @@ * EuroliteProOutputPort.h * The output port for a EurolitePro device. * Copyright (C) 2011 Simon Newton & Harry F - * Eurolite Pro USB DMX ArtNo. 51860120 + * Eurolite Pro USB DMX ArtNo. 51860120 */ #ifndef PLUGINS_USBDMX_EUROLITEPROOUTPUTPORT_H_ #define PLUGINS_USBDMX_EUROLITEPROOUTPUTPORT_H_ -#include #include +#include "ola/base/Macro.h" #include "ola/DmxBuffer.h" -#include "ola/thread/Thread.h" #include "olad/Port.h" namespace ola { namespace plugin { namespace usbdmx { +class EuroliteProDevice; -class EuroliteProOutputPort: public BasicOutputPort, ola::thread::Thread { +class EuroliteProOutputPort: public BasicOutputPort { public: - EuroliteProOutputPort(class EuroliteProDevice *parent, - unsigned int id, - libusb_device *usb_device); - ~EuroliteProOutputPort(); - std::string SerialNumber() const { return m_serial; } + /** + * @brief Create a new AnymaOutputPort. + */ + EuroliteProOutputPort(class EuroliteProDevice *parent, + unsigned int id, + class EuroliteProWidgetInterface *widget); - bool Start(); - void *Run(); + /** + * @brief Cleanup. + */ + ~EuroliteProOutputPort(); - bool WriteDMX(const DmxBuffer &buffer, uint8_t priority); - std::string Description() const { return ""; } + bool WriteDMX(const DmxBuffer &buffer, uint8_t priority); + std::string Description() const { return ""; } private: - static const unsigned int URB_TIMEOUT_MS = 500; - static const unsigned int UDMX_SET_CHANNEL_RANGE = 0x0002; - static const unsigned char ENDPOINT = 0x02; - static const char EXPECTED_MANUFACTURER[]; - static const char EXPECTED_PRODUCT[]; - static const uint8_t DMX_LABEL = 6; + class EuroliteProWidgetInterface* const m_widget; - bool m_term; - int m_interface_number; - std::string m_serial; - - libusb_device *m_usb_device; - libusb_device_handle *m_usb_handle; - DmxBuffer m_buffer; - ola::thread::Mutex m_data_mutex; - ola::thread::Mutex m_term_mutex; - - bool SendDMX(const DmxBuffer &buffer_old); - - bool GetDescriptorString(libusb_device_handle *usb_handle, - uint8_t desc_index, - std::string *data); - bool LocateInterface(); - - // 513 + header + code + size(2) + footer - enum { FRAME_SIZE = 518 }; + DISALLOW_COPY_AND_ASSIGN(EuroliteProOutputPort); }; } // namespace usbdmx } // namespace plugin diff --git a/plugins/usbdmx/EuroliteProWidget.cpp b/plugins/usbdmx/EuroliteProWidget.cpp new file mode 100644 index 0000000000..b81d00fb06 --- /dev/null +++ b/plugins/usbdmx/EuroliteProWidget.cpp @@ -0,0 +1,272 @@ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * 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 Library 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * EuroliteProWidget.cpp + * The synchronous and asynchronous EurolitePro widgets. + * Copyright (C) 2014 Simon Newton + */ + +#include "plugins/usbdmx/EuroliteProWidget.h" + +#include "ola/Constants.h" +#include "ola/Logging.h" +#include "plugins/usbdmx/LibUsbHelper.h" + +namespace ola { +namespace plugin { +namespace usbdmx { + +const char EuroliteProWidgetInterface::EXPECTED_MANUFACTURER[] = "Eurolite"; +const char EuroliteProWidgetInterface::EXPECTED_PRODUCT[] = + "Eurolite DMX512 Pro"; + +namespace { + +// Why is this so long? +static const unsigned int URB_TIMEOUT_MS = 500; +static const uint8_t DMX_LABEL = 6; +static const unsigned char ENDPOINT = 0x02; + +/* + * Called by the AsynchronousEuroliteProWidget when the transfer completes. + */ +void AsyncCallback(struct libusb_transfer *transfer) { + AsynchronousEuroliteProWidget *widget = + reinterpret_cast(transfer->user_data); + widget->TransferComplete(transfer); +} + +/* + * Create a Eurolite Pro message to match the supplied DmxBuffer. + */ +void CreateFrame( + const DmxBuffer &buffer, + uint8_t frame[EuroliteProWidgetInterface::EUROLITE_PRO_FRAME_SIZE]) { + unsigned int frame_size = buffer.Size(); + + // header + frame[0] = 0x7E; // Start message delimiter + frame[1] = DMX_LABEL; // Label + frame[4] = DMX512_START_CODE; + buffer.Get(frame + 5, &frame_size); + frame[2] = (DMX_UNIVERSE_SIZE + 1) & 0xff; // Data length LSB. + frame[3] = ((DMX_UNIVERSE_SIZE + 1) >> 8); // Data length MSB + memset(frame + 5 + frame_size, 0, DMX_UNIVERSE_SIZE - frame_size); + // End message delimiter + + frame[EuroliteProWidgetInterface::EUROLITE_PRO_FRAME_SIZE - 1] = 0xE7; +} + +/** + * Find the interface with the endpoint we're after. Usually this is interface + * 1 but we check them all just in case. + */ +bool LocateInterface(libusb_device *usb_device, + int *interface_number) { + struct libusb_config_descriptor *device_config; + if (libusb_get_config_descriptor(usb_device, 0, &device_config) != 0) { + OLA_WARN << "Failed to get device config descriptor"; + return false; + } + + OLA_DEBUG << static_cast(device_config->bNumInterfaces) << + " interfaces found"; + for (unsigned int i = 0; i < device_config->bNumInterfaces; i++) { + const struct libusb_interface *interface = &device_config->interface[i]; + for (int j = 0; j < interface->num_altsetting; j++) { + const struct libusb_interface_descriptor *iface_descriptor = + &interface->altsetting[j]; + for (uint8_t k = 0; k < iface_descriptor->bNumEndpoints; k++) { + const struct libusb_endpoint_descriptor *endpoint = + &iface_descriptor->endpoint[k]; + OLA_DEBUG << "Interface " << i << ", altsetting " << j << ", endpoint " + << static_cast(k) << ", endpoint address 0x" << std::hex << + static_cast(endpoint->bEndpointAddress); + if (endpoint->bEndpointAddress == ENDPOINT) { + OLA_INFO << "Using interface " << i; + *interface_number = i; + libusb_free_config_descriptor(device_config); + return true; + } + } + } + } + OLA_WARN << "Failed to locate endpoint for EurolitePro device."; + libusb_free_config_descriptor(device_config); + return false; +} +} // namespace + +/* + * Sends messages to a EurolitePro device in a separate thread. + */ +class EuroliteProThreadedSender: public ThreadedUsbSender { + public: + EuroliteProThreadedSender(libusb_device *usb_device, + libusb_device_handle *handle); + + private: + bool TransmitBuffer(libusb_device_handle *handle, + const DmxBuffer &buffer); +}; + +EuroliteProThreadedSender::EuroliteProThreadedSender( + libusb_device *usb_device, + libusb_device_handle *usb_handle) + : ThreadedUsbSender(usb_device, usb_handle) { +} + +bool EuroliteProThreadedSender::TransmitBuffer(libusb_device_handle *handle, + const DmxBuffer &buffer) { + uint8_t frame[EuroliteProWidgetInterface::EUROLITE_PRO_FRAME_SIZE]; + CreateFrame(buffer, frame); + + int transferred; + int r = libusb_bulk_transfer( + handle, + ENDPOINT, + frame, + EuroliteProWidgetInterface::EUROLITE_PRO_FRAME_SIZE, + &transferred, + URB_TIMEOUT_MS); + if (transferred != EuroliteProWidgetInterface::EUROLITE_PRO_FRAME_SIZE) { + // not sure if this is fatal or not + OLA_WARN << "EurolitePro driver failed to transfer all data"; + } + return r == 0; +} + +SynchronousEuroliteProWidget::SynchronousEuroliteProWidget( + libusb_device *usb_device) + : m_usb_device(usb_device) { +} + +bool SynchronousEuroliteProWidget::Init() { + libusb_device_handle *usb_handle; + + int interface_number; + if (!LocateInterface(m_usb_device, &interface_number)) { + return false; + } + + bool ok = LibUsbHelper::OpenDeviceAndClaimInterface( + m_usb_device, interface_number, &usb_handle); + if (!ok) { + return false; + } + + std::auto_ptr sender( + new EuroliteProThreadedSender(m_usb_device, usb_handle)); + if (!sender->Start()) { + return false; + } + m_sender.reset(sender.release()); + return true; +} + +bool SynchronousEuroliteProWidget::SendDMX(const DmxBuffer &buffer) { + return m_sender.get() ? m_sender->SendDMX(buffer) : false; +} + +AsynchronousEuroliteProWidget::AsynchronousEuroliteProWidget( + libusb_device *usb_device) + : m_usb_device(usb_device), + m_usb_handle(NULL), + m_transfer_state(IDLE) { + m_transfer = libusb_alloc_transfer(0); + libusb_ref_device(usb_device); +} + +AsynchronousEuroliteProWidget::~AsynchronousEuroliteProWidget() { + bool canceled = false; + OLA_INFO << "AsynchronousEuroliteProWidget shutdown"; + while (1) { + ola::thread::MutexLocker locker(&m_mutex); + if (m_transfer_state == IDLE) { + break; + } + if (!canceled) { + libusb_cancel_transfer(m_transfer); + canceled = true; + } + } + + libusb_free_transfer(m_transfer); + libusb_unref_device(m_usb_device); +} + +bool AsynchronousEuroliteProWidget::Init() { + int interface_number; + if (!LocateInterface(m_usb_device, &interface_number)) { + return false; + } + + bool ok = LibUsbHelper::OpenDeviceAndClaimInterface( + m_usb_device, 0, &m_usb_handle); + if (!ok) { + return false; + } + return true; +} + +bool AsynchronousEuroliteProWidget::SendDMX(const DmxBuffer &buffer) { + OLA_INFO << "Call to AsynchronousEuroliteProWidget::SendDMX"; + if (!m_usb_handle) { + OLA_WARN << "AsynchronousEuroliteProWidget hasn't been initialized"; + return false; + } + + ola::thread::MutexLocker locker(&m_mutex); + if (m_transfer_state != IDLE) { + return true; + } + + CreateFrame(buffer, m_tx_frame); + + libusb_fill_bulk_transfer( + m_transfer, + m_usb_handle, + ENDPOINT, + m_tx_frame, + EUROLITE_PRO_FRAME_SIZE, + &AsyncCallback, + this, + URB_TIMEOUT_MS); + + int ret = libusb_submit_transfer(m_transfer); + if (ret) { + OLA_WARN << "libusb_submit_transfer returned " << libusb_error_name(ret); + return false; + } + OLA_INFO << "submit ok"; + m_transfer_state = IN_PROGRESS; + return true; +} + +void AsynchronousEuroliteProWidget::TransferComplete( + struct libusb_transfer *transfer) { + if (transfer != m_transfer) { + OLA_WARN << "Mismatched libusb transfer: " << transfer << " != " + << m_transfer; + return; + } + + OLA_INFO << "async transfer complete"; + ola::thread::MutexLocker locker(&m_mutex); + m_transfer_state = IDLE; +} +} // namespace usbdmx +} // namespace plugin +} // namespace ola diff --git a/plugins/usbdmx/EuroliteProWidget.h b/plugins/usbdmx/EuroliteProWidget.h new file mode 100644 index 0000000000..d973cfe069 --- /dev/null +++ b/plugins/usbdmx/EuroliteProWidget.h @@ -0,0 +1,109 @@ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * 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 Library 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * EuroliteProWidget.h + * The synchronous and asynchronous EurolitePro widgets. + * Copyright (C) 2014 Simon Newton + */ + +#ifndef PLUGINS_USBDMX_EUROLITEPROWIDGET_H_ +#define PLUGINS_USBDMX_EUROLITEPROWIDGET_H_ + +#include +#include "ola/base/Macro.h" +#include "ola/DmxBuffer.h" +#include "plugins/usbdmx/ThreadedUsbSender.h" + +namespace ola { +namespace plugin { +namespace usbdmx { + +class EuroliteProThreadedSender; + +/** + * @brief The interface for the EurolitePro Widgets + */ +class EuroliteProWidgetInterface { + public: + virtual ~EuroliteProWidgetInterface() {} + + virtual bool Init() = 0; + + virtual bool SendDMX(const DmxBuffer &buffer) = 0; + + static const char EXPECTED_MANUFACTURER[]; + static const char EXPECTED_PRODUCT[]; + + // 513 + header + code + size(2) + footer + enum { EUROLITE_PRO_FRAME_SIZE = 518 }; +}; + + +/** + * @brief An EurolitePro widget that uses synchronous libusb operations. + * + * Internally this spawns a new thread to avoid blocking SendDMX() calls. + */ +class SynchronousEuroliteProWidget: public EuroliteProWidgetInterface { + public: + explicit SynchronousEuroliteProWidget(libusb_device *usb_device); + + bool Init(); + + bool SendDMX(const DmxBuffer &buffer); + + private: + libusb_device* const m_usb_device; + std::auto_ptr m_sender; + + DISALLOW_COPY_AND_ASSIGN(SynchronousEuroliteProWidget); +}; + +/** + * @brief An EurolitePro widget that uses asynchronous libusb operations. + */ +class AsynchronousEuroliteProWidget: public EuroliteProWidgetInterface { + public: + explicit AsynchronousEuroliteProWidget(libusb_device *usb_device); + ~AsynchronousEuroliteProWidget(); + + bool Init(); + + bool SendDMX(const DmxBuffer &buffer); + + void TransferComplete(struct libusb_transfer *transfer); + + private: + enum TransferState { + IDLE, + IN_PROGRESS, + }; + + libusb_device* const m_usb_device; + libusb_device_handle *m_usb_handle; + + TransferState m_transfer_state; + ola::thread::Mutex m_mutex; + + struct libusb_transfer *m_transfer; + + uint8_t m_tx_frame[EUROLITE_PRO_FRAME_SIZE]; + + DISALLOW_COPY_AND_ASSIGN(AsynchronousEuroliteProWidget); +}; +} // namespace usbdmx +} // namespace plugin +} // namespace ola +#endif // PLUGINS_USBDMX_EUROLITEPROWIDGET_H_ diff --git a/plugins/usbdmx/FirmwareLoader.h b/plugins/usbdmx/FirmwareLoader.h index adb517d45c..ab7a00521f 100644 --- a/plugins/usbdmx/FirmwareLoader.h +++ b/plugins/usbdmx/FirmwareLoader.h @@ -27,10 +27,10 @@ namespace usbdmx { class FirmwareLoader { public: - FirmwareLoader() {} - virtual ~FirmwareLoader() {} + FirmwareLoader() {} + virtual ~FirmwareLoader() {} - virtual bool LoadFirmware() = 0; + virtual bool LoadFirmware() = 0; }; } // namespace usbdmx } // namespace plugin diff --git a/plugins/usbdmx/LibUsbAdaptor.cpp b/plugins/usbdmx/LibUsbAdaptor.cpp new file mode 100644 index 0000000000..8ea5682476 --- /dev/null +++ b/plugins/usbdmx/LibUsbAdaptor.cpp @@ -0,0 +1,36 @@ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * 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 Library 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * LibUsbAdaptor.h + * The wrapper around libusb that abstracts syncronous vs asyncronous calls. + * Copyright (C) 2014 Simon Newton + */ + +#include "plugins/usbdmx/LibUsbAdaptor.h" + +#include +#include + +namespace ola { +namespace plugin { +namespace usbdmx { + +void LibUsbAdaptor::SetDebug(libusb_context *context) { + OLA_DEBUG << "libusb debug level set to " << m_debug_level; + libusb_set_debug(context, m_debug_level); +} +} // namespace usbdmx +} // namespace plugin +} // namespace ola diff --git a/plugins/usbdmx/LibUsbAdaptor.h b/plugins/usbdmx/LibUsbAdaptor.h new file mode 100644 index 0000000000..871528320f --- /dev/null +++ b/plugins/usbdmx/LibUsbAdaptor.h @@ -0,0 +1,47 @@ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * 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 Library 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * LibUsbAdaptor.h + * The wrapper around libusb that abstracts syncronous vs asyncronous calls. + * Copyright (C) 2014 Simon Newton + */ + +#ifndef PLUGINS_USBDMX_LIBUSBADAPTOR_H_ +#define PLUGINS_USBDMX_LIBUSBADAPTOR_H_ + +#include +#include "ola/base/Macro.h" + +namespace ola { +namespace plugin { +namespace usbdmx { + +class LibUsbAdaptor { + public: + explicit LibUsbAdaptor(unsigned int debug_level) + : m_debug_level(debug_level) { + } + + void SetDebug(libusb_context *context); + + private: + unsigned int m_debug_level; + + DISALLOW_COPY_AND_ASSIGN(LibUsbAdaptor); +}; +} // namespace usbdmx +} // namespace plugin +} // namespace ola +#endif // PLUGINS_USBDMX_LIBUSBADAPTOR_H_ diff --git a/plugins/usbdmx/LibUsbHelper.cpp b/plugins/usbdmx/LibUsbHelper.cpp new file mode 100644 index 0000000000..3bbdbab861 --- /dev/null +++ b/plugins/usbdmx/LibUsbHelper.cpp @@ -0,0 +1,126 @@ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * 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 Library 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * LibUsbHelper.cpp + * Helper methods for libusb device enumeration. + * Copyright (C) 2014 Simon Newton + */ +#include "plugins/usbdmx/LibUsbHelper.h" + +#include +#include +#include "ola/Logging.h" + +namespace ola { +namespace plugin { +namespace usbdmx { + +using std::string; + +namespace { +bool GetDescriptorString(libusb_device_handle *usb_handle, + uint8_t desc_index, + string *data) { + enum { buffer_size = 32 }; // static arrays FTW! + unsigned char buffer[buffer_size]; + int r = libusb_get_string_descriptor_ascii( + usb_handle, + desc_index, + buffer, + buffer_size); + + if (r <= 0) { + OLA_INFO << "libusb_get_string_descriptor_ascii returned " << r; + return false; + } + data->assign(reinterpret_cast(buffer)); + return true; +} +} // namespace + +bool LibUsbHelper::GetDeviceInfo( + struct libusb_device *usb_device, + const struct libusb_device_descriptor &device_descriptor, + DeviceInformation *device_info) { + libusb_device_handle *usb_handle; + if (!OpenDevice(usb_device, &usb_handle)) { + return false; + } + + if (!GetDescriptorString(usb_handle, device_descriptor.iManufacturer, + &device_info->manufacturer)) { + OLA_INFO << "Failed to get manufacturer name"; + } + + if (!GetDescriptorString(usb_handle, device_descriptor.iProduct, + &device_info->product)) { + OLA_INFO << "Failed to get product name"; + } + + if (!GetDescriptorString(usb_handle, device_descriptor.iSerialNumber, + &device_info->serial)) { + OLA_WARN << "Failed to read serial number, the device probably doesn't " + << "have one"; + } + + libusb_close(usb_handle); + return true; +} + +bool LibUsbHelper::CheckManufacturer(const string &expected, + const string &actual) { + if (expected != actual) { + OLA_WARN << "Manufacturer mismatch: " << expected << " != " << actual; + return false; + } + return true; +} + +bool LibUsbHelper::CheckProduct(const string &expected, const string &actual) { + if (expected != actual) { + OLA_WARN << "Product mismatch: " << expected << " != " << actual; + return false; + } + return true; +} + +bool LibUsbHelper::OpenDevice(libusb_device *usb_device, + libusb_device_handle **usb_handle) { + if (libusb_open(usb_device, usb_handle)) { + OLA_WARN << "Failed to open libusb device: " << usb_device; + return false; + } + return true; +} + +bool LibUsbHelper::OpenDeviceAndClaimInterface( + libusb_device *usb_device, + int interface, + libusb_device_handle **usb_handle) { + if (!OpenDevice(usb_device, usb_handle)) { + return false; + } + + if (libusb_claim_interface(*usb_handle, 0)) { + OLA_WARN << "Failed to claim interface " << interface + << " for libusb device: " << usb_device; + libusb_close(*usb_handle); + return false; + } + return true; +} +} // namespace usbdmx +} // namespace plugin +} // namespace ola diff --git a/plugins/usbdmx/LibUsbHelper.h b/plugins/usbdmx/LibUsbHelper.h new file mode 100644 index 0000000000..b4b7c0ab1f --- /dev/null +++ b/plugins/usbdmx/LibUsbHelper.h @@ -0,0 +1,97 @@ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * 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 Library 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * LibUsbHelper.h + * Helper methods for libusb device enumeration. + * Copyright (C) 2014 Simon Newton + */ + +#ifndef PLUGINS_USBDMX_LIBUSBHELPER_H_ +#define PLUGINS_USBDMX_LIBUSBHELPER_H_ + +#include +#include +#include + +namespace ola { +namespace plugin { +namespace usbdmx { + +/** + * @brief Some helper methods for device enumeration. + */ +class LibUsbHelper { + public: + struct DeviceInformation { + std::string manufacturer; + std::string product; + std::string serial; + }; + + /** + * @brief Fetch the manufacturer, product and serial strings from a device. + * @param usb_device The USB device to get information for. + * @param device_descriptor The descriptor to use + * @param[out] device_info The DeviceInformation struct to populate. + * @returns true if we fetched the information, false otherwise. + */ + static bool GetDeviceInfo( + struct libusb_device *usb_device, + const struct libusb_device_descriptor &device_descriptor, + DeviceInformation *device_info); + + /** + * @brief Check if the manufacturer string matches the expected value. + * @param expected The expected manufacturer string. + * @param actual The actual manufacturer string. + * @returns true if the strings matched, false otherwise. + */ + static bool CheckManufacturer(const std::string &expected, + const std::string &actual); + + /** + * @brief Check if the product string matches the expected value. + * @param expected The expected product string. + * @param actual The actual product string. + * @returns true if the strings matched, false otherwise. + */ + static bool CheckProduct(const std::string &expected, + const std::string &actual); + + /** + * @brief Open a libusb device. + * @param usb_device The usb device to open. + * @param[out] usb_handle the new device handle. + * @returns true if the device was opened, false otherwise. + */ + static bool OpenDevice(libusb_device *usb_device, + libusb_device_handle **usb_handle); + + /** + * @brief Open a libusb device and claim an interface. + * @param usb_device The usb device to open. + * @param interface the interface index to claim. + * @param[out] usb_handle the new device handle. + * @returns true if the device was opened and the interface claimed, + * false otherwise. + */ + static bool OpenDeviceAndClaimInterface(libusb_device *usb_device, + int interface, + libusb_device_handle **usb_handle); +}; +} // namespace usbdmx +} // namespace plugin +} // namespace ola +#endif // PLUGINS_USBDMX_LIBUSBHELPER_H_ diff --git a/plugins/usbdmx/Makefile.mk b/plugins/usbdmx/Makefile.mk index 2410feda77..b6866c2132 100644 --- a/plugins/usbdmx/Makefile.mk +++ b/plugins/usbdmx/Makefile.mk @@ -5,27 +5,52 @@ lib_LTLIBRARIES += plugins/usbdmx/libolausbdmx.la plugins_usbdmx_libolausbdmx_la_SOURCES = \ plugins/usbdmx/AnymaDevice.cpp \ plugins/usbdmx/AnymaDevice.h \ + plugins/usbdmx/AnymaDeviceManager.cpp \ + plugins/usbdmx/AnymaDeviceManager.h \ plugins/usbdmx/AnymaOutputPort.cpp \ plugins/usbdmx/AnymaOutputPort.h \ + plugins/usbdmx/AnymaWidget.cpp \ + plugins/usbdmx/AnymaWidget.h \ + plugins/usbdmx/AsyncPluginImpl.cpp \ + plugins/usbdmx/AsyncPluginImpl.h \ plugins/usbdmx/EuroliteProDevice.cpp \ plugins/usbdmx/EuroliteProDevice.h \ + plugins/usbdmx/EuroliteProWidget.h \ + plugins/usbdmx/EuroliteProWidget.cpp \ + plugins/usbdmx/EuroliteProDeviceManager.h \ + plugins/usbdmx/EuroliteProDeviceManager.cpp \ plugins/usbdmx/EuroliteProOutputPort.cpp \ plugins/usbdmx/EuroliteProOutputPort.h \ plugins/usbdmx/FirmwareLoader.h \ + plugins/usbdmx/LibUsbAdaptor.cpp \ + plugins/usbdmx/LibUsbAdaptor.h \ + plugins/usbdmx/LibUsbHelper.cpp \ + plugins/usbdmx/LibUsbHelper.h \ + plugins/usbdmx/PluginImplInterface.h \ plugins/usbdmx/SunliteDevice.cpp \ plugins/usbdmx/SunliteDevice.h \ + plugins/usbdmx/SunliteDeviceManager.cpp \ + plugins/usbdmx/SunliteDeviceManager.h \ plugins/usbdmx/SunliteFirmware.h \ plugins/usbdmx/SunliteFirmwareLoader.cpp \ plugins/usbdmx/SunliteFirmwareLoader.h \ plugins/usbdmx/SunliteOutputPort.cpp \ plugins/usbdmx/SunliteOutputPort.h \ + plugins/usbdmx/SunliteWidget.cpp \ + plugins/usbdmx/SunliteWidget.h \ + plugins/usbdmx/SyncPluginImpl.cpp \ + plugins/usbdmx/SyncPluginImpl.h \ + plugins/usbdmx/ThreadedUsbSender.cpp \ + plugins/usbdmx/ThreadedUsbSender.h \ plugins/usbdmx/UsbDevice.h \ + plugins/usbdmx/UsbDeviceManagerInterface.h \ plugins/usbdmx/UsbDmxPlugin.cpp \ plugins/usbdmx/UsbDmxPlugin.h \ plugins/usbdmx/VellemanDevice.cpp \ plugins/usbdmx/VellemanDevice.h \ plugins/usbdmx/VellemanOutputPort.cpp \ plugins/usbdmx/VellemanOutputPort.h + plugins_usbdmx_libolausbdmx_la_CXXFLAGS = $(COMMON_CXXFLAGS) $(libusb_CFLAGS) plugins_usbdmx_libolausbdmx_la_LIBADD = $(libusb_LIBS) \ common/libolacommon.la diff --git a/plugins/usbdmx/PluginImplInterface.h b/plugins/usbdmx/PluginImplInterface.h new file mode 100644 index 0000000000..1564762784 --- /dev/null +++ b/plugins/usbdmx/PluginImplInterface.h @@ -0,0 +1,56 @@ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * 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 Library 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * PluginImplInterface.h + * The interface for the various implementations of the USBDMX plugin. + * Copyright (C) 2014 Simon Newton + */ + +#ifndef PLUGINS_USBDMX_PLUGINIMPLINTERFACE_H_ +#define PLUGINS_USBDMX_PLUGINIMPLINTERFACE_H_ + +#include +#include +#include +#include +#include +#include "ola/plugin_id.h" +#include "olad/Plugin.h" +#include "ola/io/Descriptor.h" + +namespace ola { +namespace plugin { +namespace usbdmx { + +class PluginImplInterface { + public: + virtual ~PluginImplInterface() {} + + /** + * @brief Start the implementation. + * @returns true if succuessful, false otherwise. + */ + virtual bool Start() = 0; + + /** + * @brief Stop the implementation. + * @returns true if succuessful, false otherwise. + */ + virtual bool Stop() = 0; +}; +} // namespace usbdmx +} // namespace plugin +} // namespace ola +#endif // PLUGINS_USBDMX_PLUGINIMPLINTERFACE_H_ diff --git a/plugins/usbdmx/SunliteDevice.cpp b/plugins/usbdmx/SunliteDevice.cpp index 36b76be780..e3087c25b7 100644 --- a/plugins/usbdmx/SunliteDevice.cpp +++ b/plugins/usbdmx/SunliteDevice.cpp @@ -14,33 +14,26 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * * SunliteDevice.cpp - * The USBDMX2 device + * The Sunlite USBDMX2 device. * Copyright (C) 2010 Simon Newton */ -#include -#include - -#include "ola/Logging.h" #include "plugins/usbdmx/SunliteDevice.h" + #include "plugins/usbdmx/SunliteOutputPort.h" namespace ola { namespace plugin { namespace usbdmx { -/* - * Start this device. - */ +SunliteDevice::SunliteDevice(ola::AbstractPlugin *owner, + SunliteWidgetInterface *widget) + : Device(owner, "Sunlite USBDMX2 Device"), + m_port(new SunliteOutputPort(this, 0, widget)) { +} + bool SunliteDevice::StartHook() { - SunliteOutputPort *output_port = new SunliteOutputPort(this, - 0, - m_usb_device); - if (!output_port->Start()) { - delete output_port; - return false; - } - AddPort(output_port); + AddPort(m_port.release()); return true; } } // namespace usbdmx diff --git a/plugins/usbdmx/SunliteDevice.h b/plugins/usbdmx/SunliteDevice.h index bfbfd45b1d..6551282231 100644 --- a/plugins/usbdmx/SunliteDevice.h +++ b/plugins/usbdmx/SunliteDevice.h @@ -14,35 +14,39 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * * SunliteDevice.h - * Interface for the Sunlite device + * The Sunlite USBDMX2 device. * Copyright (C) 2010 Simon Newton */ #ifndef PLUGINS_USBDMX_SUNLITEDEVICE_H_ #define PLUGINS_USBDMX_SUNLITEDEVICE_H_ -#include +#include #include -#include "plugins/usbdmx/UsbDevice.h" +#include "ola/base/Macro.h" +#include "olad/Device.h" namespace ola { namespace plugin { namespace usbdmx { -/* - * A Sunlite device +/** + * @brief A Sunlite device */ -class SunliteDevice: public UsbDevice { +class SunliteDevice: public Device { public: - SunliteDevice(ola::AbstractPlugin *owner, - libusb_device *usb_device): - UsbDevice(owner, "Sunlite USB Device", usb_device) { - } + SunliteDevice(ola::AbstractPlugin *owner, + class SunliteWidgetInterface *widget); - std::string DeviceId() const { return "usbdmx2"; } + std::string DeviceId() const { return "usbdmx2"; } protected: - bool StartHook(); + bool StartHook(); + + private: + std::auto_ptr m_port; + + DISALLOW_COPY_AND_ASSIGN(SunliteDevice); }; } // namespace usbdmx } // namespace plugin diff --git a/plugins/usbdmx/SunliteDeviceManager.cpp b/plugins/usbdmx/SunliteDeviceManager.cpp new file mode 100644 index 0000000000..2105457056 --- /dev/null +++ b/plugins/usbdmx/SunliteDeviceManager.cpp @@ -0,0 +1,67 @@ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * 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 Library 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * SunliteDeviceManager.cpp + * The Sunlite Device Manager + * Copyright (C) 2014 Simon Newton + */ + +#include "plugins/usbdmx/SunliteDeviceManager.h" +#include "plugins/usbdmx/SunliteFirmwareLoader.h" +#include "plugins/usbdmx/SunliteWidget.h" + +#include "ola/Logging.h" + +namespace ola { +namespace plugin { +namespace usbdmx { + +const uint16_t SunliteDeviceManager::SUNLITE_VENDOR_ID = 0x0962; + +bool SunliteDeviceManager::DeviceAdded( + libusb_device *usb_device, + const struct libusb_device_descriptor &descriptor) { + if (descriptor.idVendor == SUNLITE_VENDOR_ID && + descriptor.idProduct == 0x2000) { + OLA_INFO << "New empty SunliteDevice"; + // TODO(simon): Make this async. + SunliteFirmwareLoader loader(usb_device); + loader.LoadFirmware(); + return true; + } else if (descriptor.idVendor == SUNLITE_VENDOR_ID && + descriptor.idProduct == 0x2001 && + !HasDevice(usb_device)) { + OLA_INFO << "Found a new Sunlite device"; + + + AsynchronousSunliteWidget *widget = new AsynchronousSunliteWidget( + usb_device); + if (!widget->Init()) { + delete widget; + return false; + } + SunliteDevice *device = new SunliteDevice(ParentPlugin(), widget); + return RegisterDevice(usb_device, device); + } + return false; +} + +void SunliteDeviceManager::DeviceRemoved(libusb_device *device) { + // TODO(simon): once firmware loading is async, cancel the load here. + BaseDeviceFactory::DeviceRemoved(device); +} +} // namespace usbdmx +} // namespace plugin +} // namespace ola diff --git a/plugins/usbdmx/SunliteDeviceManager.h b/plugins/usbdmx/SunliteDeviceManager.h new file mode 100644 index 0000000000..5b27b81026 --- /dev/null +++ b/plugins/usbdmx/SunliteDeviceManager.h @@ -0,0 +1,54 @@ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * 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 Library 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * SunliteDeviceManager.h + * The Sunlite Device Manager + * Copyright (C) 2014 Simon Newton + */ + +#ifndef PLUGINS_USBDMX_SUNLITEDEVICEMANAGER_H_ +#define PLUGINS_USBDMX_SUNLITEDEVICEMANAGER_H_ + +#include "ola/base/Macro.h" +#include "plugins/usbdmx/UsbDeviceManagerInterface.h" +#include "plugins/usbdmx/SunliteDevice.h" + +namespace ola { +namespace plugin { +namespace usbdmx { + +/** + * @brief Manages SunLite Devices + */ +class SunliteDeviceManager : public BaseDeviceFactory { + public: + SunliteDeviceManager(PluginAdaptor *plugin_adaptor, + Plugin *plugin) + : BaseDeviceFactory(plugin_adaptor, plugin) {} + + bool DeviceAdded(libusb_device *device, + const struct libusb_device_descriptor &descriptor); + + void DeviceRemoved(libusb_device *device); + + private: + static const uint16_t SUNLITE_VENDOR_ID; + + DISALLOW_COPY_AND_ASSIGN(SunliteDeviceManager); +}; +} // namespace usbdmx +} // namespace plugin +} // namespace ola +#endif // PLUGINS_USBDMX_SUNLITEDEVICEMANAGER_H_ diff --git a/plugins/usbdmx/SunliteFirmwareLoader.h b/plugins/usbdmx/SunliteFirmwareLoader.h index ad8abbcb11..c16f59181d 100644 --- a/plugins/usbdmx/SunliteFirmwareLoader.h +++ b/plugins/usbdmx/SunliteFirmwareLoader.h @@ -25,6 +25,7 @@ #define PLUGINS_USBDMX_SUNLITEFIRMWARELOADER_H_ #include +#include "ola/base/Macro.h" #include "plugins/usbdmx/FirmwareLoader.h" namespace ola { @@ -33,19 +34,21 @@ namespace usbdmx { class SunliteFirmwareLoader: public FirmwareLoader { public: - explicit SunliteFirmwareLoader(libusb_device *usb_device) - : m_device(usb_device) {} - ~SunliteFirmwareLoader() {} + explicit SunliteFirmwareLoader(libusb_device *usb_device) + : m_device(usb_device) {} + ~SunliteFirmwareLoader() {} - bool LoadFirmware(); + bool LoadFirmware(); private: - libusb_device *m_device; + libusb_device *m_device; - static const int INTERFACE_NUMBER = 0; // the device only has 1 interface - static const uint8_t UPLOAD_REQUEST_TYPE = 0x40; - static const uint8_t UPLOAD_REQUEST = 0xa0; - static const unsigned int UPLOAD_TIMEOUT = 300; // ms + static const int INTERFACE_NUMBER = 0; // the device only has 1 interface + static const uint8_t UPLOAD_REQUEST_TYPE = 0x40; + static const uint8_t UPLOAD_REQUEST = 0xa0; + static const unsigned int UPLOAD_TIMEOUT = 300; // ms + + DISALLOW_COPY_AND_ASSIGN(SunliteFirmwareLoader); }; } // namespace usbdmx } // namespace plugin diff --git a/plugins/usbdmx/SunliteOutputPort.cpp b/plugins/usbdmx/SunliteOutputPort.cpp index 3024fb9ad2..8b68b9a742 100644 --- a/plugins/usbdmx/SunliteOutputPort.cpp +++ b/plugins/usbdmx/SunliteOutputPort.cpp @@ -14,189 +14,38 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * * SunliteOutputPort.cpp - * Thread for the Sunlite USBDMX2 Output Port + * The output port for a Sunlite USBDMX2 device. * Copyright (C) 2010 Simon Newton - * - * See the comments in SunliteOutputPort.h */ -#include -#include +#include "plugins/usbdmx/SunliteOutputPort.h" #include "ola/Logging.h" -#include "plugins/usbdmx/SunliteOutputPort.h" #include "plugins/usbdmx/SunliteDevice.h" - +#include "plugins/usbdmx/SunliteWidget.h" namespace ola { namespace plugin { namespace usbdmx { - -/* - * Create a new SunliteOutputPort object - */ SunliteOutputPort::SunliteOutputPort(SunliteDevice *parent, unsigned int id, - libusb_device *usb_device) + SunliteWidgetInterface *widget) : BasicOutputPort(parent, id), - m_term(false), - m_new_data(false), - m_usb_device(usb_device), - m_usb_handle(NULL) { - InitPacket(); + m_widget(widget) { } - -/* - * Cleanup - */ SunliteOutputPort::~SunliteOutputPort() { - { - ola::thread::MutexLocker locker(&m_term_mutex); - m_term = true; - } - Join(); + // TODO(simon): stop the thread here?? + OLA_INFO << "SunliteOutputPort::~SunliteOutputPort()"; + delete m_widget; } - -/* - * Start this thread - */ -bool SunliteOutputPort::Start() { - libusb_device_handle *usb_handle; - - if (libusb_open(m_usb_device, &usb_handle)) { - OLA_WARN << "Failed to open Sunlite usb device"; - return false; - } - - if (libusb_claim_interface(usb_handle, 0)) { - OLA_WARN << "Failed to claim Sunlite usb device"; - libusb_close(usb_handle); - return false; - } - - m_usb_handle = usb_handle; - bool ret = ola::thread::Thread::Start(); - if (!ret) { - OLA_WARN << "pthread create failed"; - libusb_release_interface(m_usb_handle, 0); - libusb_close(usb_handle); - return false; - } +bool SunliteOutputPort::WriteDMX(const DmxBuffer &buffer, + OLA_UNUSED uint8_t priority) { + m_widget->SendDMX(buffer); return true; } - - -/* - * Run this thread - */ -void *SunliteOutputPort::Run() { - DmxBuffer buffer; - bool new_data; - - if (!m_usb_handle) - return NULL; - - while (true) { - { - ola::thread::MutexLocker locker(&m_term_mutex); - if (m_term) - break; - } - - { - ola::thread::MutexLocker locker(&m_data_mutex); - buffer.Set(m_buffer); - new_data = m_new_data; - m_new_data = false; - } - - if (new_data) { - if (!SendDMX(buffer)) { - OLA_WARN << "Send failed, stopping thread..."; - break; - } - } else { - // sleep for a bit - usleep(40000); - } - } - libusb_release_interface(m_usb_handle, 0); - libusb_close(m_usb_handle); - return NULL; -} - - -/* - * Store the data in the shared buffer - */ -bool SunliteOutputPort::WriteDMX(const DmxBuffer &buffer, uint8_t priority) { - ola::thread::MutexLocker locker(&m_data_mutex); - m_buffer.Set(buffer); - m_new_data = true; - return true; - (void) priority; -} - - -/* - * Setup the packet we send to the device. - * - * The packet is divided into 26 chunks of 32 bytes each. Each chunk contains - * the data for 20 channels (except the last one which has 12 channels of data) - */ -void SunliteOutputPort::InitPacket() { - // zero everything - memset(m_packet, 0, SUNLITE_PACKET_SIZE); - - for (unsigned int chunk = 0; chunk < CHUNKS_PER_PACKET; ++chunk) { - unsigned int i = chunk * CHUNK_SIZE; // index into the packet - unsigned int channel = chunk * CHANNELS_PER_CHUNK; - - m_packet[i] = 0x80; - m_packet[i+1] = channel / 2; - m_packet[i+2] = 0x84; - m_packet[i+7] = channel / 2 + 2; - m_packet[i+8] = 0x84; - m_packet[i+13] = channel / 2 + 4; - if (chunk < CHUNKS_PER_PACKET - 1) { - m_packet[i+14] = 0x84; - m_packet[i+19] = channel / 2 + 6; - m_packet[i+20] = 0x84; - m_packet[i+25] = channel / 2 + 8; - m_packet[i+26] = 0x04; - m_packet[i+31] = 0x00; - } else { - // the last m_packet is short - m_packet[i+14] = 0x04; - } - } -} - - -/* - * Send DMX to the widget - */ -bool SunliteOutputPort::SendDMX(const DmxBuffer &buffer) { - for (unsigned int i = 0; i < buffer.Size(); i++) - m_packet[(i / CHANNELS_PER_CHUNK) * CHUNK_SIZE + - ((i / 4) % 5) * 6 + 3 + (i % 4)] = buffer.Get(i); - - int transferred; - int r = libusb_bulk_transfer( - m_usb_handle, - ENDPOINT, - (unsigned char*) m_packet, - SUNLITE_PACKET_SIZE, - &transferred, - TIMEOUT); - if (transferred != SUNLITE_PACKET_SIZE) - // not sure if this is fatal or not - OLA_WARN << "Sunlite driver failed to transfer all data"; - return r == 0; -} } // namespace usbdmx } // namespace plugin } // namespace ola diff --git a/plugins/usbdmx/SunliteOutputPort.h b/plugins/usbdmx/SunliteOutputPort.h index 307d14be8c..26969d8939 100644 --- a/plugins/usbdmx/SunliteOutputPort.h +++ b/plugins/usbdmx/SunliteOutputPort.h @@ -16,20 +16,14 @@ * SunliteOutputPort.h * The output port for a Sunlite USBDMX2 device. * Copyright (C) 2010 Simon Newton - * - * It takes around 11ms to complete the transfer to the device so we use a - * a separate thread for the writes. The time to acquire the lock, copy the - * buffer & release is 1-2 uS. */ #ifndef PLUGINS_USBDMX_SUNLITEOUTPUTPORT_H_ #define PLUGINS_USBDMX_SUNLITEOUTPUTPORT_H_ -#include -#include #include +#include "ola/base/Macro.h" #include "ola/DmxBuffer.h" -#include "ola/thread/Thread.h" #include "olad/Port.h" namespace ola { @@ -38,39 +32,28 @@ namespace usbdmx { class SunliteDevice; -class SunliteOutputPort: public BasicOutputPort, ola::thread::Thread { +class SunliteOutputPort: public BasicOutputPort { public: - SunliteOutputPort(SunliteDevice *parent, - unsigned int id, - libusb_device *usb_device); - ~SunliteOutputPort(); - - bool Start(); - void *Run(); + /** + * @brief Create a new SunliteOutputPort. + */ + SunliteOutputPort(SunliteDevice *parent, + unsigned int id, + class SunliteWidgetInterface *widget); - bool WriteDMX(const DmxBuffer &buffer, uint8_t priority); - std::string Description() const { return ""; } + /** + * @brief Cleanup. + */ + ~SunliteOutputPort(); - private: - enum {SUNLITE_PACKET_SIZE = 0x340}; + bool WriteDMX(const DmxBuffer &buffer, uint8_t priority); - static const unsigned int CHUNKS_PER_PACKET = 26; - static const unsigned int CHANNELS_PER_CHUNK = 20; - static const unsigned int CHUNK_SIZE = 32; - static const uint8_t ENDPOINT = 1; - static const unsigned int TIMEOUT = 50; // 50ms is ok + std::string Description() const { return ""; } - bool m_term; - bool m_new_data; - uint8_t m_packet[SUNLITE_PACKET_SIZE]; - libusb_device *m_usb_device; - libusb_device_handle *m_usb_handle; - DmxBuffer m_buffer; - ola::thread::Mutex m_data_mutex; - ola::thread::Mutex m_term_mutex; + private: + class SunliteWidgetInterface* const m_widget; - void InitPacket(); - bool SendDMX(const DmxBuffer &buffer); + DISALLOW_COPY_AND_ASSIGN(SunliteOutputPort); }; } // namespace usbdmx } // namespace plugin diff --git a/plugins/usbdmx/SunliteWidget.cpp b/plugins/usbdmx/SunliteWidget.cpp new file mode 100644 index 0000000000..868e3a8e10 --- /dev/null +++ b/plugins/usbdmx/SunliteWidget.cpp @@ -0,0 +1,245 @@ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * 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 Library 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * SunliteWidget.cpp + * The synchronous and asynchronous Sunlite widgets. + * Copyright (C) 2014 Simon Newton + */ + +#include "plugins/usbdmx/SunliteWidget.h" + +#include "ola/Constants.h" +#include "ola/Logging.h" +#include "plugins/usbdmx/LibUsbHelper.h" + +namespace ola { +namespace plugin { +namespace usbdmx { + +namespace { + +static const unsigned int CHUNKS_PER_PACKET = 26; +static const unsigned int CHANNELS_PER_CHUNK = 20; +static const unsigned int CHUNK_SIZE = 32; +static const uint8_t ENDPOINT = 1; +static const unsigned int TIMEOUT = 50; // 50ms is ok + +/* + * Called by the AsynchronousSunliteWidget when the transfer completes. + */ +void AsyncCallback(struct libusb_transfer *transfer) { + AsynchronousSunliteWidget *widget = + reinterpret_cast(transfer->user_data); + widget->TransferComplete(transfer); +} + +/* + * Initialize a USBDMX2 packet + */ +void InitPacket(uint8_t packet[SunliteWidgetInterface::SUNLITE_PACKET_SIZE]) { + memset(packet, 0, SunliteWidgetInterface::SUNLITE_PACKET_SIZE); + + // The packet is divided into 26 chunks of 32 bytes each. Each chunk contains + // the data for 20 channels (except the last one which has 12 channels of + // data). + for (unsigned int chunk = 0; chunk < CHUNKS_PER_PACKET; ++chunk) { + unsigned int i = chunk * CHUNK_SIZE; // index into the packet + unsigned int channel = chunk * CHANNELS_PER_CHUNK; + + packet[i] = 0x80; + packet[i + 1] = channel / 2; + packet[i + 2] = 0x84; + packet[i + 7] = channel / 2 + 2; + packet[i + 8] = 0x84; + packet[i + 13] = channel / 2 + 4; + if (chunk < CHUNKS_PER_PACKET - 1) { + packet[i + 14] = 0x84; + packet[i + 19] = channel / 2 + 6; + packet[i + 20] = 0x84; + packet[i + 25] = channel / 2 + 8; + packet[i + 26] = 0x04; + packet[i + 31] = 0x00; + } else { + // the last chunk is short + packet[i + 14] = 0x04; + } + } +} + +/* + * Update a USBDMX2 packet to match the supplied DmxBuffer. + */ +void UpdatePacket(const DmxBuffer &buffer, + uint8_t packet[SunliteWidgetInterface::SUNLITE_PACKET_SIZE]) { + for (unsigned int i = 0; i < buffer.Size(); i++) { + packet[(i / CHANNELS_PER_CHUNK) * CHUNK_SIZE + + ((i / 4) % 5) * 6 + 3 + (i % 4)] = buffer.Get(i); + } +} + +} // namespace + +/* + * Sends messages to a Sunlite device in a separate thread. + */ +class SunliteThreadedSender: public ThreadedUsbSender { + public: + SunliteThreadedSender(libusb_device *usb_device, + libusb_device_handle *handle); + + private: + uint8_t m_packet[SunliteWidgetInterface::SUNLITE_PACKET_SIZE]; + + bool TransmitBuffer(libusb_device_handle *handle, + const DmxBuffer &buffer); +}; + +SunliteThreadedSender::SunliteThreadedSender( + libusb_device *usb_device, + libusb_device_handle *usb_handle) + : ThreadedUsbSender(usb_device, usb_handle) { + InitPacket(m_packet); +} + +bool SunliteThreadedSender::TransmitBuffer(libusb_device_handle *handle, + const DmxBuffer &buffer) { + UpdatePacket(buffer, m_packet); + int transferred; + int r = libusb_bulk_transfer( + handle, + ENDPOINT, + (unsigned char*) m_packet, + SunliteWidgetInterface::SUNLITE_PACKET_SIZE, + &transferred, + TIMEOUT); + if (transferred != SunliteWidgetInterface::SUNLITE_PACKET_SIZE) { + // not sure if this is fatal or not + OLA_WARN << "Sunlite driver failed to transfer all data"; + } + return r == 0; +} + + +SynchronousSunliteWidget::SynchronousSunliteWidget(libusb_device *usb_device) + : m_usb_device(usb_device) { +} + +bool SynchronousSunliteWidget::Init() { + libusb_device_handle *usb_handle; + + bool ok = LibUsbHelper::OpenDeviceAndClaimInterface( + m_usb_device, 0, &usb_handle); + if (!ok) { + return false; + } + + std::auto_ptr sender( + new SunliteThreadedSender(m_usb_device, usb_handle)); + if (!sender->Start()) { + return false; + } + m_sender.reset(sender.release()); + return true; +} + +bool SynchronousSunliteWidget::SendDMX(const DmxBuffer &buffer) { + return m_sender.get() ? m_sender->SendDMX(buffer) : false; +} + +AsynchronousSunliteWidget::AsynchronousSunliteWidget( + libusb_device *usb_device) + : m_usb_device(usb_device), + m_usb_handle(NULL), + m_transfer_state(IDLE) { + InitPacket(m_packet); + m_transfer = libusb_alloc_transfer(0); + libusb_ref_device(usb_device); +} + +AsynchronousSunliteWidget::~AsynchronousSunliteWidget() { + bool canceled = false; + OLA_INFO << "AsynchronousSunliteWidget shutdown"; + while (1) { + ola::thread::MutexLocker locker(&m_mutex); + if (m_transfer_state == IDLE) { + break; + } + if (!canceled) { + libusb_cancel_transfer(m_transfer); + canceled = true; + } + } + + libusb_free_transfer(m_transfer); + libusb_unref_device(m_usb_device); +} + +bool AsynchronousSunliteWidget::Init() { + bool ok = LibUsbHelper::OpenDeviceAndClaimInterface( + m_usb_device, 0, &m_usb_handle); + if (!ok) { + return false; + } + return true; +} + +bool AsynchronousSunliteWidget::SendDMX(const DmxBuffer &buffer) { + OLA_INFO << "Call to AsynchronousSunliteWidget::SendDMX"; + if (!m_usb_handle) { + OLA_WARN << "AsynchronousSunliteWidget hasn't been initialized"; + return false; + } + + ola::thread::MutexLocker locker(&m_mutex); + if (m_transfer_state != IDLE) { + return true; + } + + UpdatePacket(buffer, m_packet); + libusb_fill_bulk_transfer( + m_transfer, + m_usb_handle, + ENDPOINT, + (unsigned char*) m_packet, + SUNLITE_PACKET_SIZE, + &AsyncCallback, + this, + TIMEOUT); + + int ret = libusb_submit_transfer(m_transfer); + if (ret) { + OLA_WARN << "libusb_submit_transfer returned " << libusb_error_name(ret); + return false; + } + OLA_INFO << "submit ok"; + m_transfer_state = IN_PROGRESS; + return true; +} + +void AsynchronousSunliteWidget::TransferComplete( + struct libusb_transfer *transfer) { + if (transfer != m_transfer) { + OLA_WARN << "Mismatched libusb transfer: " << transfer << " != " + << m_transfer; + return; + } + + OLA_INFO << "async transfer complete"; + ola::thread::MutexLocker locker(&m_mutex); + m_transfer_state = IDLE; +} +} // namespace usbdmx +} // namespace plugin +} // namespace ola diff --git a/plugins/usbdmx/SunliteWidget.h b/plugins/usbdmx/SunliteWidget.h new file mode 100644 index 0000000000..03b271b5a9 --- /dev/null +++ b/plugins/usbdmx/SunliteWidget.h @@ -0,0 +1,105 @@ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * 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 Library 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * SunliteWidget.h + * The synchronous and asynchronous Sunlite widgets. + * Copyright (C) 2014 Simon Newton + */ + +#ifndef PLUGINS_USBDMX_SUNLITEWIDGET_H_ +#define PLUGINS_USBDMX_SUNLITEWIDGET_H_ + +#include +#include "ola/base/Macro.h" +#include "ola/DmxBuffer.h" +#include "plugins/usbdmx/ThreadedUsbSender.h" + +namespace ola { +namespace plugin { +namespace usbdmx { + +class SunliteThreadedSender; + +/** + * @brief The interface for the Sunlite Widgets + */ +class SunliteWidgetInterface { + public: + virtual ~SunliteWidgetInterface() {} + + virtual bool Init() = 0; + + virtual bool SendDMX(const DmxBuffer &buffer) = 0; + + enum {SUNLITE_PACKET_SIZE = 0x340}; +}; + + +/** + * @brief An Sunlite widget that uses synchronous libusb operations. + * + * Internally this spawns a new thread to avoid blocking SendDMX() calls. + */ +class SynchronousSunliteWidget: public SunliteWidgetInterface { + public: + explicit SynchronousSunliteWidget(libusb_device *usb_device); + + bool Init(); + + bool SendDMX(const DmxBuffer &buffer); + + private: + libusb_device* const m_usb_device; + std::auto_ptr m_sender; + + DISALLOW_COPY_AND_ASSIGN(SynchronousSunliteWidget); +}; + +/** + * @brief An Sunlite widget that uses asynchronous libusb operations. + */ +class AsynchronousSunliteWidget: public SunliteWidgetInterface { + public: + explicit AsynchronousSunliteWidget(libusb_device *usb_device); + ~AsynchronousSunliteWidget(); + + bool Init(); + + bool SendDMX(const DmxBuffer &buffer); + + void TransferComplete(struct libusb_transfer *transfer); + + private: + enum TransferState { + IDLE, + IN_PROGRESS, + }; + + libusb_device* const m_usb_device; + libusb_device_handle *m_usb_handle; + + TransferState m_transfer_state; + ola::thread::Mutex m_mutex; + + struct libusb_transfer *m_transfer; + + uint8_t m_packet[SUNLITE_PACKET_SIZE]; + + DISALLOW_COPY_AND_ASSIGN(AsynchronousSunliteWidget); +}; +} // namespace usbdmx +} // namespace plugin +} // namespace ola +#endif // PLUGINS_USBDMX_SUNLITEWIDGET_H_ diff --git a/plugins/usbdmx/SyncPluginImpl.cpp b/plugins/usbdmx/SyncPluginImpl.cpp new file mode 100644 index 0000000000..0ab5621ef8 --- /dev/null +++ b/plugins/usbdmx/SyncPluginImpl.cpp @@ -0,0 +1,331 @@ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * 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 Library 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * SyncPluginImpl.cpp + * The synchronous implementation of the USB DMX plugin. + * Copyright (C) 2006 Simon Newton + */ + +#include "plugins/usbdmx/SyncPluginImpl.h" + +#include +#include +#include +#include + +#include +#include +#include + +#include "ola/Callback.h" +#include "ola/Logging.h" +#include "ola/StringUtils.h" +#include "ola/io/Descriptor.h" +#include "ola/stl/STLUtils.h" +#include "olad/PluginAdaptor.h" +#include "olad/Preferences.h" + +#include "plugins/usbdmx/AnymaDevice.h" +#include "plugins/usbdmx/AnymaWidget.h" +#include "plugins/usbdmx/EuroliteProWidget.h" + +#include "plugins/usbdmx/EuroliteProDevice.h" +#include "plugins/usbdmx/FirmwareLoader.h" +#include "plugins/usbdmx/SunliteDevice.h" +#include "plugins/usbdmx/SunliteFirmwareLoader.h" +#include "plugins/usbdmx/SunliteWidget.h" +#include "plugins/usbdmx/UsbDevice.h" +#include "plugins/usbdmx/VellemanDevice.h" + + +namespace ola { +namespace plugin { +namespace usbdmx { + +using ola::io::DeviceDescriptor; +using std::pair; +using std::string; +using std::vector; + +SyncPluginImpl::SyncPluginImpl(PluginAdaptor *plugin_adaptor, + Plugin *plugin, + LibUsbAdaptor *libusb_adaptor) + : m_plugin_adaptor(plugin_adaptor), + m_plugin(plugin), + m_libusb_adaptor(libusb_adaptor), + m_context(NULL), + m_anyma_devices_missing_serial_numbers(false) { +} + +bool SyncPluginImpl::Start() { + if (libusb_init(&m_context)) { + OLA_WARN << "Failed to init libusb"; + return false; + } + + m_libusb_adaptor->SetDebug(m_context); + + if (LoadFirmware()) { + // we loaded firmware for at least one device, set up a callback to run in + // a couple of seconds to re-scan for devices + m_plugin_adaptor->RegisterSingleTimeout( + 3500, + NewSingleCallback(this, &SyncPluginImpl::FindDevices)); + } + FindDevices(); + return true; +} + +bool SyncPluginImpl::Stop() { + vector::iterator iter; + for (iter = m_devices.begin(); iter != m_devices.end(); ++iter) { + m_plugin_adaptor->UnregisterDevice(*iter); + (*iter)->Stop(); + delete *iter; + } + m_devices.clear(); + m_registered_devices.clear(); + + libusb_exit(m_context); + + return true; +} + + + +/* + * Load firmware onto devices if required. + * @returns true if we loaded firmware for one or more devices + */ +bool SyncPluginImpl::LoadFirmware() { + libusb_device **device_list; + size_t device_count = libusb_get_device_list(m_context, &device_list); + FirmwareLoader *loader; + bool loaded = false; + + for (unsigned int i = 0; i < device_count; i++) { + libusb_device *usb_device = device_list[i]; + loader = NULL; + struct libusb_device_descriptor device_descriptor; + libusb_get_device_descriptor(usb_device, &device_descriptor); + + if (device_descriptor.idVendor == 0x0962 && + device_descriptor.idProduct == 0x2000) { + loader = new SunliteFirmwareLoader(usb_device); + } + + if (loader) { + loader->LoadFirmware(); + loaded = true; + delete loader; + } + } + libusb_free_device_list(device_list, 1); // unref devices + return loaded; +} + + +/* + * Find known devices & register them + */ +void SyncPluginImpl::FindDevices() { + libusb_device **device_list; + size_t device_count = libusb_get_device_list(m_context, &device_list); + + for (unsigned int i = 0; i < device_count; i++) { + CheckDevice(device_list[i]); + } + libusb_free_device_list(device_list, 1); // unref devices +} + +void SyncPluginImpl::CheckDevice(libusb_device *usb_device) { + struct libusb_device_descriptor device_descriptor; + libusb_get_device_descriptor(usb_device, &device_descriptor); + Device *device = NULL; + + pair bus_dev_id(libusb_get_bus_number(usb_device), + libusb_get_device_address(usb_device)); + + if (STLContains(m_registered_devices, bus_dev_id)) { + return; + } + + if (device_descriptor.idVendor == 0x10cf && + device_descriptor.idProduct == 0x8062) { + OLA_INFO << "Found a Velleman USB device"; + device = new VellemanDevice(m_plugin, usb_device); + } else if (device_descriptor.idVendor == 0x0962 && + device_descriptor.idProduct == 0x2001) { + OLA_INFO << "Found a Sunlite device"; + SynchronousSunliteWidget *widget = new SynchronousSunliteWidget(usb_device); + if (!widget->Init()) { + delete widget; + return; + } + device = new SunliteDevice(m_plugin, widget); + + } else if (device_descriptor.idVendor == 0x16C0 && + device_descriptor.idProduct == 0x05DC) { + OLA_INFO << "Found an Anyma device"; + device = NewAnymaDevice(usb_device, device_descriptor); + } else if (device_descriptor.idVendor == 0x04d8 && + device_descriptor.idProduct == 0xfa63) { + OLA_INFO << "Found a EUROLITE device"; + SynchronousEuroliteProWidget *widget = new SynchronousEuroliteProWidget( + usb_device); + if (!widget->Init()) { + delete widget; + return; + } + device = new EuroliteProDevice(m_plugin, widget); + } + + if (device) { + if (!device->Start()) { + delete device; + return; + } + m_registered_devices.insert(bus_dev_id); + m_devices.push_back(device); + m_plugin_adaptor->RegisterDevice(device); + } +} + +/** + * Create a new AnymaDevice. Some Anyma devices don't have serial numbers, so + * we can only support one of those. + */ +Device* SyncPluginImpl::NewAnymaDevice( + libusb_device *usb_device, + const struct libusb_device_descriptor &device_descriptor) { + libusb_device_handle *usb_handle; + if (libusb_open(usb_device, &usb_handle)) { + OLA_WARN << "Failed to open Anyma usb device"; + return NULL; + } + + USBDeviceInformation info; + GetDeviceInfo(usb_handle, device_descriptor, &info); + + if (!MatchManufacturer(AnymaWidgetInterface::EXPECTED_MANUFACTURER, + info.manufacturer)) { + libusb_close(usb_handle); + return NULL; + } + + if (!MatchProduct(AnymaWidgetInterface::EXPECTED_PRODUCT, info.product)) { + libusb_close(usb_handle); + return NULL; + } + + if (info.serial.empty()) { + if (m_anyma_devices_missing_serial_numbers) { + OLA_WARN << "Failed to read serial number or serial number empty. " + << "We can only support one device without a serial number."; + return NULL; + } else { + OLA_WARN << "Failed to read serial number from " << info.manufacturer + << " : " << info.product + << " the device probably doesn't have one"; + m_anyma_devices_missing_serial_numbers = true; + } + } + + SynchronousAnymaWidget *widget = new SynchronousAnymaWidget(usb_device); + if (!widget->Init()) { + delete widget; + return NULL; + } + return new AnymaDevice(m_plugin, widget, info.serial); +} + + +/** + * Get the Manufacturer, Product and Serial number strings for a device. + */ +void SyncPluginImpl::GetDeviceInfo( + libusb_device_handle *usb_handle, + const struct libusb_device_descriptor &device_descriptor, + USBDeviceInformation *device_info) { + if (!GetDescriptorString(usb_handle, device_descriptor.iManufacturer, + &device_info->manufacturer)) + OLA_INFO << "Failed to get manufacturer name"; + + if (!GetDescriptorString(usb_handle, device_descriptor.iProduct, + &device_info->product)) + OLA_INFO << "Failed to get product name"; + + if (!GetDescriptorString(usb_handle, device_descriptor.iSerialNumber, + &device_info->serial)) + OLA_WARN << "Failed to read serial number, the device probably doesn't " + << "have one"; +} + + +/** + * Check if the manufacturer string matches the expected value. Log a message + * if it doesn't. + */ +bool SyncPluginImpl::MatchManufacturer(const string &expected, + const string &actual) { + if (expected != actual) { + OLA_WARN << "Manufacturer mismatch: " << expected << " != " << actual; + return false; + } + return true; +} + + +/** + * Check if the manufacturer string matches the expected value. Log a message + * if it doesn't. + */ +bool SyncPluginImpl::MatchProduct(const string &expected, + const string &actual) { + if (expected != actual) { + OLA_WARN << "Product mismatch: " << expected << " != " << actual; + return false; + } + return true; +} + +/* + * Return a string descriptor. + * @param usb_handle the usb handle to the device + * @param desc_index the index of the descriptor + * @param data where to store the output string + * @returns true if we got the value, false otherwise + */ +bool SyncPluginImpl::GetDescriptorString(libusb_device_handle *usb_handle, + uint8_t desc_index, + string *data) { + enum { buffer_size = 32 }; // static arrays FTW! + unsigned char buffer[buffer_size]; + int r = libusb_get_string_descriptor_ascii( + usb_handle, + desc_index, + buffer, + buffer_size); + + if (r <= 0) { + OLA_INFO << "libusb_get_string_descriptor_ascii returned " << r; + return false; + } + data->assign(reinterpret_cast(buffer)); + return true; +} +} // namespace usbdmx +} // namespace plugin +} // namespace ola diff --git a/plugins/usbdmx/SyncPluginImpl.h b/plugins/usbdmx/SyncPluginImpl.h new file mode 100644 index 0000000000..db880af5a5 --- /dev/null +++ b/plugins/usbdmx/SyncPluginImpl.h @@ -0,0 +1,107 @@ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * 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 Library 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * SyncPluginImpl.h + * The synchronous implementation of the USB DMX plugin. + * Copyright (C) 2010 Simon Newton + */ + +#ifndef PLUGINS_USBDMX_SYNCPLUGINIMPL_H_ +#define PLUGINS_USBDMX_SYNCPLUGINIMPL_H_ + +#include +#include +#include +#include +#include +#include "ola/base/Macro.h" +#include "plugins/usbdmx/PluginImplInterface.h" +#include "plugins/usbdmx/LibUsbAdaptor.h" + + +namespace ola { + +class Device; + +namespace plugin { +namespace usbdmx { + +/** + * @brief The legacy implementation. + * + * This implementation spawns a thread for each dongle, and then uses + * synchronous calls to libusb. + * + * This does not support hotplug. + */ +class SyncPluginImpl: public PluginImplInterface { + public: + /** + * @brief Create a new SyncPluginImpl. + * @param plugin_adaptor The PluginAdaptor to use, ownership is not + * transferred. + * @param plugin The parent Plugin object which is used when creating + * devices. + * @param libusb_adaptor The adaptor to use when calling libusb. + */ + SyncPluginImpl(PluginAdaptor *plugin_adaptor, + Plugin *plugin, + LibUsbAdaptor *libusb_adaptor); + + bool Start(); + bool Stop(); + + private: + struct USBDeviceInformation { + std::string manufacturer; + std::string product; + std::string serial; + }; + + PluginAdaptor *m_plugin_adaptor; + Plugin *m_plugin; + LibUsbAdaptor *m_libusb_adaptor; + + libusb_context *m_context; + + bool m_anyma_devices_missing_serial_numbers; + std::vector m_devices; // list of our devices + std::set > m_registered_devices; + + bool LoadFirmware(); + void FindDevices(); + void CheckDevice(libusb_device *device); + + class Device* NewAnymaDevice( + struct libusb_device *usb_device, + const struct libusb_device_descriptor &device_descriptor); + + void GetDeviceInfo( + struct libusb_device_handle *usb_handle, + const struct libusb_device_descriptor &device_descriptor, + USBDeviceInformation *device_info); + bool MatchManufacturer(const std::string &expected, + const std::string &actual); + bool MatchProduct(const std::string &expected, const std::string &actual); + bool GetDescriptorString(libusb_device_handle *usb_handle, + uint8_t desc_index, + std::string *data); + + DISALLOW_COPY_AND_ASSIGN(SyncPluginImpl); +}; +} // namespace usbdmx +} // namespace plugin +} // namespace ola +#endif // PLUGINS_USBDMX_SYNCPLUGINIMPL_H_ diff --git a/plugins/usbdmx/ThreadedUsbSender.cpp b/plugins/usbdmx/ThreadedUsbSender.cpp new file mode 100644 index 0000000000..caff8887ca --- /dev/null +++ b/plugins/usbdmx/ThreadedUsbSender.cpp @@ -0,0 +1,98 @@ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * 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 Library 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * ThreadedUsbSender.cpp + * Send DMX data over USB from a dedicated thread. + * Copyright (C) 2014 Simon Newton + */ + +#include "plugins/usbdmx/ThreadedUsbSender.h" + +#include +#include "ola/Logging.h" + +namespace ola { +namespace plugin { +namespace usbdmx { + +ThreadedUsbSender::ThreadedUsbSender(libusb_device *usb_device, + libusb_device_handle *usb_handle) + : m_term(false), + m_usb_device(usb_device), + m_usb_handle(usb_handle) { + libusb_ref_device(usb_device); +} + +ThreadedUsbSender::~ThreadedUsbSender() { + { + ola::thread::MutexLocker locker(&m_term_mutex); + m_term = true; + } + Join(); + libusb_unref_device(m_usb_device); +} + +bool ThreadedUsbSender::Start() { + bool ret = ola::thread::Thread::Start(); + if (!ret) { + OLA_WARN << "Failed to start sender thread"; + libusb_release_interface(m_usb_handle, 0); + libusb_close(m_usb_handle); + return false; + } + return true; +} + +void *ThreadedUsbSender::Run() { + DmxBuffer buffer; + if (!m_usb_handle) + return NULL; + + while (1) { + { + ola::thread::MutexLocker locker(&m_term_mutex); + if (m_term) + break; + } + + { + ola::thread::MutexLocker locker(&m_data_mutex); + buffer.Set(m_buffer); + } + + if (buffer.Size()) { + if (!TransmitBuffer(m_usb_handle, buffer)) { + OLA_WARN << "Send failed, stopping thread..."; + break; + } + } else { + // sleep for a bit + usleep(40000); + } + } + libusb_release_interface(m_usb_handle, 0); + libusb_close(m_usb_handle); + return NULL; +} + +bool ThreadedUsbSender::SendDMX(const DmxBuffer &buffer) { + // Store the new data in the shared buffer. + ola::thread::MutexLocker locker(&m_data_mutex); + m_buffer.Set(buffer); + return true; +} +} // namespace usbdmx +} // namespace plugin +} // namespace ola diff --git a/plugins/usbdmx/ThreadedUsbSender.h b/plugins/usbdmx/ThreadedUsbSender.h new file mode 100644 index 0000000000..336b667043 --- /dev/null +++ b/plugins/usbdmx/ThreadedUsbSender.h @@ -0,0 +1,73 @@ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * 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 Library 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * ThreadedUsbSender.h + * Send DMX data over USB from a dedicated thread. + * Copyright (C) 2010 Simon Newton + */ + +#ifndef PLUGINS_USBDMX_THREADEDUSBSENDER_H_ +#define PLUGINS_USBDMX_THREADEDUSBSENDER_H_ + +#include +#include "ola/base/Macro.h" +#include "ola/DmxBuffer.h" +#include "ola/thread/Thread.h" + +namespace ola { +namespace plugin { +namespace usbdmx { + +/** + * @brief Send DMX data using libusb, from a separate thread. + * + * The synchronous libusb calls can sometimes take a while to complete, I've + * seen cases of up to 21ms. + * To avoid blocking the main thread, we need to perform the libusb transfer + * calls in a separate thread. This class contains all the thread management + * code, leaving the subclass to implement TransmitBuffer(), which performs the + * actual transfer. + * + * ThreadedUsbSender can be used as a building block for synchronous widgets. + */ +class ThreadedUsbSender: private ola::thread::Thread { + public: + ThreadedUsbSender(libusb_device *usb_device, + libusb_device_handle *usb_handle); + virtual ~ThreadedUsbSender(); + + bool Start(); + void *Run(); + + bool SendDMX(const DmxBuffer &buffer); + + protected: + virtual bool TransmitBuffer(libusb_device_handle *handle, + const DmxBuffer &buffer) = 0; + + private: + bool m_term; + libusb_device* const m_usb_device; + libusb_device_handle* const m_usb_handle; + DmxBuffer m_buffer; + ola::thread::Mutex m_data_mutex; + ola::thread::Mutex m_term_mutex; + + DISALLOW_COPY_AND_ASSIGN(ThreadedUsbSender); +}; +} // namespace usbdmx +} // namespace plugin +} // namespace ola +#endif // PLUGINS_USBDMX_THREADEDUSBSENDER_H_ diff --git a/plugins/usbdmx/UsbDevice.h b/plugins/usbdmx/UsbDevice.h index db3e62b108..30221149d1 100644 --- a/plugins/usbdmx/UsbDevice.h +++ b/plugins/usbdmx/UsbDevice.h @@ -23,6 +23,7 @@ #include #include +#include "ola/base/Macro.h" #include "olad/Device.h" namespace ola { @@ -47,7 +48,10 @@ class UsbDevice: public ola::Device { } protected: - libusb_device *m_usb_device; + libusb_device *m_usb_device; + + private: + DISALLOW_COPY_AND_ASSIGN(UsbDevice); }; } // namespace usbdmx } // namespace plugin diff --git a/plugins/usbdmx/UsbDeviceManagerInterface.h b/plugins/usbdmx/UsbDeviceManagerInterface.h new file mode 100644 index 0000000000..281ae7f2f0 --- /dev/null +++ b/plugins/usbdmx/UsbDeviceManagerInterface.h @@ -0,0 +1,110 @@ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * 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 Library 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * UsbDeviceManagerInterface.h + * The interface for the USB Device Managers + * Copyright (C) 2014 Simon Newton + */ + +#ifndef PLUGINS_USBDMX_USBDEVICEMANAGERINTERFACE_H_ +#define PLUGINS_USBDMX_USBDEVICEMANAGERINTERFACE_H_ + +#include +#include +#include "ola/base/Macro.h" +#include "ola/stl/STLUtils.h" +#include "ola/Logging.h" +#include "olad/Plugin.h" +#include "olad/PluginAdaptor.h" + +namespace ola { +namespace plugin { +namespace usbdmx { + +/** + * @brief Manages a particular type of USB Device. + */ +class UsbDeviceManagerInterface { + public: + virtual ~UsbDeviceManagerInterface() {} + + virtual bool DeviceAdded( + libusb_device *device, + const struct libusb_device_descriptor &descriptor) = 0; + + virtual void DeviceRemoved(libusb_device *device) = 0; +}; + +template +class BaseDeviceFactory : public UsbDeviceManagerInterface { + public: + BaseDeviceFactory(ola::PluginAdaptor *plugin_adaptor, ola::Plugin *plugin) + : m_plugin_adaptor(plugin_adaptor), + m_plugin(plugin) { + } + + void DeviceRemoved(libusb_device *device); + + protected: + bool HasDevice(libusb_device *device) { + return STLContains(m_device_map, device); + } + + ola::Plugin* ParentPlugin() { return m_plugin; } + + bool RegisterDevice(libusb_device *usb_device, DeviceClass *device); + + private: + typedef std::map DeviceMap; + + ola::PluginAdaptor *m_plugin_adaptor; + ola::Plugin *m_plugin; + DeviceMap m_device_map; + + DISALLOW_COPY_AND_ASSIGN(BaseDeviceFactory); +}; + +template +bool BaseDeviceFactory::RegisterDevice(libusb_device *usb_device, + DeviceClass *device) { + if (!device->Start()) { + delete device; + return false; + } + + DeviceClass *old_device = STLReplacePtr(&m_device_map, usb_device, device); + if (old_device) { + m_plugin_adaptor->UnregisterDevice(old_device); + old_device->Stop(); + delete old_device; + } + m_plugin_adaptor->RegisterDevice(device); + return true; +} + +template +void BaseDeviceFactory::DeviceRemoved(libusb_device *usb_device) { + OLA_INFO << "Removing device " << usb_device; + DeviceClass *device = STLLookupAndRemovePtr(&m_device_map, usb_device); + if (device) { + m_plugin_adaptor->UnregisterDevice(device); + device->Stop(); + delete device; + } +} +} // namespace usbdmx +} // namespace plugin +} // namespace ola +#endif // PLUGINS_USBDMX_USBDEVICEMANAGERINTERFACE_H_ diff --git a/plugins/usbdmx/UsbDmxPlugin.cpp b/plugins/usbdmx/UsbDmxPlugin.cpp index 982727fe10..f64706c32b 100644 --- a/plugins/usbdmx/UsbDmxPlugin.cpp +++ b/plugins/usbdmx/UsbDmxPlugin.cpp @@ -14,44 +14,30 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * * UsbDmxPlugin.cpp - * The UsbDmx plugin for ola + * The UsbDmx plugin for OLA. * Copyright (C) 2006 Simon Newton */ -#include -#include -#include -#include +#include "plugins/usbdmx/UsbDmxPlugin.h" #include -#include -#include -#include "ola/Callback.h" #include "ola/Logging.h" -#include "ola/StringUtils.h" -#include "ola/io/Descriptor.h" -#include "olad/PluginAdaptor.h" +#include "ola/base/Flags.h" #include "olad/Preferences.h" +#include "plugins/usbdmx/AsyncPluginImpl.h" +#include "plugins/usbdmx/LibUsbAdaptor.h" +#include "plugins/usbdmx/PluginImplInterface.h" +#include "plugins/usbdmx/SyncPluginImpl.h" -#include "plugins/usbdmx/AnymaDevice.h" -#include "plugins/usbdmx/EuroliteProDevice.h" -#include "plugins/usbdmx/FirmwareLoader.h" -#include "plugins/usbdmx/SunliteDevice.h" -#include "plugins/usbdmx/SunliteFirmwareLoader.h" -#include "plugins/usbdmx/UsbDevice.h" -#include "plugins/usbdmx/UsbDmxPlugin.h" -#include "plugins/usbdmx/VellemanDevice.h" - +DEFINE_default_bool(use_async_libusb, true, + "Use the asyncronous libusb calls."); namespace ola { namespace plugin { namespace usbdmx { -using ola::io::DeviceDescriptor; -using std::pair; using std::string; -using std::vector; const char UsbDmxPlugin::PLUGIN_NAME[] = "USB"; const char UsbDmxPlugin::PLUGIN_PREFIX[] = "usbdmx"; @@ -60,186 +46,49 @@ int UsbDmxPlugin::LIBUSB_DEFAULT_DEBUG_LEVEL = 0; int UsbDmxPlugin::LIBUSB_MAX_DEBUG_LEVEL = 3; -/* - * Called by libusb when a new descriptor is created. - */ -void libusb_fd_added(int fd, short events, void *data) { // NOLINT(runtime/int) - UsbDmxPlugin *plugin = static_cast(data); - - OLA_INFO << "USB new FD: " << fd; - plugin->AddDeviceDescriptor(fd); - (void) events; -} - - -/* - * Called by libusb when a descriptor is no longer needed. - */ -void libusb_fd_removed(int fd, void *data) { - UsbDmxPlugin *plugin = static_cast(data); - OLA_INFO << "USB rm FD: " << fd; - plugin->RemoveDeviceDescriptor(fd); +UsbDmxPlugin::UsbDmxPlugin(PluginAdaptor *plugin_adaptor) + : Plugin(plugin_adaptor) { } +UsbDmxPlugin::~UsbDmxPlugin() {} -/* - * Start the plugin - */ bool UsbDmxPlugin::StartHook() { - if (libusb_init(NULL)) { - OLA_WARN << "Failed to init libusb"; - return false; + if (m_impl.get()) { + return true; } unsigned int debug_level; if (!StringToInt(m_preferences->GetValue(LIBUSB_DEBUG_LEVEL_KEY) , - &debug_level)) + &debug_level)) { debug_level = LIBUSB_DEFAULT_DEBUG_LEVEL; - - OLA_DEBUG << "libusb debug level set to " << debug_level; - libusb_set_debug(NULL, debug_level); - - if (LoadFirmware()) { - // we loaded firmware for at least one device, set up a callback to run in - // a couple of seconds to re-scan for devices - m_plugin_adaptor->RegisterSingleTimeout( - 3500, - NewSingleCallback(this, &UsbDmxPlugin::FindDevices)); } - FindDevices(); - /* - if (!libusb_pollfds_handle_timeouts(m_usb_context)) { - OLA_FATAL << "libusb doesn't have timeout support, BAD"; - return false; + std::auto_ptr libusb_adaptor(new LibUsbAdaptor(debug_level)); + std::auto_ptr impl; + if (FLAGS_use_async_libusb) { + impl.reset( + new AsyncPluginImpl(m_plugin_adaptor, this, libusb_adaptor.get())); + } else { + impl.reset( + new SyncPluginImpl(m_plugin_adaptor, this, libusb_adaptor.get())); } - libusb_set_pollfd_notifiers(m_usb_context, - &libusb_fd_added, - &libusb_fd_removed, - this); - - const libusb_pollfd **pollfds = libusb_get_pollfds(m_usb_context); - while (*pollfds) { - OLA_WARN << "poll fd " << (*pollfds)->fd; - AddDeviceDescriptor((*pollfds)->fd); - pollfds++; - } - */ - - return true; -} - - -/* - * Load firmware onto devices if required. - * @returns true if we loaded firmware for one or more devices - */ -bool UsbDmxPlugin::LoadFirmware() { - libusb_device **device_list; - size_t device_count = libusb_get_device_list(NULL, &device_list); - FirmwareLoader *loader; - bool loaded = false; - - for (unsigned int i = 0; i < device_count; i++) { - libusb_device *usb_device = device_list[i]; - loader = NULL; - struct libusb_device_descriptor device_descriptor; - libusb_get_device_descriptor(usb_device, &device_descriptor); - - if (device_descriptor.idVendor == 0x0962 && - device_descriptor.idProduct == 0x2000) { - loader = new SunliteFirmwareLoader(usb_device); - } - - if (loader) { - loader->LoadFirmware(); - loaded = true; - delete loader; - } - } - libusb_free_device_list(device_list, 1); // unref devices - return loaded; -} - - -/* - * Find known devices & register them - */ -void UsbDmxPlugin::FindDevices() { - libusb_device **device_list; - size_t device_count = libusb_get_device_list(NULL, &device_list); - - for (unsigned int i = 0; i < device_count; i++) { - libusb_device *usb_device = device_list[i]; - struct libusb_device_descriptor device_descriptor; - libusb_get_device_descriptor(usb_device, &device_descriptor); - UsbDevice *device = NULL; - - pair bus_dev_id(libusb_get_bus_number(usb_device), - libusb_get_device_address(usb_device)); - - if ((m_registered_devices.find(bus_dev_id) != m_registered_devices.end())) - continue; - - if (device_descriptor.idVendor == 0x10cf && - device_descriptor.idProduct == 0x8062) { - OLA_INFO << "Found a Velleman USB device"; - device = new VellemanDevice(this, usb_device); - } else if (device_descriptor.idVendor == 0x0962 && - device_descriptor.idProduct == 0x2001) { - OLA_INFO << "Found a Sunlite device"; - device = new SunliteDevice(this, usb_device); - } else if (device_descriptor.idVendor == 0x16C0 && - device_descriptor.idProduct == 0x05DC) { - OLA_INFO << "Found an Anyma device"; - device = NewAnymaDevice(usb_device, device_descriptor); - } else if (device_descriptor.idVendor == 0x04d8 && - device_descriptor.idProduct == 0xfa63) { - OLA_INFO << "Found a EUROLITE device"; - device = new EuroliteProDevice(this, usb_device); - } - - if (device) { - if (!device->Start()) { - delete device; - continue; - } - m_registered_devices.insert(bus_dev_id); - m_devices.push_back(device); - m_plugin_adaptor->RegisterDevice(device); - } + if (impl->Start()) { + m_libusb_adaptor.reset(libusb_adaptor.release()); + m_impl.reset(impl.release()); + return true; + } else { + return false; } - libusb_free_device_list(device_list, 1); // unref devices } - -/* - * Stop the plugin - * @return true on success, false on failure - */ bool UsbDmxPlugin::StopHook() { - vector::iterator iter; - for (iter = m_devices.begin(); iter != m_devices.end(); ++iter) { - m_plugin_adaptor->UnregisterDevice(*iter); - (*iter)->Stop(); - delete *iter; - } - m_devices.clear(); - m_registered_devices.clear(); - - libusb_exit(NULL); - - if (!m_descriptors.empty()) { - OLA_WARN << "libusb is still using descriptor, this is a bug"; + if (m_impl.get()) { + m_impl->Stop(); } return true; } - -/* - * Return the description for this plugin - */ string UsbDmxPlugin::Description() const { return "USB DMX Plugin\n" @@ -256,10 +105,6 @@ string UsbDmxPlugin::Description() const { "\n"; } - -/* - * Default to sensible values - */ bool UsbDmxPlugin::SetDefaultPreferences() { if (!m_preferences) return false; @@ -274,169 +119,6 @@ bool UsbDmxPlugin::SetDefaultPreferences() { return true; } - - -bool UsbDmxPlugin::AddDeviceDescriptor(int fd) { - vector::const_iterator iter = m_descriptors.begin(); - for (; iter != m_descriptors.end(); ++iter) { - if ((*iter)->ReadDescriptor() == fd) - return true; - } - DeviceDescriptor *socket = new DeviceDescriptor(fd); - socket->SetOnData(NewCallback(this, &UsbDmxPlugin::SocketReady)); - m_plugin_adaptor->AddReadDescriptor(socket); - m_descriptors.push_back(socket); - return true; -} - - -bool UsbDmxPlugin::RemoveDeviceDescriptor(int fd) { - vector::iterator iter = m_descriptors.begin(); - for (; iter != m_descriptors.end(); ++iter) { - if ((*iter)->ReadDescriptor() == fd) { - m_plugin_adaptor->RemoveReadDescriptor(*iter); - delete *iter; - m_descriptors.erase(iter); - return true; - } - } - return false; -} - - -/* - * Called when there is activity on one of our sockets. - */ -void UsbDmxPlugin::SocketReady() { - struct timeval tv; - tv.tv_sec = 0; - tv.tv_usec = 0; - libusb_handle_events_timeout(NULL, &tv); -} - - -/** - * Create a new AnymaDevice. Some Anyma devices don't have serial numbers, so - * we can only support one of those. - */ -UsbDevice* UsbDmxPlugin::NewAnymaDevice( - libusb_device *usb_device, - const struct libusb_device_descriptor &device_descriptor) { - libusb_device_handle *usb_handle; - if (libusb_open(usb_device, &usb_handle)) { - OLA_WARN << "Failed to open Anyma usb device"; - return NULL; - } - - USBDeviceInformation info; - GetDeviceInfo(usb_handle, device_descriptor, &info); - - if (!MatchManufacturer(AnymaDevice::EXPECTED_MANUFACTURER, - info.manufacturer)) { - libusb_close(usb_handle); - return NULL; - } - - if (!MatchProduct(AnymaDevice::EXPECTED_PRODUCT, info.product)) { - libusb_close(usb_handle); - return NULL; - } - - if (info.serial.empty()) { - if (m_anyma_devices_missing_serial_numbers) { - OLA_WARN << "Failed to read serial number or serial number empty. " - << "We can only support one device without a serial number."; - return NULL; - } else { - OLA_WARN << "Failed to read serial number from " << info.manufacturer - << " : " << info.product - << " the device probably doesn't have one"; - m_anyma_devices_missing_serial_numbers = true; - } - } - - if (libusb_claim_interface(usb_handle, 0)) { - OLA_WARN << "Failed to claim Anyma usb device"; - libusb_close(usb_handle); - return NULL; - } - return new AnymaDevice(this, usb_device, usb_handle, info.serial); -} - - -/** - * Get the Manufacturer, Product and Serial number strings for a device. - */ -void UsbDmxPlugin::GetDeviceInfo( - libusb_device_handle *usb_handle, - const struct libusb_device_descriptor &device_descriptor, - USBDeviceInformation *device_info) { - if (!GetDescriptorString(usb_handle, device_descriptor.iManufacturer, - &device_info->manufacturer)) - OLA_INFO << "Failed to get manufacturer name"; - - if (!GetDescriptorString(usb_handle, device_descriptor.iProduct, - &device_info->product)) - OLA_INFO << "Failed to get product name"; - - if (!GetDescriptorString(usb_handle, device_descriptor.iSerialNumber, - &device_info->serial)) - OLA_WARN << "Failed to read serial number, the device probably doesn't " - << "have one"; -} - - -/** - * Check if the manufacturer string matches the expected value. Log a message - * if it doesn't. - */ -bool UsbDmxPlugin::MatchManufacturer(const string &expected, - const string &actual) { - if (expected != actual) { - OLA_WARN << "Manufacturer mismatch: " << expected << " != " << actual; - return false; - } - return true; -} - - -/** - * Check if the manufacturer string matches the expected value. Log a message - * if it doesn't. - */ -bool UsbDmxPlugin::MatchProduct(const string &expected, const string &actual) { - if (expected != actual) { - OLA_WARN << "Product mismatch: " << expected << " != " << actual; - return false; - } - return true; -} - -/* - * Return a string descriptor. - * @param usb_handle the usb handle to the device - * @param desc_index the index of the descriptor - * @param data where to store the output string - * @returns true if we got the value, false otherwise - */ -bool UsbDmxPlugin::GetDescriptorString(libusb_device_handle *usb_handle, - uint8_t desc_index, - string *data) { - enum { buffer_size = 32 }; // static arrays FTW! - unsigned char buffer[buffer_size]; - int r = libusb_get_string_descriptor_ascii( - usb_handle, - desc_index, - buffer, - buffer_size); - - if (r <= 0) { - OLA_INFO << "libusb_get_string_descriptor_ascii returned " << r; - return false; - } - data->assign(reinterpret_cast(buffer)); - return true; -} } // namespace usbdmx } // namespace plugin } // namespace ola diff --git a/plugins/usbdmx/UsbDmxPlugin.h b/plugins/usbdmx/UsbDmxPlugin.h index 3897d1c007..46fd9a23c3 100644 --- a/plugins/usbdmx/UsbDmxPlugin.h +++ b/plugins/usbdmx/UsbDmxPlugin.h @@ -21,72 +21,55 @@ #ifndef PLUGINS_USBDMX_USBDMXPLUGIN_H_ #define PLUGINS_USBDMX_USBDMXPLUGIN_H_ -#include +#include #include -#include -#include +#include "ola/base/Macro.h" #include "ola/plugin_id.h" #include "olad/Plugin.h" -#include "ola/io/Descriptor.h" namespace ola { namespace plugin { - namespace usbdmx { +/** + * @brief A plugin which uses libusb to comunicate with devices. + * + * This Plugin supports a number of USB dongles including + * - Anyma + * - Eurolite + * - Sunlite + * - Velleman + */ class UsbDmxPlugin: public ola::Plugin { public: - explicit UsbDmxPlugin(PluginAdaptor *plugin_adaptor): - Plugin(plugin_adaptor), - m_anyma_devices_missing_serial_numbers(false) { - } + /** + * @brief Create a new UsbDmxPlugin. + * @param plugin_adaptor The PluginAdaptor to use, ownership is not + * transferred. + */ + explicit UsbDmxPlugin(PluginAdaptor *plugin_adaptor); + ~UsbDmxPlugin(); - std::string Name() const { return PLUGIN_NAME; } - std::string Description() const; - ola_plugin_id Id() const { return OLA_PLUGIN_USBDMX; } - std::string PluginPrefix() const { return PLUGIN_PREFIX; } - - bool AddDeviceDescriptor(int fd); - bool RemoveDeviceDescriptor(int fd); - void SocketReady(); + std::string Name() const { return PLUGIN_NAME; } + std::string Description() const; + ola_plugin_id Id() const { return OLA_PLUGIN_USBDMX; } + std::string PluginPrefix() const { return PLUGIN_PREFIX; } private: - struct USBDeviceInformation { - std::string manufacturer; - std::string product; - std::string serial; - }; - - bool m_anyma_devices_missing_serial_numbers; - std::vector m_devices; // list of our devices - std::vector m_descriptors; - std::set > m_registered_devices; + std::auto_ptr m_libusb_adaptor; + std::auto_ptr m_impl; - bool StartHook(); - bool LoadFirmware(); - void FindDevices(); - bool StopHook(); - bool SetDefaultPreferences(); - class UsbDevice* NewAnymaDevice( - struct libusb_device *usb_device, - const struct libusb_device_descriptor &device_descriptor); + bool StartHook(); + bool StopHook(); + bool SetDefaultPreferences(); - void GetDeviceInfo( - struct libusb_device_handle *usb_handle, - const struct libusb_device_descriptor &device_descriptor, - USBDeviceInformation *device_info); - bool MatchManufacturer(const std::string &expected, - const std::string &actual); - bool MatchProduct(const std::string &expected, const std::string &actual); - bool GetDescriptorString(libusb_device_handle *usb_handle, - uint8_t desc_index, - std::string *data); + static const char PLUGIN_NAME[]; + static const char PLUGIN_PREFIX[]; + static const char LIBUSB_DEBUG_LEVEL_KEY[]; + static int LIBUSB_DEFAULT_DEBUG_LEVEL; + static int LIBUSB_MAX_DEBUG_LEVEL; - static const char PLUGIN_NAME[]; - static const char PLUGIN_PREFIX[]; - static const char LIBUSB_DEBUG_LEVEL_KEY[]; - static int LIBUSB_DEFAULT_DEBUG_LEVEL; - static int LIBUSB_MAX_DEBUG_LEVEL; + DISALLOW_COPY_AND_ASSIGN(UsbDmxPlugin); }; } // namespace usbdmx } // namespace plugin diff --git a/plugins/usbdmx/VellemanDevice.h b/plugins/usbdmx/VellemanDevice.h index 8e2cc1d2d2..d5717067b0 100644 --- a/plugins/usbdmx/VellemanDevice.h +++ b/plugins/usbdmx/VellemanDevice.h @@ -23,6 +23,7 @@ #include #include +#include "ola/base/Macro.h" #include "plugins/usbdmx/UsbDevice.h" namespace ola { @@ -34,15 +35,18 @@ namespace usbdmx { */ class VellemanDevice: public UsbDevice { public: - VellemanDevice(ola::AbstractPlugin *owner, - libusb_device *usb_device): - UsbDevice(owner, "Velleman USB Device", usb_device) { - } + VellemanDevice(ola::AbstractPlugin *owner, + libusb_device *usb_device): + UsbDevice(owner, "Velleman USB Device", usb_device) { + } - std::string DeviceId() const { return "velleman"; } + std::string DeviceId() const { return "velleman"; } protected: - bool StartHook(); + bool StartHook(); + + private: + DISALLOW_COPY_AND_ASSIGN(VellemanDevice); }; } // namespace usbdmx } // namespace plugin diff --git a/plugins/usbdmx/VellemanOutputPort.h b/plugins/usbdmx/VellemanOutputPort.h index 4ee4e58131..3fe2ae30d8 100644 --- a/plugins/usbdmx/VellemanOutputPort.h +++ b/plugins/usbdmx/VellemanOutputPort.h @@ -31,6 +31,7 @@ #include #include #include +#include "ola/base/Macro.h" #include "ola/DmxBuffer.h" #include "ola/thread/Thread.h" #include "olad/Port.h" @@ -72,6 +73,8 @@ class VellemanOutputPort: public BasicOutputPort, ola::thread::Thread { bool SendDMX(const DmxBuffer &buffer_old); bool SendDataChunk(uint8_t *usb_data); + + DISALLOW_COPY_AND_ASSIGN(VellemanOutputPort); }; } // namespace usbdmx } // namespace plugin From 82c316606202c2bcd2acf0ec889bc97c6ec743f5 Mon Sep 17 00:00:00 2001 From: Simon Newton Date: Wed, 12 Nov 2014 08:52:18 -0800 Subject: [PATCH 02/34] Continue the USB plugin cleanup. --- plugins/usbdmx/AnymaDevice.cpp | 8 +- plugins/usbdmx/AnymaDevice.h | 3 +- plugins/usbdmx/AnymaOutputPort.cpp | 3 +- plugins/usbdmx/AnymaOutputPort.h | 4 +- plugins/usbdmx/AnymaWidget.cpp | 21 ++- plugins/usbdmx/AnymaWidget.h | 30 +++- ...viceManager.cpp => AnymaWidgetFactory.cpp} | 34 ++-- ...maDeviceManager.h => AnymaWidgetFactory.h} | 31 ++-- plugins/usbdmx/AsyncPluginImpl.cpp | 91 ++++++++-- plugins/usbdmx/AsyncPluginImpl.h | 30 +++- plugins/usbdmx/EuroliteProDevice.cpp | 6 +- plugins/usbdmx/EuroliteProDevice.h | 3 +- plugins/usbdmx/EuroliteProOutputPort.cpp | 5 +- plugins/usbdmx/EuroliteProOutputPort.h | 4 +- plugins/usbdmx/EuroliteProWidget.cpp | 32 ++-- plugins/usbdmx/EuroliteProWidget.h | 26 ++- ...nager.cpp => EuroliteProWidgetFactory.cpp} | 35 ++-- ...ceManager.h => EuroliteProWidgetFactory.h} | 29 ++-- plugins/usbdmx/Makefile.mk | 20 +-- plugins/usbdmx/SunliteDevice.cpp | 2 +- plugins/usbdmx/SunliteDevice.h | 2 +- plugins/usbdmx/SunliteDeviceManager.cpp | 67 -------- plugins/usbdmx/SunliteDeviceManager.h | 54 ------ plugins/usbdmx/SunliteOutputPort.cpp | 2 +- plugins/usbdmx/SunliteOutputPort.h | 4 +- plugins/usbdmx/SunliteWidget.cpp | 12 +- plugins/usbdmx/SunliteWidget.h | 8 +- plugins/usbdmx/SyncPluginImpl.cpp | 39 +++-- plugins/usbdmx/SyncPluginImpl.h | 2 + plugins/usbdmx/UsbDeviceManagerInterface.h | 110 ------------ plugins/usbdmx/WidgetFactory.h | 162 ++++++++++++++++++ 31 files changed, 463 insertions(+), 416 deletions(-) rename plugins/usbdmx/{AnymaDeviceManager.cpp => AnymaWidgetFactory.cpp} (70%) rename plugins/usbdmx/{AnymaDeviceManager.h => AnymaWidgetFactory.h} (55%) rename plugins/usbdmx/{EuroliteProDeviceManager.cpp => EuroliteProWidgetFactory.cpp} (68%) rename plugins/usbdmx/{EuroliteProDeviceManager.h => EuroliteProWidgetFactory.h} (60%) delete mode 100644 plugins/usbdmx/SunliteDeviceManager.cpp delete mode 100644 plugins/usbdmx/SunliteDeviceManager.h delete mode 100644 plugins/usbdmx/UsbDeviceManagerInterface.h create mode 100644 plugins/usbdmx/WidgetFactory.h diff --git a/plugins/usbdmx/AnymaDevice.cpp b/plugins/usbdmx/AnymaDevice.cpp index c65830d066..fce1d2e3c6 100644 --- a/plugins/usbdmx/AnymaDevice.cpp +++ b/plugins/usbdmx/AnymaDevice.cpp @@ -20,19 +20,17 @@ #include "plugins/usbdmx/AnymaDevice.h" -#include -#include "ola/Logging.h" #include "plugins/usbdmx/AnymaOutputPort.h" +#include "plugins/usbdmx/AnymaWidget.h" namespace ola { namespace plugin { namespace usbdmx { AnymaDevice::AnymaDevice(ola::AbstractPlugin *owner, - AnymaWidgetInterface *widget, - const std::string &serial) + AnymaWidget *widget) : Device(owner, "Anyma USB Device"), - m_device_id("anyma-" + serial), + m_device_id("anyma-" + widget->SerialNumber()), m_port(new AnymaOutputPort(this, 0, widget)) { } diff --git a/plugins/usbdmx/AnymaDevice.h b/plugins/usbdmx/AnymaDevice.h index 5b651fa565..482fe903be 100644 --- a/plugins/usbdmx/AnymaDevice.h +++ b/plugins/usbdmx/AnymaDevice.h @@ -36,8 +36,7 @@ namespace usbdmx { class AnymaDevice: public Device { public: AnymaDevice(ola::AbstractPlugin *owner, - class AnymaWidgetInterface *widget, - const std::string &serial); + class AnymaWidget *widget); std::string DeviceId() const { return m_device_id; diff --git a/plugins/usbdmx/AnymaOutputPort.cpp b/plugins/usbdmx/AnymaOutputPort.cpp index e9b7c7dcee..f2531dcb7d 100644 --- a/plugins/usbdmx/AnymaOutputPort.cpp +++ b/plugins/usbdmx/AnymaOutputPort.cpp @@ -30,7 +30,7 @@ namespace usbdmx { AnymaOutputPort::AnymaOutputPort(AnymaDevice *parent, unsigned int id, - AnymaWidgetInterface *widget) + AnymaWidget *widget) : BasicOutputPort(parent, id), m_widget(widget) { } @@ -38,7 +38,6 @@ AnymaOutputPort::AnymaOutputPort(AnymaDevice *parent, AnymaOutputPort::~AnymaOutputPort() { // TODO(simon): stop the thread here?? OLA_INFO << "AnymaOutputPort::~AnymaOutputPort()"; - delete m_widget; } bool AnymaOutputPort::WriteDMX(const DmxBuffer &buffer, diff --git a/plugins/usbdmx/AnymaOutputPort.h b/plugins/usbdmx/AnymaOutputPort.h index 7c5b16b023..056af5945a 100644 --- a/plugins/usbdmx/AnymaOutputPort.h +++ b/plugins/usbdmx/AnymaOutputPort.h @@ -39,7 +39,7 @@ class AnymaOutputPort: public BasicOutputPort { */ AnymaOutputPort(AnymaDevice *parent, unsigned int id, - class AnymaWidgetInterface *widget); + class AnymaWidget *widget); /** * @brief Cleanup. */ @@ -50,7 +50,7 @@ class AnymaOutputPort: public BasicOutputPort { std::string Description() const { return ""; } private: - class AnymaWidgetInterface* const m_widget; + class AnymaWidget* const m_widget; DISALLOW_COPY_AND_ASSIGN(AnymaOutputPort); }; diff --git a/plugins/usbdmx/AnymaWidget.cpp b/plugins/usbdmx/AnymaWidget.cpp index 21c27d6053..5ac361fdfa 100644 --- a/plugins/usbdmx/AnymaWidget.cpp +++ b/plugins/usbdmx/AnymaWidget.cpp @@ -21,16 +21,21 @@ #include "plugins/usbdmx/AnymaWidget.h" #include +#include + #include "ola/Logging.h" #include "ola/Constants.h" #include "plugins/usbdmx/LibUsbHelper.h" +#include "plugins/usbdmx/ThreadedUsbSender.h" namespace ola { namespace plugin { namespace usbdmx { -const char AnymaWidgetInterface::EXPECTED_MANUFACTURER[] = "www.anyma.ch"; -const char AnymaWidgetInterface::EXPECTED_PRODUCT[] = "uDMX"; +const char AnymaWidget::EXPECTED_MANUFACTURER[] = "www.anyma.ch"; +const char AnymaWidget::EXPECTED_PRODUCT[] = "uDMX"; + +using std::string; namespace { @@ -84,8 +89,10 @@ bool AnymaThreadedSender::TransmitBuffer(libusb_device_handle *handle, } -SynchronousAnymaWidget::SynchronousAnymaWidget(libusb_device *usb_device) - : m_usb_device(usb_device) { +SynchronousAnymaWidget::SynchronousAnymaWidget(libusb_device *usb_device, + const string &serial) + : AnymaWidget(serial), + m_usb_device(usb_device) { } bool SynchronousAnymaWidget::Init() { @@ -111,8 +118,10 @@ bool SynchronousAnymaWidget::SendDMX(const DmxBuffer &buffer) { } AsynchronousAnymaWidget::AsynchronousAnymaWidget( - libusb_device *usb_device) - : m_usb_device(usb_device), + libusb_device *usb_device, + const string &serial) + : AnymaWidget(serial), + m_usb_device(usb_device), m_usb_handle(NULL), m_control_setup_buffer(NULL), m_transfer_state(IDLE) { diff --git a/plugins/usbdmx/AnymaWidget.h b/plugins/usbdmx/AnymaWidget.h index 3db8da9612..0bfadae5ca 100644 --- a/plugins/usbdmx/AnymaWidget.h +++ b/plugins/usbdmx/AnymaWidget.h @@ -22,9 +22,12 @@ #define PLUGINS_USBDMX_ANYMAWIDGET_H_ #include -#include "ola/base/Macro.h" +#include + #include "ola/DmxBuffer.h" -#include "plugins/usbdmx/ThreadedUsbSender.h" +#include "ola/base/Macro.h" +#include "ola/thread/Mutex.h" +#include "plugins/usbdmx/Widget.h" namespace ola { namespace plugin { @@ -33,16 +36,25 @@ namespace usbdmx { /** * @brief The interface for the Anyma Widgets */ -class AnymaWidgetInterface { +class AnymaWidget: public Widget { public: - virtual ~AnymaWidgetInterface() {} + explicit AnymaWidget(const std::string &serial) : m_serial(serial) {} + + virtual ~AnymaWidget() {} virtual bool Init() = 0; virtual bool SendDMX(const DmxBuffer &buffer) = 0; + std::string SerialNumber() const { + return m_serial; + } + static const char EXPECTED_MANUFACTURER[]; static const char EXPECTED_PRODUCT[]; + + private: + std::string m_serial; }; /** @@ -50,9 +62,10 @@ class AnymaWidgetInterface { * * Internally this spawns a new thread to avoid blocking SendDMX() calls. */ -class SynchronousAnymaWidget: public AnymaWidgetInterface { +class SynchronousAnymaWidget: public AnymaWidget { public: - explicit SynchronousAnymaWidget(libusb_device *usb_device); + SynchronousAnymaWidget(libusb_device *usb_device, + const std::string &serial); bool Init(); @@ -68,9 +81,10 @@ class SynchronousAnymaWidget: public AnymaWidgetInterface { /** * @brief An Anyma widget that uses asynchronous libusb operations. */ -class AsynchronousAnymaWidget : public AnymaWidgetInterface { +class AsynchronousAnymaWidget : public AnymaWidget { public: - explicit AsynchronousAnymaWidget(libusb_device *usb_device); + AsynchronousAnymaWidget(libusb_device *usb_device, + const std::string &serial); ~AsynchronousAnymaWidget(); bool Init(); diff --git a/plugins/usbdmx/AnymaDeviceManager.cpp b/plugins/usbdmx/AnymaWidgetFactory.cpp similarity index 70% rename from plugins/usbdmx/AnymaDeviceManager.cpp rename to plugins/usbdmx/AnymaWidgetFactory.cpp index 14a5a1036d..28424a9358 100644 --- a/plugins/usbdmx/AnymaDeviceManager.cpp +++ b/plugins/usbdmx/AnymaWidgetFactory.cpp @@ -13,15 +13,14 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * - * AnymaDeviceManager.cpp - * The Anyma Device Manager + * AnymaWidgetFactory.cpp + * The WidgetFactory for Anyma widgets. * Copyright (C) 2014 Simon Newton */ -#include "plugins/usbdmx/AnymaDeviceManager.h" +#include "plugins/usbdmx/AnymaWidgetFactory.h" #include "ola/Logging.h" -#include "plugins/usbdmx/AnymaDevice.h" #include "plugins/usbdmx/AnymaWidget.h" #include "plugins/usbdmx/LibUsbHelper.h" @@ -29,13 +28,15 @@ namespace ola { namespace plugin { namespace usbdmx { -const uint16_t AnymaDeviceManager::ANYMA_VENDOR_ID = 0x16C0; +const uint16_t AnymaWidgetFactory::ANYMA_VENDOR_ID = 0x16C0; +const uint16_t AnymaWidgetFactory::ANYMA_PRODUCT_ID = 0x05DC; -bool AnymaDeviceManager::DeviceAdded( +bool AnymaWidgetFactory::DeviceAdded( + WidgetObserver *observer, libusb_device *usb_device, const struct libusb_device_descriptor &descriptor) { if (descriptor.idVendor != ANYMA_VENDOR_ID || - descriptor.idProduct != 0x05DC || + descriptor.idProduct != ANYMA_PRODUCT_ID || HasDevice(usb_device)) { return false; } @@ -47,15 +48,18 @@ bool AnymaDeviceManager::DeviceAdded( } if (!LibUsbHelper::CheckManufacturer( - AnymaWidgetInterface::EXPECTED_MANUFACTURER, info.manufacturer)) { + AnymaWidget::EXPECTED_MANUFACTURER, info.manufacturer)) { return false; } - if (!LibUsbHelper::CheckProduct(AnymaWidgetInterface::EXPECTED_PRODUCT, - info.product)) { + if (!LibUsbHelper::CheckProduct( + AnymaWidget::EXPECTED_PRODUCT, info.product)) { return false; } + // Some Anyma devices don't have serial numbers. Since there isn't another + // good way to uniquely identify a USB device, we only support one of these + // types of devices per host. if (info.serial.empty()) { if (m_missing_serial_number) { OLA_WARN << "Failed to read serial number or serial number empty. " @@ -69,14 +73,8 @@ bool AnymaDeviceManager::DeviceAdded( } } - AsynchronousAnymaWidget *widget = new AsynchronousAnymaWidget(usb_device); - if (!widget->Init()) { - delete widget; - return false; - } - - AnymaDevice *device = new AnymaDevice(ParentPlugin(), widget, info.serial); - return RegisterDevice(usb_device, device); + return AddWidget(observer, usb_device, + new AsynchronousAnymaWidget(usb_device, info.serial)); } } // namespace usbdmx } // namespace plugin diff --git a/plugins/usbdmx/AnymaDeviceManager.h b/plugins/usbdmx/AnymaWidgetFactory.h similarity index 55% rename from plugins/usbdmx/AnymaDeviceManager.h rename to plugins/usbdmx/AnymaWidgetFactory.h index efface5b07..bd9fa20f72 100644 --- a/plugins/usbdmx/AnymaDeviceManager.h +++ b/plugins/usbdmx/AnymaWidgetFactory.h @@ -13,17 +13,16 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * - * AnymaDeviceManager.h - * The Anyma Device Manager. + * AnymaWidgetFactory.h + * The WidgetFactory for Anyma widgets. * Copyright (C) 2014 Simon Newton */ -#ifndef PLUGINS_USBDMX_ANYMADEVICEMANAGER_H_ -#define PLUGINS_USBDMX_ANYMADEVICEMANAGER_H_ +#ifndef PLUGINS_USBDMX_ANYMAWIDGETFACTORY_H_ +#define PLUGINS_USBDMX_ANYMAWIDGETFACTORY_H_ #include "ola/base/Macro.h" -#include "plugins/usbdmx/UsbDeviceManagerInterface.h" -#include "plugins/usbdmx/AnymaDevice.h" +#include "plugins/usbdmx/WidgetFactory.h" namespace ola { namespace plugin { @@ -32,28 +31,24 @@ namespace usbdmx { /** * @brief Manages Anyma Devices */ -class AnymaDeviceManager : public BaseDeviceFactory { +class AnymaWidgetFactory : public BaseWidgetFactory { public: - AnymaDeviceManager(PluginAdaptor *plugin_adaptor, - Plugin *plugin) - : BaseDeviceFactory(plugin_adaptor, plugin), - m_missing_serial_number(false) {} + AnymaWidgetFactory() : m_missing_serial_number(false) {} bool DeviceAdded( - libusb_device *device, - const struct libusb_device_descriptor &descriptor); + WidgetObserver *observer, + libusb_device *usb_device, + const struct libusb_device_descriptor &descriptor); private: - // Some Anyma devices don't have serial numbers. Since there isn't another - // good way to uniquely identify a USB device, we only support one of these - // types of devices. bool m_missing_serial_number; static const uint16_t ANYMA_VENDOR_ID; + static const uint16_t ANYMA_PRODUCT_ID; - DISALLOW_COPY_AND_ASSIGN(AnymaDeviceManager); + DISALLOW_COPY_AND_ASSIGN(AnymaWidgetFactory); }; } // namespace usbdmx } // namespace plugin } // namespace ola -#endif // PLUGINS_USBDMX_ANYMADEVICEMANAGER_H_ +#endif // PLUGINS_USBDMX_ANYMAWIDGETFACTORY_H_ diff --git a/plugins/usbdmx/AsyncPluginImpl.cpp b/plugins/usbdmx/AsyncPluginImpl.cpp index b329ca99e6..a0972ac761 100644 --- a/plugins/usbdmx/AsyncPluginImpl.cpp +++ b/plugins/usbdmx/AsyncPluginImpl.cpp @@ -29,9 +29,13 @@ #include "ola/stl/STLUtils.h" #include "olad/PluginAdaptor.h" -#include "plugins/usbdmx/AnymaDeviceManager.h" -#include "plugins/usbdmx/SunliteDeviceManager.h" -#include "plugins/usbdmx/EuroliteProDeviceManager.h" +#include "plugins/usbdmx/AnymaWidgetFactory.h" +#include "plugins/usbdmx/AnymaWidget.h" +#include "plugins/usbdmx/AnymaDevice.h" +#include "plugins/usbdmx/EuroliteProWidgetFactory.h" +#include "plugins/usbdmx/EuroliteProDevice.h" +#include "plugins/usbdmx/SunliteWidgetFactory.h" +#include "plugins/usbdmx/SunliteDevice.h" #include "plugins/usbdmx/VellemanDevice.h" @@ -136,7 +140,9 @@ void *LibUsbThread::Run() { AsyncPluginImpl::AsyncPluginImpl(PluginAdaptor *plugin_adaptor, Plugin *plugin, LibUsbAdaptor *libusb_adaptor) - : m_libusb_adaptor(libusb_adaptor), + : m_plugin_adaptor(plugin_adaptor), + m_plugin(plugin), + m_libusb_adaptor(libusb_adaptor), m_context(NULL), m_use_hotplug(false), m_stopping(false) { @@ -144,14 +150,13 @@ AsyncPluginImpl::AsyncPluginImpl(PluginAdaptor *plugin_adaptor, m_hotplug_handle = 0; #endif - m_device_managers.push_back(new AnymaDeviceManager(plugin_adaptor, plugin)); - m_device_managers.push_back(new SunliteDeviceManager(plugin_adaptor, plugin)); - m_device_managers.push_back(new EuroliteProDeviceManager(plugin_adaptor, - plugin)); + m_widget_factories.push_back(new AnymaWidgetFactory()); + m_widget_factories.push_back(new EuroliteProWidgetFactory()); + m_widget_factories.push_back(new SunliteWidgetFactory()); } AsyncPluginImpl::~AsyncPluginImpl() { - STLDeleteElements(&m_device_managers); + STLDeleteElements(&m_widget_factories); } bool AsyncPluginImpl::Start() { @@ -193,9 +198,9 @@ bool AsyncPluginImpl::Stop() { m_usb_thread.reset(); // I think we need a lock here - DeviceToFactoryMap::iterator iter = m_device_factory_map.begin(); + USBDeviceToFactoryMap::iterator iter = m_device_factory_map.begin(); for (; iter != m_device_factory_map.end(); ++iter) { - iter->second->DeviceRemoved(iter->first); + iter->second->DeviceRemoved(this, iter->first); } m_device_factory_map.clear(); @@ -218,6 +223,60 @@ void AsyncPluginImpl::HotPlugEvent(struct libusb_device *usb_device, } #endif +bool AsyncPluginImpl::NewWidget(class Widget *widget) { + (void) widget; + return false; +} + +bool AsyncPluginImpl::NewWidget(class AnymaWidget *widget) { + AnymaDevice *device = new AnymaDevice(m_plugin, widget); + + if (!device->Start()) { + delete device; + return false; + } + + Device *old_device = STLReplacePtr(&m_widget_device_map, widget, device); + if (old_device) { + m_plugin_adaptor->UnregisterDevice(old_device); + old_device->Stop(); + delete old_device; + } + m_plugin_adaptor->RegisterDevice(device); + return true; +} + +bool AsyncPluginImpl::NewWidget(class EuroliteProWidget *widget) { + (void) widget; + return false; +} + +bool AsyncPluginImpl::NewWidget(class SunliteWidget *widget) { + (void) widget; + return false; +} + +void AsyncPluginImpl::WidgetRemoved(class Widget *widget) { + (void) widget; +} + +void AsyncPluginImpl::WidgetRemoved(class AnymaWidget *widget) { + Device *device = STLLookupAndRemovePtr(&m_widget_device_map, widget); + if (device) { + m_plugin_adaptor->UnregisterDevice(device); + device->Stop(); + delete device; + } +} + +void AsyncPluginImpl::WidgetRemoved(class EuroliteProWidget *widget) { + (void) widget; +} + +void AsyncPluginImpl::WidgetRemoved(class SunliteWidget *widget) { + (void) widget; +} + bool AsyncPluginImpl::SetupHotPlug() { #if defined(LIBUSB_API_VERSION) && (LIBUSB_API_VERSION >= 0x01000102) if (libusb_has_capability(LIBUSB_CAP_HAS_HOTPLUG) == 0) { @@ -261,9 +320,9 @@ void AsyncPluginImpl::DeviceAdded(libusb_device *usb_device) { struct libusb_device_descriptor device_descriptor; libusb_get_device_descriptor(usb_device, &device_descriptor); - DeviceManagers::iterator iter = m_device_managers.begin(); - for (; iter != m_device_managers.end(); ++iter) { - if ((*iter)->DeviceAdded(usb_device, device_descriptor)) { + WidgetFactories::iterator iter = m_widget_factories.begin(); + for (; iter != m_widget_factories.end(); ++iter) { + if ((*iter)->DeviceAdded(this, usb_device, device_descriptor)) { STLReplacePtr(&m_device_factory_map, usb_device, *iter); return; } @@ -279,10 +338,10 @@ void AsyncPluginImpl::DeviceAdded(libusb_device *usb_device) { } void AsyncPluginImpl::DeviceRemoved(libusb_device *usb_device) { - UsbDeviceManagerInterface *factory = STLLookupAndRemovePtr( + WidgetFactory *factory = STLLookupAndRemovePtr( &m_device_factory_map, usb_device); if (factory) { - factory->DeviceRemoved(usb_device); + factory->DeviceRemoved(this, usb_device); } } } // namespace usbdmx diff --git a/plugins/usbdmx/AsyncPluginImpl.h b/plugins/usbdmx/AsyncPluginImpl.h index 3f696ce623..d041a9bc5e 100644 --- a/plugins/usbdmx/AsyncPluginImpl.h +++ b/plugins/usbdmx/AsyncPluginImpl.h @@ -32,9 +32,12 @@ #include "ola/thread/Thread.h" #include "plugins/usbdmx/LibUsbAdaptor.h" #include "plugins/usbdmx/PluginImplInterface.h" -#include "plugins/usbdmx/UsbDeviceManagerInterface.h" +#include "plugins/usbdmx/WidgetFactory.h" namespace ola { + +class Device; + namespace plugin { namespace usbdmx { @@ -45,7 +48,7 @@ namespace usbdmx { /** * @brief The asynchronous libusb implementation. */ -class AsyncPluginImpl: public PluginImplInterface { +class AsyncPluginImpl: public PluginImplInterface, public WidgetObserver { public: /** * @brief Create a new AsyncPluginImpl. @@ -80,10 +83,20 @@ class AsyncPluginImpl: public PluginImplInterface { libusb_hotplug_event event); #endif + bool NewWidget(class Widget *widget); + bool NewWidget(class AnymaWidget *widget); + bool NewWidget(class EuroliteProWidget *widget); + bool NewWidget(class SunliteWidget *widget); + + void WidgetRemoved(class Widget *widget); + void WidgetRemoved(class AnymaWidget *widget); + void WidgetRemoved(class EuroliteProWidget *widget); + void WidgetRemoved(class SunliteWidget *widget); + private: - typedef std::vector DeviceManagers; - typedef std::map - DeviceToFactoryMap; + typedef std::vector WidgetFactories; + typedef std::map USBDeviceToFactoryMap; + typedef std::map WidgetToDeviceMap; struct USBDeviceInformation { std::string manufacturer; @@ -91,14 +104,17 @@ class AsyncPluginImpl: public PluginImplInterface { std::string serial; }; + PluginAdaptor* const m_plugin_adaptor; + Plugin* const m_plugin; LibUsbAdaptor* const m_libusb_adaptor; libusb_context *m_context; bool m_use_hotplug; bool m_stopping; std::auto_ptr m_usb_thread; - DeviceManagers m_device_managers; - DeviceToFactoryMap m_device_factory_map; + WidgetFactories m_widget_factories; + USBDeviceToFactoryMap m_device_factory_map; + WidgetToDeviceMap m_widget_device_map; #ifdef OLA_LIBUSB_HAS_HOTPLUG_API libusb_hotplug_callback_handle m_hotplug_handle; diff --git a/plugins/usbdmx/EuroliteProDevice.cpp b/plugins/usbdmx/EuroliteProDevice.cpp index 78290c42a7..d88cdff616 100644 --- a/plugins/usbdmx/EuroliteProDevice.cpp +++ b/plugins/usbdmx/EuroliteProDevice.cpp @@ -24,16 +24,16 @@ #include #include "ola/Logging.h" #include "plugins/usbdmx/EuroliteProOutputPort.h" +#include "plugins/usbdmx/EuroliteProWidget.h" namespace ola { namespace plugin { namespace usbdmx { EuroliteProDevice::EuroliteProDevice(ola::AbstractPlugin *owner, - EuroliteProWidgetInterface *widget, - const std::string &serial) + EuroliteProWidget *widget) : Device(owner, "EurolitePro USB Device"), - m_device_id("eurolite-" + serial), + m_device_id("eurolite-" + widget->SerialNumber()), m_port(new EuroliteProOutputPort(this, 0, widget)) { } diff --git a/plugins/usbdmx/EuroliteProDevice.h b/plugins/usbdmx/EuroliteProDevice.h index f9ef4f962d..3cb9a9fca5 100644 --- a/plugins/usbdmx/EuroliteProDevice.h +++ b/plugins/usbdmx/EuroliteProDevice.h @@ -37,8 +37,7 @@ namespace usbdmx { class EuroliteProDevice: public Device { public: EuroliteProDevice(ola::AbstractPlugin *owner, - class EuroliteProWidgetInterface *widget, - const std::string &serial); + class EuroliteProWidget *widget); std::string DeviceId() const { return m_device_id; diff --git a/plugins/usbdmx/EuroliteProOutputPort.cpp b/plugins/usbdmx/EuroliteProOutputPort.cpp index 1b5bda6992..e7d2d0a202 100644 --- a/plugins/usbdmx/EuroliteProOutputPort.cpp +++ b/plugins/usbdmx/EuroliteProOutputPort.cpp @@ -31,7 +31,7 @@ namespace usbdmx { EuroliteProOutputPort::EuroliteProOutputPort(EuroliteProDevice *parent, unsigned int id, - EuroliteProWidgetInterface *widget) + EuroliteProWidget *widget) : BasicOutputPort(parent, id), m_widget(widget) { } @@ -39,11 +39,10 @@ EuroliteProOutputPort::EuroliteProOutputPort(EuroliteProDevice *parent, EuroliteProOutputPort::~EuroliteProOutputPort() { // TODO(simon): stop the thread here?? OLA_INFO << "EuroliteProOutputPort::~EuroliteProOutputPort()"; - delete m_widget; } bool EuroliteProOutputPort::WriteDMX(const DmxBuffer &buffer, - OLA_UNUSED uint8_t priority) { + OLA_UNUSED uint8_t priority) { m_widget->SendDMX(buffer); return true; } diff --git a/plugins/usbdmx/EuroliteProOutputPort.h b/plugins/usbdmx/EuroliteProOutputPort.h index 9d0fa974f6..983d10c101 100644 --- a/plugins/usbdmx/EuroliteProOutputPort.h +++ b/plugins/usbdmx/EuroliteProOutputPort.h @@ -40,7 +40,7 @@ class EuroliteProOutputPort: public BasicOutputPort { */ EuroliteProOutputPort(class EuroliteProDevice *parent, unsigned int id, - class EuroliteProWidgetInterface *widget); + class EuroliteProWidget *widget); /** * @brief Cleanup. @@ -51,7 +51,7 @@ class EuroliteProOutputPort: public BasicOutputPort { std::string Description() const { return ""; } private: - class EuroliteProWidgetInterface* const m_widget; + class EuroliteProWidget* const m_widget; DISALLOW_COPY_AND_ASSIGN(EuroliteProOutputPort); }; diff --git a/plugins/usbdmx/EuroliteProWidget.cpp b/plugins/usbdmx/EuroliteProWidget.cpp index b81d00fb06..de1fb3ac42 100644 --- a/plugins/usbdmx/EuroliteProWidget.cpp +++ b/plugins/usbdmx/EuroliteProWidget.cpp @@ -20,6 +20,8 @@ #include "plugins/usbdmx/EuroliteProWidget.h" +#include + #include "ola/Constants.h" #include "ola/Logging.h" #include "plugins/usbdmx/LibUsbHelper.h" @@ -28,10 +30,12 @@ namespace ola { namespace plugin { namespace usbdmx { -const char EuroliteProWidgetInterface::EXPECTED_MANUFACTURER[] = "Eurolite"; -const char EuroliteProWidgetInterface::EXPECTED_PRODUCT[] = +const char EuroliteProWidget::EXPECTED_MANUFACTURER[] = "Eurolite"; +const char EuroliteProWidget::EXPECTED_PRODUCT[] = "Eurolite DMX512 Pro"; +using std::string; + namespace { // Why is this so long? @@ -53,7 +57,7 @@ void AsyncCallback(struct libusb_transfer *transfer) { */ void CreateFrame( const DmxBuffer &buffer, - uint8_t frame[EuroliteProWidgetInterface::EUROLITE_PRO_FRAME_SIZE]) { + uint8_t frame[EuroliteProWidget::EUROLITE_PRO_FRAME_SIZE]) { unsigned int frame_size = buffer.Size(); // header @@ -66,7 +70,7 @@ void CreateFrame( memset(frame + 5 + frame_size, 0, DMX_UNIVERSE_SIZE - frame_size); // End message delimiter - frame[EuroliteProWidgetInterface::EUROLITE_PRO_FRAME_SIZE - 1] = 0xE7; + frame[EuroliteProWidget::EUROLITE_PRO_FRAME_SIZE - 1] = 0xE7; } /** @@ -115,7 +119,7 @@ bool LocateInterface(libusb_device *usb_device, class EuroliteProThreadedSender: public ThreadedUsbSender { public: EuroliteProThreadedSender(libusb_device *usb_device, - libusb_device_handle *handle); + libusb_device_handle *handle); private: bool TransmitBuffer(libusb_device_handle *handle, @@ -130,7 +134,7 @@ EuroliteProThreadedSender::EuroliteProThreadedSender( bool EuroliteProThreadedSender::TransmitBuffer(libusb_device_handle *handle, const DmxBuffer &buffer) { - uint8_t frame[EuroliteProWidgetInterface::EUROLITE_PRO_FRAME_SIZE]; + uint8_t frame[EuroliteProWidget::EUROLITE_PRO_FRAME_SIZE]; CreateFrame(buffer, frame); int transferred; @@ -138,10 +142,10 @@ bool EuroliteProThreadedSender::TransmitBuffer(libusb_device_handle *handle, handle, ENDPOINT, frame, - EuroliteProWidgetInterface::EUROLITE_PRO_FRAME_SIZE, + EuroliteProWidget::EUROLITE_PRO_FRAME_SIZE, &transferred, URB_TIMEOUT_MS); - if (transferred != EuroliteProWidgetInterface::EUROLITE_PRO_FRAME_SIZE) { + if (transferred != EuroliteProWidget::EUROLITE_PRO_FRAME_SIZE) { // not sure if this is fatal or not OLA_WARN << "EurolitePro driver failed to transfer all data"; } @@ -149,8 +153,10 @@ bool EuroliteProThreadedSender::TransmitBuffer(libusb_device_handle *handle, } SynchronousEuroliteProWidget::SynchronousEuroliteProWidget( - libusb_device *usb_device) - : m_usb_device(usb_device) { + libusb_device *usb_device, + const string &serial) + : EuroliteProWidget(serial), + m_usb_device(usb_device) { } bool SynchronousEuroliteProWidget::Init() { @@ -181,8 +187,10 @@ bool SynchronousEuroliteProWidget::SendDMX(const DmxBuffer &buffer) { } AsynchronousEuroliteProWidget::AsynchronousEuroliteProWidget( - libusb_device *usb_device) - : m_usb_device(usb_device), + libusb_device *usb_device, + const string &serial) + : EuroliteProWidget(serial), + m_usb_device(usb_device), m_usb_handle(NULL), m_transfer_state(IDLE) { m_transfer = libusb_alloc_transfer(0); diff --git a/plugins/usbdmx/EuroliteProWidget.h b/plugins/usbdmx/EuroliteProWidget.h index d973cfe069..d8ebb74ea3 100644 --- a/plugins/usbdmx/EuroliteProWidget.h +++ b/plugins/usbdmx/EuroliteProWidget.h @@ -22,9 +22,11 @@ #define PLUGINS_USBDMX_EUROLITEPROWIDGET_H_ #include +#include #include "ola/base/Macro.h" #include "ola/DmxBuffer.h" #include "plugins/usbdmx/ThreadedUsbSender.h" +#include "plugins/usbdmx/Widget.h" namespace ola { namespace plugin { @@ -33,21 +35,29 @@ namespace usbdmx { class EuroliteProThreadedSender; /** - * @brief The interface for the EurolitePro Widgets + * @brief The EurolitePro Widget. */ -class EuroliteProWidgetInterface { +class EuroliteProWidget : public Widget { public: - virtual ~EuroliteProWidgetInterface() {} + explicit EuroliteProWidget(const std::string &serial) : m_serial(serial) {} + virtual ~EuroliteProWidget() {} virtual bool Init() = 0; virtual bool SendDMX(const DmxBuffer &buffer) = 0; + std::string SerialNumber() const { + return m_serial; + } + static const char EXPECTED_MANUFACTURER[]; static const char EXPECTED_PRODUCT[]; // 513 + header + code + size(2) + footer enum { EUROLITE_PRO_FRAME_SIZE = 518 }; + + private: + std::string m_serial; }; @@ -56,9 +66,10 @@ class EuroliteProWidgetInterface { * * Internally this spawns a new thread to avoid blocking SendDMX() calls. */ -class SynchronousEuroliteProWidget: public EuroliteProWidgetInterface { +class SynchronousEuroliteProWidget: public EuroliteProWidget { public: - explicit SynchronousEuroliteProWidget(libusb_device *usb_device); + SynchronousEuroliteProWidget(libusb_device *usb_device, + const std::string &serial); bool Init(); @@ -74,9 +85,10 @@ class SynchronousEuroliteProWidget: public EuroliteProWidgetInterface { /** * @brief An EurolitePro widget that uses asynchronous libusb operations. */ -class AsynchronousEuroliteProWidget: public EuroliteProWidgetInterface { +class AsynchronousEuroliteProWidget: public EuroliteProWidget { public: - explicit AsynchronousEuroliteProWidget(libusb_device *usb_device); + explicit AsynchronousEuroliteProWidget(libusb_device *usb_device, + const std::string &serial); ~AsynchronousEuroliteProWidget(); bool Init(); diff --git a/plugins/usbdmx/EuroliteProDeviceManager.cpp b/plugins/usbdmx/EuroliteProWidgetFactory.cpp similarity index 68% rename from plugins/usbdmx/EuroliteProDeviceManager.cpp rename to plugins/usbdmx/EuroliteProWidgetFactory.cpp index cfdee67a92..f0a215ebf6 100644 --- a/plugins/usbdmx/EuroliteProDeviceManager.cpp +++ b/plugins/usbdmx/EuroliteProWidgetFactory.cpp @@ -13,26 +13,25 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * - * EuroliteProDeviceManager.cpp - * The EurolitePro Device Manager + * EuroliteProWidgetFactory.cpp + * The WidgetFactory for EurolitePro widgets. * Copyright (C) 2014 Simon Newton */ -#include "plugins/usbdmx/EuroliteProDeviceManager.h" +#include "plugins/usbdmx/EuroliteProWidgetFactory.h" #include "ola/Logging.h" -#include "plugins/usbdmx/EuroliteProDevice.h" -#include "plugins/usbdmx/EuroliteProWidget.h" #include "plugins/usbdmx/LibUsbHelper.h" namespace ola { namespace plugin { namespace usbdmx { -const uint16_t EuroliteProDeviceManager::EUROLITE_PRODUCT_ID = 0xfa63; -const uint16_t EuroliteProDeviceManager::EUROLITE_VENDOR_ID = 0x04d; +const uint16_t EuroliteProWidgetFactory::EUROLITE_PRODUCT_ID = 0xfa63; +const uint16_t EuroliteProWidgetFactory::EUROLITE_VENDOR_ID = 0x04d; -bool EuroliteProDeviceManager::DeviceAdded( +bool EuroliteProWidgetFactory::DeviceAdded( + WidgetObserver *observer, libusb_device *usb_device, const struct libusb_device_descriptor &descriptor) { if (descriptor.idVendor != EUROLITE_VENDOR_ID || @@ -48,12 +47,12 @@ bool EuroliteProDeviceManager::DeviceAdded( } if (!LibUsbHelper::CheckManufacturer( - EuroliteProWidgetInterface::EXPECTED_MANUFACTURER, info.manufacturer)) { + EuroliteProWidget::EXPECTED_MANUFACTURER, info.manufacturer)) { return false; } - if (!LibUsbHelper::CheckProduct(EuroliteProWidgetInterface::EXPECTED_PRODUCT, - info.product)) { + if (!LibUsbHelper::CheckProduct( + EuroliteProWidget::EXPECTED_PRODUCT, info.product)) { return false; } @@ -72,16 +71,10 @@ bool EuroliteProDeviceManager::DeviceAdded( std::ostringstream serial_str; serial_str << bus_number << "-" << device_address; - AsynchronousEuroliteProWidget *widget = new AsynchronousEuroliteProWidget( - usb_device); - if (!widget->Init()) { - delete widget; - return false; - } - - EuroliteProDevice *device = new EuroliteProDevice(ParentPlugin(), widget, - serial_str.str()); - return RegisterDevice(usb_device, device); + return AddWidget( + observer, + usb_device, + new AsynchronousEuroliteProWidget(usb_device, serial_str.str())); } } // namespace usbdmx } // namespace plugin diff --git a/plugins/usbdmx/EuroliteProDeviceManager.h b/plugins/usbdmx/EuroliteProWidgetFactory.h similarity index 60% rename from plugins/usbdmx/EuroliteProDeviceManager.h rename to plugins/usbdmx/EuroliteProWidgetFactory.h index 7b6daf630e..608538d68b 100644 --- a/plugins/usbdmx/EuroliteProDeviceManager.h +++ b/plugins/usbdmx/EuroliteProWidgetFactory.h @@ -13,17 +13,17 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * - * EuroliteProDeviceManager.h - * The EurolitePro Device Manager. + * EuroliteProWidgetFactory.h + * The WidgetFactory for EurolitePro widgets. * Copyright (C) 2014 Simon Newton */ -#ifndef PLUGINS_USBDMX_EUROLITEPRODEVICEMANAGER_H_ -#define PLUGINS_USBDMX_EUROLITEPRODEVICEMANAGER_H_ +#ifndef PLUGINS_USBDMX_EUROLITEPROWIDGETFACTORY_H_ +#define PLUGINS_USBDMX_EUROLITEPROWIDGETFACTORY_H_ #include "ola/base/Macro.h" -#include "plugins/usbdmx/UsbDeviceManagerInterface.h" -#include "plugins/usbdmx/EuroliteProDevice.h" +#include "plugins/usbdmx/EuroliteProWidget.h" +#include "plugins/usbdmx/WidgetFactory.h" namespace ola { namespace plugin { @@ -32,24 +32,23 @@ namespace usbdmx { /** * @brief Manages EurolitePro Devices */ -class EuroliteProDeviceManager : public BaseDeviceFactory { +class EuroliteProWidgetFactory + : public BaseWidgetFactory { public: - EuroliteProDeviceManager(PluginAdaptor *plugin_adaptor, - Plugin *plugin) - : BaseDeviceFactory(plugin_adaptor, plugin) { - } + EuroliteProWidgetFactory() {} bool DeviceAdded( - libusb_device *device, - const struct libusb_device_descriptor &descriptor); + WidgetObserver *observer, + libusb_device *usb_device, + const struct libusb_device_descriptor &descriptor); private: static const uint16_t EUROLITE_PRODUCT_ID; static const uint16_t EUROLITE_VENDOR_ID; - DISALLOW_COPY_AND_ASSIGN(EuroliteProDeviceManager); + DISALLOW_COPY_AND_ASSIGN(EuroliteProWidgetFactory); }; } // namespace usbdmx } // namespace plugin } // namespace ola -#endif // PLUGINS_USBDMX_EUROLITEPRODEVICEMANAGER_H_ +#endif // PLUGINS_USBDMX_EUROLITEPROWIDGETFACTORY_H_ diff --git a/plugins/usbdmx/Makefile.mk b/plugins/usbdmx/Makefile.mk index b6866c2132..fee8671351 100644 --- a/plugins/usbdmx/Makefile.mk +++ b/plugins/usbdmx/Makefile.mk @@ -5,22 +5,22 @@ lib_LTLIBRARIES += plugins/usbdmx/libolausbdmx.la plugins_usbdmx_libolausbdmx_la_SOURCES = \ plugins/usbdmx/AnymaDevice.cpp \ plugins/usbdmx/AnymaDevice.h \ - plugins/usbdmx/AnymaDeviceManager.cpp \ - plugins/usbdmx/AnymaDeviceManager.h \ plugins/usbdmx/AnymaOutputPort.cpp \ plugins/usbdmx/AnymaOutputPort.h \ plugins/usbdmx/AnymaWidget.cpp \ plugins/usbdmx/AnymaWidget.h \ + plugins/usbdmx/AnymaWidgetFactory.cpp \ + plugins/usbdmx/AnymaWidgetFactory.h \ plugins/usbdmx/AsyncPluginImpl.cpp \ plugins/usbdmx/AsyncPluginImpl.h \ plugins/usbdmx/EuroliteProDevice.cpp \ plugins/usbdmx/EuroliteProDevice.h \ - plugins/usbdmx/EuroliteProWidget.h \ - plugins/usbdmx/EuroliteProWidget.cpp \ - plugins/usbdmx/EuroliteProDeviceManager.h \ - plugins/usbdmx/EuroliteProDeviceManager.cpp \ plugins/usbdmx/EuroliteProOutputPort.cpp \ plugins/usbdmx/EuroliteProOutputPort.h \ + plugins/usbdmx/EuroliteProWidget.cpp \ + plugins/usbdmx/EuroliteProWidget.h \ + plugins/usbdmx/EuroliteProWidgetFactory.cpp \ + plugins/usbdmx/EuroliteProWidgetFactory.h \ plugins/usbdmx/FirmwareLoader.h \ plugins/usbdmx/LibUsbAdaptor.cpp \ plugins/usbdmx/LibUsbAdaptor.h \ @@ -29,8 +29,6 @@ plugins_usbdmx_libolausbdmx_la_SOURCES = \ plugins/usbdmx/PluginImplInterface.h \ plugins/usbdmx/SunliteDevice.cpp \ plugins/usbdmx/SunliteDevice.h \ - plugins/usbdmx/SunliteDeviceManager.cpp \ - plugins/usbdmx/SunliteDeviceManager.h \ plugins/usbdmx/SunliteFirmware.h \ plugins/usbdmx/SunliteFirmwareLoader.cpp \ plugins/usbdmx/SunliteFirmwareLoader.h \ @@ -38,18 +36,20 @@ plugins_usbdmx_libolausbdmx_la_SOURCES = \ plugins/usbdmx/SunliteOutputPort.h \ plugins/usbdmx/SunliteWidget.cpp \ plugins/usbdmx/SunliteWidget.h \ + plugins/usbdmx/SunliteWidgetFactory.cpp \ + plugins/usbdmx/SunliteWidgetFactory.h \ plugins/usbdmx/SyncPluginImpl.cpp \ plugins/usbdmx/SyncPluginImpl.h \ plugins/usbdmx/ThreadedUsbSender.cpp \ plugins/usbdmx/ThreadedUsbSender.h \ plugins/usbdmx/UsbDevice.h \ - plugins/usbdmx/UsbDeviceManagerInterface.h \ plugins/usbdmx/UsbDmxPlugin.cpp \ plugins/usbdmx/UsbDmxPlugin.h \ plugins/usbdmx/VellemanDevice.cpp \ plugins/usbdmx/VellemanDevice.h \ plugins/usbdmx/VellemanOutputPort.cpp \ - plugins/usbdmx/VellemanOutputPort.h + plugins/usbdmx/VellemanOutputPort.h \ + plugins/usbdmx/WidgetFactory.h plugins_usbdmx_libolausbdmx_la_CXXFLAGS = $(COMMON_CXXFLAGS) $(libusb_CFLAGS) plugins_usbdmx_libolausbdmx_la_LIBADD = $(libusb_LIBS) \ diff --git a/plugins/usbdmx/SunliteDevice.cpp b/plugins/usbdmx/SunliteDevice.cpp index e3087c25b7..d38faea28d 100644 --- a/plugins/usbdmx/SunliteDevice.cpp +++ b/plugins/usbdmx/SunliteDevice.cpp @@ -27,7 +27,7 @@ namespace plugin { namespace usbdmx { SunliteDevice::SunliteDevice(ola::AbstractPlugin *owner, - SunliteWidgetInterface *widget) + SunliteWidget *widget) : Device(owner, "Sunlite USBDMX2 Device"), m_port(new SunliteOutputPort(this, 0, widget)) { } diff --git a/plugins/usbdmx/SunliteDevice.h b/plugins/usbdmx/SunliteDevice.h index 6551282231..42ca7f4a30 100644 --- a/plugins/usbdmx/SunliteDevice.h +++ b/plugins/usbdmx/SunliteDevice.h @@ -36,7 +36,7 @@ namespace usbdmx { class SunliteDevice: public Device { public: SunliteDevice(ola::AbstractPlugin *owner, - class SunliteWidgetInterface *widget); + class SunliteWidget *widget); std::string DeviceId() const { return "usbdmx2"; } diff --git a/plugins/usbdmx/SunliteDeviceManager.cpp b/plugins/usbdmx/SunliteDeviceManager.cpp deleted file mode 100644 index 2105457056..0000000000 --- a/plugins/usbdmx/SunliteDeviceManager.cpp +++ /dev/null @@ -1,67 +0,0 @@ -/* - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * 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 Library 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - * - * SunliteDeviceManager.cpp - * The Sunlite Device Manager - * Copyright (C) 2014 Simon Newton - */ - -#include "plugins/usbdmx/SunliteDeviceManager.h" -#include "plugins/usbdmx/SunliteFirmwareLoader.h" -#include "plugins/usbdmx/SunliteWidget.h" - -#include "ola/Logging.h" - -namespace ola { -namespace plugin { -namespace usbdmx { - -const uint16_t SunliteDeviceManager::SUNLITE_VENDOR_ID = 0x0962; - -bool SunliteDeviceManager::DeviceAdded( - libusb_device *usb_device, - const struct libusb_device_descriptor &descriptor) { - if (descriptor.idVendor == SUNLITE_VENDOR_ID && - descriptor.idProduct == 0x2000) { - OLA_INFO << "New empty SunliteDevice"; - // TODO(simon): Make this async. - SunliteFirmwareLoader loader(usb_device); - loader.LoadFirmware(); - return true; - } else if (descriptor.idVendor == SUNLITE_VENDOR_ID && - descriptor.idProduct == 0x2001 && - !HasDevice(usb_device)) { - OLA_INFO << "Found a new Sunlite device"; - - - AsynchronousSunliteWidget *widget = new AsynchronousSunliteWidget( - usb_device); - if (!widget->Init()) { - delete widget; - return false; - } - SunliteDevice *device = new SunliteDevice(ParentPlugin(), widget); - return RegisterDevice(usb_device, device); - } - return false; -} - -void SunliteDeviceManager::DeviceRemoved(libusb_device *device) { - // TODO(simon): once firmware loading is async, cancel the load here. - BaseDeviceFactory::DeviceRemoved(device); -} -} // namespace usbdmx -} // namespace plugin -} // namespace ola diff --git a/plugins/usbdmx/SunliteDeviceManager.h b/plugins/usbdmx/SunliteDeviceManager.h deleted file mode 100644 index 5b27b81026..0000000000 --- a/plugins/usbdmx/SunliteDeviceManager.h +++ /dev/null @@ -1,54 +0,0 @@ -/* - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * 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 Library 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - * - * SunliteDeviceManager.h - * The Sunlite Device Manager - * Copyright (C) 2014 Simon Newton - */ - -#ifndef PLUGINS_USBDMX_SUNLITEDEVICEMANAGER_H_ -#define PLUGINS_USBDMX_SUNLITEDEVICEMANAGER_H_ - -#include "ola/base/Macro.h" -#include "plugins/usbdmx/UsbDeviceManagerInterface.h" -#include "plugins/usbdmx/SunliteDevice.h" - -namespace ola { -namespace plugin { -namespace usbdmx { - -/** - * @brief Manages SunLite Devices - */ -class SunliteDeviceManager : public BaseDeviceFactory { - public: - SunliteDeviceManager(PluginAdaptor *plugin_adaptor, - Plugin *plugin) - : BaseDeviceFactory(plugin_adaptor, plugin) {} - - bool DeviceAdded(libusb_device *device, - const struct libusb_device_descriptor &descriptor); - - void DeviceRemoved(libusb_device *device); - - private: - static const uint16_t SUNLITE_VENDOR_ID; - - DISALLOW_COPY_AND_ASSIGN(SunliteDeviceManager); -}; -} // namespace usbdmx -} // namespace plugin -} // namespace ola -#endif // PLUGINS_USBDMX_SUNLITEDEVICEMANAGER_H_ diff --git a/plugins/usbdmx/SunliteOutputPort.cpp b/plugins/usbdmx/SunliteOutputPort.cpp index 8b68b9a742..b9208d5738 100644 --- a/plugins/usbdmx/SunliteOutputPort.cpp +++ b/plugins/usbdmx/SunliteOutputPort.cpp @@ -30,7 +30,7 @@ namespace usbdmx { SunliteOutputPort::SunliteOutputPort(SunliteDevice *parent, unsigned int id, - SunliteWidgetInterface *widget) + SunliteWidget *widget) : BasicOutputPort(parent, id), m_widget(widget) { } diff --git a/plugins/usbdmx/SunliteOutputPort.h b/plugins/usbdmx/SunliteOutputPort.h index 26969d8939..b13b0ab71a 100644 --- a/plugins/usbdmx/SunliteOutputPort.h +++ b/plugins/usbdmx/SunliteOutputPort.h @@ -39,7 +39,7 @@ class SunliteOutputPort: public BasicOutputPort { */ SunliteOutputPort(SunliteDevice *parent, unsigned int id, - class SunliteWidgetInterface *widget); + class SunliteWidget *widget); /** * @brief Cleanup. @@ -51,7 +51,7 @@ class SunliteOutputPort: public BasicOutputPort { std::string Description() const { return ""; } private: - class SunliteWidgetInterface* const m_widget; + class SunliteWidget* const m_widget; DISALLOW_COPY_AND_ASSIGN(SunliteOutputPort); }; diff --git a/plugins/usbdmx/SunliteWidget.cpp b/plugins/usbdmx/SunliteWidget.cpp index 868e3a8e10..3dfb4e498f 100644 --- a/plugins/usbdmx/SunliteWidget.cpp +++ b/plugins/usbdmx/SunliteWidget.cpp @@ -48,8 +48,8 @@ void AsyncCallback(struct libusb_transfer *transfer) { /* * Initialize a USBDMX2 packet */ -void InitPacket(uint8_t packet[SunliteWidgetInterface::SUNLITE_PACKET_SIZE]) { - memset(packet, 0, SunliteWidgetInterface::SUNLITE_PACKET_SIZE); +void InitPacket(uint8_t packet[SunliteWidget::SUNLITE_PACKET_SIZE]) { + memset(packet, 0, SunliteWidget::SUNLITE_PACKET_SIZE); // The packet is divided into 26 chunks of 32 bytes each. Each chunk contains // the data for 20 channels (except the last one which has 12 channels of @@ -82,7 +82,7 @@ void InitPacket(uint8_t packet[SunliteWidgetInterface::SUNLITE_PACKET_SIZE]) { * Update a USBDMX2 packet to match the supplied DmxBuffer. */ void UpdatePacket(const DmxBuffer &buffer, - uint8_t packet[SunliteWidgetInterface::SUNLITE_PACKET_SIZE]) { + uint8_t packet[SunliteWidget::SUNLITE_PACKET_SIZE]) { for (unsigned int i = 0; i < buffer.Size(); i++) { packet[(i / CHANNELS_PER_CHUNK) * CHUNK_SIZE + ((i / 4) % 5) * 6 + 3 + (i % 4)] = buffer.Get(i); @@ -100,7 +100,7 @@ class SunliteThreadedSender: public ThreadedUsbSender { libusb_device_handle *handle); private: - uint8_t m_packet[SunliteWidgetInterface::SUNLITE_PACKET_SIZE]; + uint8_t m_packet[SunliteWidget::SUNLITE_PACKET_SIZE]; bool TransmitBuffer(libusb_device_handle *handle, const DmxBuffer &buffer); @@ -121,10 +121,10 @@ bool SunliteThreadedSender::TransmitBuffer(libusb_device_handle *handle, handle, ENDPOINT, (unsigned char*) m_packet, - SunliteWidgetInterface::SUNLITE_PACKET_SIZE, + SunliteWidget::SUNLITE_PACKET_SIZE, &transferred, TIMEOUT); - if (transferred != SunliteWidgetInterface::SUNLITE_PACKET_SIZE) { + if (transferred != SunliteWidget::SUNLITE_PACKET_SIZE) { // not sure if this is fatal or not OLA_WARN << "Sunlite driver failed to transfer all data"; } diff --git a/plugins/usbdmx/SunliteWidget.h b/plugins/usbdmx/SunliteWidget.h index 03b271b5a9..49c7c6f560 100644 --- a/plugins/usbdmx/SunliteWidget.h +++ b/plugins/usbdmx/SunliteWidget.h @@ -35,9 +35,9 @@ class SunliteThreadedSender; /** * @brief The interface for the Sunlite Widgets */ -class SunliteWidgetInterface { +class SunliteWidget { public: - virtual ~SunliteWidgetInterface() {} + virtual ~SunliteWidget() {} virtual bool Init() = 0; @@ -52,7 +52,7 @@ class SunliteWidgetInterface { * * Internally this spawns a new thread to avoid blocking SendDMX() calls. */ -class SynchronousSunliteWidget: public SunliteWidgetInterface { +class SynchronousSunliteWidget: public SunliteWidget { public: explicit SynchronousSunliteWidget(libusb_device *usb_device); @@ -70,7 +70,7 @@ class SynchronousSunliteWidget: public SunliteWidgetInterface { /** * @brief An Sunlite widget that uses asynchronous libusb operations. */ -class AsynchronousSunliteWidget: public SunliteWidgetInterface { +class AsynchronousSunliteWidget: public SunliteWidget { public: explicit AsynchronousSunliteWidget(libusb_device *usb_device); ~AsynchronousSunliteWidget(); diff --git a/plugins/usbdmx/SyncPluginImpl.cpp b/plugins/usbdmx/SyncPluginImpl.cpp index 0ab5621ef8..5cfc635e78 100644 --- a/plugins/usbdmx/SyncPluginImpl.cpp +++ b/plugins/usbdmx/SyncPluginImpl.cpp @@ -183,13 +183,7 @@ void SyncPluginImpl::CheckDevice(libusb_device *usb_device) { } else if (device_descriptor.idVendor == 0x04d8 && device_descriptor.idProduct == 0xfa63) { OLA_INFO << "Found a EUROLITE device"; - SynchronousEuroliteProWidget *widget = new SynchronousEuroliteProWidget( - usb_device); - if (!widget->Init()) { - delete widget; - return; - } - device = new EuroliteProDevice(m_plugin, widget); + device = NewEuroliteProDevice(usb_device); } if (device) { @@ -219,13 +213,13 @@ Device* SyncPluginImpl::NewAnymaDevice( USBDeviceInformation info; GetDeviceInfo(usb_handle, device_descriptor, &info); - if (!MatchManufacturer(AnymaWidgetInterface::EXPECTED_MANUFACTURER, + if (!MatchManufacturer(AnymaWidget::EXPECTED_MANUFACTURER, info.manufacturer)) { libusb_close(usb_handle); return NULL; } - if (!MatchProduct(AnymaWidgetInterface::EXPECTED_PRODUCT, info.product)) { + if (!MatchProduct(AnymaWidget::EXPECTED_PRODUCT, info.product)) { libusb_close(usb_handle); return NULL; } @@ -243,14 +237,37 @@ Device* SyncPluginImpl::NewAnymaDevice( } } - SynchronousAnymaWidget *widget = new SynchronousAnymaWidget(usb_device); + SynchronousAnymaWidget *widget = new SynchronousAnymaWidget( + usb_device, info.serial); if (!widget->Init()) { delete widget; return NULL; } - return new AnymaDevice(m_plugin, widget, info.serial); + return new AnymaDevice(m_plugin, widget); } +Device* SyncPluginImpl::NewEuroliteProDevice( + struct libusb_device *usb_device) { + + // There is no Serialnumber--> work around: bus+device number + int bus_number = libusb_get_bus_number(usb_device); + int device_address = libusb_get_device_address(usb_device); + + OLA_INFO << "Bus_number: " << bus_number << ", Device_address: " << + device_address; + + std::ostringstream serial_str; + serial_str << bus_number << "-" << device_address; + + SynchronousEuroliteProWidget *widget = new SynchronousEuroliteProWidget( + usb_device, serial_str.str()); + if (!widget->Init()) { + delete widget; + return NULL; + } + + return new EuroliteProDevice(m_plugin, widget); +} /** * Get the Manufacturer, Product and Serial number strings for a device. diff --git a/plugins/usbdmx/SyncPluginImpl.h b/plugins/usbdmx/SyncPluginImpl.h index db880af5a5..f86194ca9a 100644 --- a/plugins/usbdmx/SyncPluginImpl.h +++ b/plugins/usbdmx/SyncPluginImpl.h @@ -87,6 +87,8 @@ class SyncPluginImpl: public PluginImplInterface { class Device* NewAnymaDevice( struct libusb_device *usb_device, const struct libusb_device_descriptor &device_descriptor); + class Device* NewEuroliteProDevice( + struct libusb_device *usb_device); void GetDeviceInfo( struct libusb_device_handle *usb_handle, diff --git a/plugins/usbdmx/UsbDeviceManagerInterface.h b/plugins/usbdmx/UsbDeviceManagerInterface.h deleted file mode 100644 index 281ae7f2f0..0000000000 --- a/plugins/usbdmx/UsbDeviceManagerInterface.h +++ /dev/null @@ -1,110 +0,0 @@ -/* - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * 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 Library 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - * - * UsbDeviceManagerInterface.h - * The interface for the USB Device Managers - * Copyright (C) 2014 Simon Newton - */ - -#ifndef PLUGINS_USBDMX_USBDEVICEMANAGERINTERFACE_H_ -#define PLUGINS_USBDMX_USBDEVICEMANAGERINTERFACE_H_ - -#include -#include -#include "ola/base/Macro.h" -#include "ola/stl/STLUtils.h" -#include "ola/Logging.h" -#include "olad/Plugin.h" -#include "olad/PluginAdaptor.h" - -namespace ola { -namespace plugin { -namespace usbdmx { - -/** - * @brief Manages a particular type of USB Device. - */ -class UsbDeviceManagerInterface { - public: - virtual ~UsbDeviceManagerInterface() {} - - virtual bool DeviceAdded( - libusb_device *device, - const struct libusb_device_descriptor &descriptor) = 0; - - virtual void DeviceRemoved(libusb_device *device) = 0; -}; - -template -class BaseDeviceFactory : public UsbDeviceManagerInterface { - public: - BaseDeviceFactory(ola::PluginAdaptor *plugin_adaptor, ola::Plugin *plugin) - : m_plugin_adaptor(plugin_adaptor), - m_plugin(plugin) { - } - - void DeviceRemoved(libusb_device *device); - - protected: - bool HasDevice(libusb_device *device) { - return STLContains(m_device_map, device); - } - - ola::Plugin* ParentPlugin() { return m_plugin; } - - bool RegisterDevice(libusb_device *usb_device, DeviceClass *device); - - private: - typedef std::map DeviceMap; - - ola::PluginAdaptor *m_plugin_adaptor; - ola::Plugin *m_plugin; - DeviceMap m_device_map; - - DISALLOW_COPY_AND_ASSIGN(BaseDeviceFactory); -}; - -template -bool BaseDeviceFactory::RegisterDevice(libusb_device *usb_device, - DeviceClass *device) { - if (!device->Start()) { - delete device; - return false; - } - - DeviceClass *old_device = STLReplacePtr(&m_device_map, usb_device, device); - if (old_device) { - m_plugin_adaptor->UnregisterDevice(old_device); - old_device->Stop(); - delete old_device; - } - m_plugin_adaptor->RegisterDevice(device); - return true; -} - -template -void BaseDeviceFactory::DeviceRemoved(libusb_device *usb_device) { - OLA_INFO << "Removing device " << usb_device; - DeviceClass *device = STLLookupAndRemovePtr(&m_device_map, usb_device); - if (device) { - m_plugin_adaptor->UnregisterDevice(device); - device->Stop(); - delete device; - } -} -} // namespace usbdmx -} // namespace plugin -} // namespace ola -#endif // PLUGINS_USBDMX_USBDEVICEMANAGERINTERFACE_H_ diff --git a/plugins/usbdmx/WidgetFactory.h b/plugins/usbdmx/WidgetFactory.h new file mode 100644 index 0000000000..5dfcd69252 --- /dev/null +++ b/plugins/usbdmx/WidgetFactory.h @@ -0,0 +1,162 @@ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * 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 Library 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * WidgetFactory.h + * Creates USB Widgets. + * Copyright (C) 2014 Simon Newton + */ + +#ifndef PLUGINS_USBDMX_WIDGETFACTORY_H_ +#define PLUGINS_USBDMX_WIDGETFACTORY_H_ + +#include +#include +#include "ola/Logging.h" +#include "ola/stl/STLUtils.h" + +namespace ola { +namespace plugin { +namespace usbdmx { + +/** + * @brief Receives notifications when new widgets are detected. + */ +class WidgetObserver { + public: + virtual ~WidgetObserver() {} + + /** + * @brief Called when a new generic widget is found. + * @param widget the new Widget. + * @returns true if the widget is now in use, false if the widget was + * ignored. + */ + virtual bool NewWidget(class Widget *widget) = 0; + virtual bool NewWidget(class AnymaWidget *widget) = 0; + virtual bool NewWidget(class EuroliteProWidget *widget) = 0; + virtual bool NewWidget(class SunliteWidget *widget) = 0; + + /** + * @brief Called when a generic widget is removed. + * @param widget the Widget that has been removed. + * + * It is an error to use the widget once this call completes. + */ + virtual void WidgetRemoved(class Widget *widget) = 0; + + virtual void WidgetRemoved(class AnymaWidget *widget) = 0; + virtual void WidgetRemoved(class EuroliteProWidget *widget) = 0; + virtual void WidgetRemoved(class SunliteWidget *widget) = 0; +}; + +/** + * @brief + */ +class WidgetFactory { + public: + virtual ~WidgetFactory() {} + + /** + * @brief + * @param observer The WidgetObserver to notify if this results in a new + * widget. + * @param device the libusb_device that was added. + * @param descriptor the libusb_device_descriptor that corresponds to the + * libusb_device. + * @returns True if this factory has claimed the usb_device, false otherwise. + */ + virtual bool DeviceAdded( + WidgetObserver *observer, + libusb_device *device, + const struct libusb_device_descriptor &descriptor) = 0; + + /** + * @brief + * @param observer The WidgetObserver to notify if this results in a widget + * removal. + */ + virtual void DeviceRemoved(WidgetObserver *observer, + libusb_device *device) = 0; +}; + +/** + * @brief A partial implementation of WidgetFactory. + * + * This handles the mapping of libusb_devices to widgets and notifying the + * observer. + */ +template +class BaseWidgetFactory : public WidgetFactory { + public: + BaseWidgetFactory() {} + + void DeviceRemoved(WidgetObserver *observer, + libusb_device *device); + + protected: + bool HasDevice(libusb_device *device) { + return STLContains(m_widget_map, device); + } + + bool AddWidget(WidgetObserver *observer, libusb_device *usb_device, + WidgetType *widget); + + private: + typedef std::map WidgetMap; + + WidgetMap m_widget_map; + + DISALLOW_COPY_AND_ASSIGN(BaseWidgetFactory); +}; + +template +bool BaseWidgetFactory::AddWidget(WidgetObserver *observer, + libusb_device *usb_device, + WidgetType *widget) { + if (!widget->Init()) { + delete widget; + return false; + } + + if (!observer->NewWidget(widget)) { + delete widget; + return false; + } + + WidgetType *old_widget = STLReplacePtr(&m_widget_map, usb_device, widget); + if (old_widget) { + // This should never happen. + OLA_WARN << "Widget conflict for " << usb_device; + observer->WidgetRemoved(old_widget); + delete old_widget; + } + return true; +} + +template +void BaseWidgetFactory::DeviceRemoved( + WidgetObserver *observer, + libusb_device *usb_device) { + OLA_INFO << "Removing device " << usb_device; + WidgetType *widget = STLLookupAndRemovePtr(&m_widget_map, usb_device); + if (widget) { + observer->WidgetRemoved(widget); + delete widget; + } +} +} // namespace usbdmx +} // namespace plugin +} // namespace ola +#endif // PLUGINS_USBDMX_WIDGETFACTORY_H_ From 7a9f3b84e83404dffe8058f2a79d88dcfd198bfd Mon Sep 17 00:00:00 2001 From: Simon Newton Date: Wed, 12 Nov 2014 09:05:53 -0800 Subject: [PATCH 03/34] Remove all the duplicate OutputPort files. --- plugins/usbdmx/AnymaDevice.cpp | 4 +- plugins/usbdmx/AnymaDevice.h | 2 +- plugins/usbdmx/AnymaOutputPort.cpp | 50 --------------- plugins/usbdmx/AnymaOutputPort.h | 60 ------------------ plugins/usbdmx/EuroliteProDevice.cpp | 4 +- plugins/usbdmx/EuroliteProDevice.h | 2 +- plugins/usbdmx/EuroliteProOutputPort.cpp | 51 ---------------- plugins/usbdmx/EuroliteProOutputPort.h | 61 ------------------- ...teOutputPort.cpp => GenericOutputPort.cpp} | 23 ++++--- ...unliteOutputPort.h => GenericOutputPort.h} | 32 +++++----- plugins/usbdmx/Makefile.mk | 8 +-- plugins/usbdmx/SunliteDevice.cpp | 5 +- plugins/usbdmx/SunliteDevice.h | 2 +- plugins/usbdmx/SunliteWidget.h | 5 +- 14 files changed, 43 insertions(+), 266 deletions(-) delete mode 100644 plugins/usbdmx/AnymaOutputPort.cpp delete mode 100644 plugins/usbdmx/AnymaOutputPort.h delete mode 100644 plugins/usbdmx/EuroliteProOutputPort.cpp delete mode 100644 plugins/usbdmx/EuroliteProOutputPort.h rename plugins/usbdmx/{SunliteOutputPort.cpp => GenericOutputPort.cpp} (68%) rename plugins/usbdmx/{SunliteOutputPort.h => GenericOutputPort.h} (67%) diff --git a/plugins/usbdmx/AnymaDevice.cpp b/plugins/usbdmx/AnymaDevice.cpp index fce1d2e3c6..91d60973ff 100644 --- a/plugins/usbdmx/AnymaDevice.cpp +++ b/plugins/usbdmx/AnymaDevice.cpp @@ -20,8 +20,8 @@ #include "plugins/usbdmx/AnymaDevice.h" -#include "plugins/usbdmx/AnymaOutputPort.h" #include "plugins/usbdmx/AnymaWidget.h" +#include "plugins/usbdmx/GenericOutputPort.h" namespace ola { namespace plugin { @@ -31,7 +31,7 @@ AnymaDevice::AnymaDevice(ola::AbstractPlugin *owner, AnymaWidget *widget) : Device(owner, "Anyma USB Device"), m_device_id("anyma-" + widget->SerialNumber()), - m_port(new AnymaOutputPort(this, 0, widget)) { + m_port(new GenericOutputPort(this, 0, widget)) { } bool AnymaDevice::StartHook() { diff --git a/plugins/usbdmx/AnymaDevice.h b/plugins/usbdmx/AnymaDevice.h index 482fe903be..2511f1b1a1 100644 --- a/plugins/usbdmx/AnymaDevice.h +++ b/plugins/usbdmx/AnymaDevice.h @@ -47,7 +47,7 @@ class AnymaDevice: public Device { private: const std::string m_device_id; - std::auto_ptr m_port; + std::auto_ptr m_port; DISALLOW_COPY_AND_ASSIGN(AnymaDevice); }; diff --git a/plugins/usbdmx/AnymaOutputPort.cpp b/plugins/usbdmx/AnymaOutputPort.cpp deleted file mode 100644 index f2531dcb7d..0000000000 --- a/plugins/usbdmx/AnymaOutputPort.cpp +++ /dev/null @@ -1,50 +0,0 @@ -/* - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * 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 Library 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - * - * AnymaOutputPort.cpp - * The Anyma uDMX output port. - * Copyright (C) 2010 Simon Newton - */ - -#include "plugins/usbdmx/AnymaOutputPort.h" - -#include "ola/Logging.h" -#include "plugins/usbdmx/AnymaDevice.h" -#include "plugins/usbdmx/AnymaWidget.h" - -namespace ola { -namespace plugin { -namespace usbdmx { - -AnymaOutputPort::AnymaOutputPort(AnymaDevice *parent, - unsigned int id, - AnymaWidget *widget) - : BasicOutputPort(parent, id), - m_widget(widget) { -} - -AnymaOutputPort::~AnymaOutputPort() { - // TODO(simon): stop the thread here?? - OLA_INFO << "AnymaOutputPort::~AnymaOutputPort()"; -} - -bool AnymaOutputPort::WriteDMX(const DmxBuffer &buffer, - OLA_UNUSED uint8_t priority) { - m_widget->SendDMX(buffer); - return true; -} -} // namespace usbdmx -} // namespace plugin -} // namespace ola diff --git a/plugins/usbdmx/AnymaOutputPort.h b/plugins/usbdmx/AnymaOutputPort.h deleted file mode 100644 index 056af5945a..0000000000 --- a/plugins/usbdmx/AnymaOutputPort.h +++ /dev/null @@ -1,60 +0,0 @@ -/* - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * 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 Library 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - * - * AnymaOutputPort.h - * The Anyma uDMX output port. - * Copyright (C) 2010 Simon Newton - */ - -#ifndef PLUGINS_USBDMX_ANYMAOUTPUTPORT_H_ -#define PLUGINS_USBDMX_ANYMAOUTPUTPORT_H_ - -#include -#include "ola/base/Macro.h" -#include "ola/DmxBuffer.h" -#include "olad/Port.h" - -namespace ola { -namespace plugin { -namespace usbdmx { - -class AnymaDevice; - -class AnymaOutputPort: public BasicOutputPort { - public: - /** - * @brief Create a new AnymaOutputPort. - */ - AnymaOutputPort(AnymaDevice *parent, - unsigned int id, - class AnymaWidget *widget); - /** - * @brief Cleanup. - */ - ~AnymaOutputPort(); - - bool WriteDMX(const DmxBuffer &buffer, uint8_t priority); - - std::string Description() const { return ""; } - - private: - class AnymaWidget* const m_widget; - - DISALLOW_COPY_AND_ASSIGN(AnymaOutputPort); -}; -} // namespace usbdmx -} // namespace plugin -} // namespace ola -#endif // PLUGINS_USBDMX_ANYMAOUTPUTPORT_H_ diff --git a/plugins/usbdmx/EuroliteProDevice.cpp b/plugins/usbdmx/EuroliteProDevice.cpp index d88cdff616..edd395244c 100644 --- a/plugins/usbdmx/EuroliteProDevice.cpp +++ b/plugins/usbdmx/EuroliteProDevice.cpp @@ -23,7 +23,7 @@ #include #include "ola/Logging.h" -#include "plugins/usbdmx/EuroliteProOutputPort.h" +#include "plugins/usbdmx/GenericOutputPort.h" #include "plugins/usbdmx/EuroliteProWidget.h" namespace ola { @@ -34,7 +34,7 @@ EuroliteProDevice::EuroliteProDevice(ola::AbstractPlugin *owner, EuroliteProWidget *widget) : Device(owner, "EurolitePro USB Device"), m_device_id("eurolite-" + widget->SerialNumber()), - m_port(new EuroliteProOutputPort(this, 0, widget)) { + m_port(new GenericOutputPort(this, 0, widget)) { } bool EuroliteProDevice::StartHook() { diff --git a/plugins/usbdmx/EuroliteProDevice.h b/plugins/usbdmx/EuroliteProDevice.h index 3cb9a9fca5..97bdd2ed00 100644 --- a/plugins/usbdmx/EuroliteProDevice.h +++ b/plugins/usbdmx/EuroliteProDevice.h @@ -48,7 +48,7 @@ class EuroliteProDevice: public Device { private: const std::string m_device_id; - std::auto_ptr m_port; + std::auto_ptr m_port; DISALLOW_COPY_AND_ASSIGN(EuroliteProDevice); }; diff --git a/plugins/usbdmx/EuroliteProOutputPort.cpp b/plugins/usbdmx/EuroliteProOutputPort.cpp deleted file mode 100644 index e7d2d0a202..0000000000 --- a/plugins/usbdmx/EuroliteProOutputPort.cpp +++ /dev/null @@ -1,51 +0,0 @@ -/* - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * 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 Library 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - * - * EuroliteProOutputPort.cpp - * Thread for the EurolitePro Output Port - * Copyright (C) 2011 Simon Newton & Harry F - * Eurolite Pro USB DMX ArtNo. 51860120 - */ - -#include "plugins/usbdmx/EuroliteProOutputPort.h" - -#include "ola/Logging.h" -#include "plugins/usbdmx/EuroliteProDevice.h" -#include "plugins/usbdmx/EuroliteProWidget.h" - -namespace ola { -namespace plugin { -namespace usbdmx { - -EuroliteProOutputPort::EuroliteProOutputPort(EuroliteProDevice *parent, - unsigned int id, - EuroliteProWidget *widget) - : BasicOutputPort(parent, id), - m_widget(widget) { -} - -EuroliteProOutputPort::~EuroliteProOutputPort() { - // TODO(simon): stop the thread here?? - OLA_INFO << "EuroliteProOutputPort::~EuroliteProOutputPort()"; -} - -bool EuroliteProOutputPort::WriteDMX(const DmxBuffer &buffer, - OLA_UNUSED uint8_t priority) { - m_widget->SendDMX(buffer); - return true; -} -} // namespace usbdmx -} // namespace plugin -} // namespace ola diff --git a/plugins/usbdmx/EuroliteProOutputPort.h b/plugins/usbdmx/EuroliteProOutputPort.h deleted file mode 100644 index 983d10c101..0000000000 --- a/plugins/usbdmx/EuroliteProOutputPort.h +++ /dev/null @@ -1,61 +0,0 @@ -/* - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * 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 Library 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - * - * EuroliteProOutputPort.h - * The output port for a EurolitePro device. - * Copyright (C) 2011 Simon Newton & Harry F - * Eurolite Pro USB DMX ArtNo. 51860120 - */ - -#ifndef PLUGINS_USBDMX_EUROLITEPROOUTPUTPORT_H_ -#define PLUGINS_USBDMX_EUROLITEPROOUTPUTPORT_H_ - -#include -#include "ola/base/Macro.h" -#include "ola/DmxBuffer.h" -#include "olad/Port.h" - -namespace ola { -namespace plugin { -namespace usbdmx { - -class EuroliteProDevice; - -class EuroliteProOutputPort: public BasicOutputPort { - public: - /** - * @brief Create a new AnymaOutputPort. - */ - EuroliteProOutputPort(class EuroliteProDevice *parent, - unsigned int id, - class EuroliteProWidget *widget); - - /** - * @brief Cleanup. - */ - ~EuroliteProOutputPort(); - - bool WriteDMX(const DmxBuffer &buffer, uint8_t priority); - std::string Description() const { return ""; } - - private: - class EuroliteProWidget* const m_widget; - - DISALLOW_COPY_AND_ASSIGN(EuroliteProOutputPort); -}; -} // namespace usbdmx -} // namespace plugin -} // namespace ola -#endif // PLUGINS_USBDMX_EUROLITEPROOUTPUTPORT_H_ diff --git a/plugins/usbdmx/SunliteOutputPort.cpp b/plugins/usbdmx/GenericOutputPort.cpp similarity index 68% rename from plugins/usbdmx/SunliteOutputPort.cpp rename to plugins/usbdmx/GenericOutputPort.cpp index b9208d5738..f65c307542 100644 --- a/plugins/usbdmx/SunliteOutputPort.cpp +++ b/plugins/usbdmx/GenericOutputPort.cpp @@ -13,35 +13,34 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * - * SunliteOutputPort.cpp - * The output port for a Sunlite USBDMX2 device. - * Copyright (C) 2010 Simon Newton + * GenericOutputPort.cpp + * A Generic output port that uses a widget. + * Copyright (C) 2014 Simon Newton */ -#include "plugins/usbdmx/SunliteOutputPort.h" +#include "plugins/usbdmx/GenericOutputPort.h" #include "ola/Logging.h" -#include "plugins/usbdmx/SunliteDevice.h" -#include "plugins/usbdmx/SunliteWidget.h" +#include "olad/Device.h" +#include "plugins/usbdmx/Widget.h" namespace ola { namespace plugin { namespace usbdmx { -SunliteOutputPort::SunliteOutputPort(SunliteDevice *parent, +GenericOutputPort::GenericOutputPort(Device *parent, unsigned int id, - SunliteWidget *widget) + Widget *widget) : BasicOutputPort(parent, id), m_widget(widget) { } -SunliteOutputPort::~SunliteOutputPort() { +GenericOutputPort::~GenericOutputPort() { // TODO(simon): stop the thread here?? - OLA_INFO << "SunliteOutputPort::~SunliteOutputPort()"; - delete m_widget; + OLA_INFO << "GenericOutputPort::~GenericOutputPort()"; } -bool SunliteOutputPort::WriteDMX(const DmxBuffer &buffer, +bool GenericOutputPort::WriteDMX(const DmxBuffer &buffer, OLA_UNUSED uint8_t priority) { m_widget->SendDMX(buffer); return true; diff --git a/plugins/usbdmx/SunliteOutputPort.h b/plugins/usbdmx/GenericOutputPort.h similarity index 67% rename from plugins/usbdmx/SunliteOutputPort.h rename to plugins/usbdmx/GenericOutputPort.h index b13b0ab71a..368dd8a8e2 100644 --- a/plugins/usbdmx/SunliteOutputPort.h +++ b/plugins/usbdmx/GenericOutputPort.h @@ -13,13 +13,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * - * SunliteOutputPort.h - * The output port for a Sunlite USBDMX2 device. - * Copyright (C) 2010 Simon Newton + * GenericOutputPort.h + * A Generic output port that uses a widget. + * Copyright (C) 2014 Simon Newton */ -#ifndef PLUGINS_USBDMX_SUNLITEOUTPUTPORT_H_ -#define PLUGINS_USBDMX_SUNLITEOUTPUTPORT_H_ +#ifndef PLUGINS_USBDMX_GENERICOUTPUTPORT_H_ +#define PLUGINS_USBDMX_GENERICOUTPUTPORT_H_ #include #include "ola/base/Macro.h" @@ -27,35 +27,37 @@ #include "olad/Port.h" namespace ola { + +class Device; + namespace plugin { namespace usbdmx { -class SunliteDevice; +class Widget; -class SunliteOutputPort: public BasicOutputPort { +class GenericOutputPort: public BasicOutputPort { public: /** - * @brief Create a new SunliteOutputPort. + * @brief Create a new GenericOutputPort.. */ - SunliteOutputPort(SunliteDevice *parent, + GenericOutputPort(Device *parent, unsigned int id, - class SunliteWidget *widget); - + class Widget *widget); /** * @brief Cleanup. */ - ~SunliteOutputPort(); + ~GenericOutputPort(); bool WriteDMX(const DmxBuffer &buffer, uint8_t priority); std::string Description() const { return ""; } private: - class SunliteWidget* const m_widget; + class Widget* const m_widget; - DISALLOW_COPY_AND_ASSIGN(SunliteOutputPort); + DISALLOW_COPY_AND_ASSIGN(GenericOutputPort); }; } // namespace usbdmx } // namespace plugin } // namespace ola -#endif // PLUGINS_USBDMX_SUNLITEOUTPUTPORT_H_ +#endif // PLUGINS_USBDMX_GENERICOUTPUTPORT_H_ diff --git a/plugins/usbdmx/Makefile.mk b/plugins/usbdmx/Makefile.mk index fee8671351..0e0b4adc6b 100644 --- a/plugins/usbdmx/Makefile.mk +++ b/plugins/usbdmx/Makefile.mk @@ -5,8 +5,6 @@ lib_LTLIBRARIES += plugins/usbdmx/libolausbdmx.la plugins_usbdmx_libolausbdmx_la_SOURCES = \ plugins/usbdmx/AnymaDevice.cpp \ plugins/usbdmx/AnymaDevice.h \ - plugins/usbdmx/AnymaOutputPort.cpp \ - plugins/usbdmx/AnymaOutputPort.h \ plugins/usbdmx/AnymaWidget.cpp \ plugins/usbdmx/AnymaWidget.h \ plugins/usbdmx/AnymaWidgetFactory.cpp \ @@ -15,13 +13,13 @@ plugins_usbdmx_libolausbdmx_la_SOURCES = \ plugins/usbdmx/AsyncPluginImpl.h \ plugins/usbdmx/EuroliteProDevice.cpp \ plugins/usbdmx/EuroliteProDevice.h \ - plugins/usbdmx/EuroliteProOutputPort.cpp \ - plugins/usbdmx/EuroliteProOutputPort.h \ plugins/usbdmx/EuroliteProWidget.cpp \ plugins/usbdmx/EuroliteProWidget.h \ plugins/usbdmx/EuroliteProWidgetFactory.cpp \ plugins/usbdmx/EuroliteProWidgetFactory.h \ plugins/usbdmx/FirmwareLoader.h \ + plugins/usbdmx/GenericOutputPort.h \ + plugins/usbdmx/GenericOutputPort.cpp \ plugins/usbdmx/LibUsbAdaptor.cpp \ plugins/usbdmx/LibUsbAdaptor.h \ plugins/usbdmx/LibUsbHelper.cpp \ @@ -32,8 +30,6 @@ plugins_usbdmx_libolausbdmx_la_SOURCES = \ plugins/usbdmx/SunliteFirmware.h \ plugins/usbdmx/SunliteFirmwareLoader.cpp \ plugins/usbdmx/SunliteFirmwareLoader.h \ - plugins/usbdmx/SunliteOutputPort.cpp \ - plugins/usbdmx/SunliteOutputPort.h \ plugins/usbdmx/SunliteWidget.cpp \ plugins/usbdmx/SunliteWidget.h \ plugins/usbdmx/SunliteWidgetFactory.cpp \ diff --git a/plugins/usbdmx/SunliteDevice.cpp b/plugins/usbdmx/SunliteDevice.cpp index d38faea28d..0a29640b19 100644 --- a/plugins/usbdmx/SunliteDevice.cpp +++ b/plugins/usbdmx/SunliteDevice.cpp @@ -20,7 +20,8 @@ #include "plugins/usbdmx/SunliteDevice.h" -#include "plugins/usbdmx/SunliteOutputPort.h" +#include "plugins/usbdmx/GenericOutputPort.h" +#include "plugins/usbdmx/SunliteWidget.h" namespace ola { namespace plugin { @@ -29,7 +30,7 @@ namespace usbdmx { SunliteDevice::SunliteDevice(ola::AbstractPlugin *owner, SunliteWidget *widget) : Device(owner, "Sunlite USBDMX2 Device"), - m_port(new SunliteOutputPort(this, 0, widget)) { + m_port(new GenericOutputPort(this, 0, widget)) { } bool SunliteDevice::StartHook() { diff --git a/plugins/usbdmx/SunliteDevice.h b/plugins/usbdmx/SunliteDevice.h index 42ca7f4a30..22a8c2e0f3 100644 --- a/plugins/usbdmx/SunliteDevice.h +++ b/plugins/usbdmx/SunliteDevice.h @@ -44,7 +44,7 @@ class SunliteDevice: public Device { bool StartHook(); private: - std::auto_ptr m_port; + std::auto_ptr m_port; DISALLOW_COPY_AND_ASSIGN(SunliteDevice); }; diff --git a/plugins/usbdmx/SunliteWidget.h b/plugins/usbdmx/SunliteWidget.h index 49c7c6f560..af2b369d7e 100644 --- a/plugins/usbdmx/SunliteWidget.h +++ b/plugins/usbdmx/SunliteWidget.h @@ -22,9 +22,10 @@ #define PLUGINS_USBDMX_SUNLITEWIDGET_H_ #include -#include "ola/base/Macro.h" #include "ola/DmxBuffer.h" +#include "ola/base/Macro.h" #include "plugins/usbdmx/ThreadedUsbSender.h" +#include "plugins/usbdmx/Widget.h" namespace ola { namespace plugin { @@ -35,7 +36,7 @@ class SunliteThreadedSender; /** * @brief The interface for the Sunlite Widgets */ -class SunliteWidget { +class SunliteWidget : public Widget { public: virtual ~SunliteWidget() {} From b802168ab87210603a50e3c1e4d0cf636ba08385 Mon Sep 17 00:00:00 2001 From: Simon Newton Date: Wed, 12 Nov 2014 16:49:00 -0800 Subject: [PATCH 04/34] Add missing files --- plugins/usbdmx/GenericOutputPort.h | 9 +++- plugins/usbdmx/SunliteWidgetFactory.cpp | 61 +++++++++++++++++++++++++ plugins/usbdmx/SunliteWidgetFactory.h | 55 ++++++++++++++++++++++ 3 files changed, 123 insertions(+), 2 deletions(-) create mode 100644 plugins/usbdmx/SunliteWidgetFactory.cpp create mode 100644 plugins/usbdmx/SunliteWidgetFactory.h diff --git a/plugins/usbdmx/GenericOutputPort.h b/plugins/usbdmx/GenericOutputPort.h index 368dd8a8e2..0756c1b5f6 100644 --- a/plugins/usbdmx/GenericOutputPort.h +++ b/plugins/usbdmx/GenericOutputPort.h @@ -23,7 +23,6 @@ #include #include "ola/base/Macro.h" -#include "ola/DmxBuffer.h" #include "olad/Port.h" namespace ola { @@ -35,10 +34,16 @@ namespace usbdmx { class Widget; +/** + * @brief A thin wrapper around a Widget so that it can operate as a Port. + */ class GenericOutputPort: public BasicOutputPort { public: /** - * @brief Create a new GenericOutputPort.. + * @brief Create a new GenericOutputPort. + * @param parent The parent device for this port. + * @param id The port id. + * @param widget The widget to use to send DMX frames. */ GenericOutputPort(Device *parent, unsigned int id, diff --git a/plugins/usbdmx/SunliteWidgetFactory.cpp b/plugins/usbdmx/SunliteWidgetFactory.cpp new file mode 100644 index 0000000000..4a8ab3eb0c --- /dev/null +++ b/plugins/usbdmx/SunliteWidgetFactory.cpp @@ -0,0 +1,61 @@ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * 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 Library 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * SunliteWidgetFactory.cpp + * The WidgetFactory for SunLite widgets. + * Copyright (C) 2014 Simon Newton + */ + +#include "plugins/usbdmx/SunliteWidgetFactory.h" + +#include "ola/Logging.h" +#include "plugins/usbdmx/SunliteFirmwareLoader.h" + +namespace ola { +namespace plugin { +namespace usbdmx { + +const uint16_t SunliteWidgetFactory::SUNLITE_VENDOR_ID = 0x0962; + +bool SunliteWidgetFactory::DeviceAdded( + WidgetObserver *observer, + libusb_device *usb_device, + const struct libusb_device_descriptor &descriptor) { + if (descriptor.idVendor == SUNLITE_VENDOR_ID && + descriptor.idProduct == 0x2000) { + OLA_INFO << "New empty SunliteDevice"; + // TODO(simon): Make this async. + SunliteFirmwareLoader loader(usb_device); + loader.LoadFirmware(); + return true; + } else if (descriptor.idVendor == SUNLITE_VENDOR_ID && + descriptor.idProduct == 0x2001 && + !HasDevice(usb_device)) { + OLA_INFO << "Found a new Sunlite device"; + + return AddWidget(observer, usb_device, + new AsynchronousSunliteWidget(usb_device)); + } + return false; +} + +void SunliteWidgetFactory::DeviceRemoved(WidgetObserver *observer, + libusb_device *device) { + // TODO(simon): once firmware loading is async, cancel the load here. + BaseWidgetFactory::DeviceRemoved(observer, device); +} +} // namespace usbdmx +} // namespace plugin +} // namespace ola diff --git a/plugins/usbdmx/SunliteWidgetFactory.h b/plugins/usbdmx/SunliteWidgetFactory.h new file mode 100644 index 0000000000..ce09a0cf93 --- /dev/null +++ b/plugins/usbdmx/SunliteWidgetFactory.h @@ -0,0 +1,55 @@ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * 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 Library 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * SunliteWidgetFactory.h + * The WidgetFactory for SunLite widgets. + * Copyright (C) 2014 Simon Newton + */ + +#ifndef PLUGINS_USBDMX_SUNLITEWIDGETFACTORY_H_ +#define PLUGINS_USBDMX_SUNLITEWIDGETFACTORY_H_ + +#include "ola/base/Macro.h" +#include "plugins/usbdmx/SunliteWidget.h" +#include "plugins/usbdmx/WidgetFactory.h" + +namespace ola { +namespace plugin { +namespace usbdmx { + +/** + * @brief Manages SunLite Devices + */ +class SunliteWidgetFactory : public BaseWidgetFactory { + public: + SunliteWidgetFactory() {} + + bool DeviceAdded( + WidgetObserver *observer, + libusb_device *usb_device, + const struct libusb_device_descriptor &descriptor); + + void DeviceRemoved(WidgetObserver *observer, + libusb_device *device); + + private: + static const uint16_t SUNLITE_VENDOR_ID; + + DISALLOW_COPY_AND_ASSIGN(SunliteWidgetFactory); +}; +} // namespace usbdmx +} // namespace plugin +} // namespace ola +#endif // PLUGINS_USBDMX_SUNLITEWIDGETFACTORY_H_ From da6f81453aa7130724dc76f99054a0441862992d Mon Sep 17 00:00:00 2001 From: Simon Newton Date: Wed, 12 Nov 2014 18:41:01 -0800 Subject: [PATCH 05/34] Remove duplicated Device code. --- plugins/usbdmx/AnymaDevice.cpp | 43 --------------------- plugins/usbdmx/AnymaDevice.h | 57 ---------------------------- plugins/usbdmx/AsyncPluginImpl.cpp | 9 ++--- plugins/usbdmx/EuroliteProDevice.cpp | 46 ---------------------- plugins/usbdmx/Makefile.mk | 10 ++--- plugins/usbdmx/SunliteDevice.cpp | 42 -------------------- plugins/usbdmx/SunliteDevice.h | 54 -------------------------- plugins/usbdmx/SyncPluginImpl.cpp | 16 ++++---- 8 files changed, 16 insertions(+), 261 deletions(-) delete mode 100644 plugins/usbdmx/AnymaDevice.cpp delete mode 100644 plugins/usbdmx/AnymaDevice.h delete mode 100644 plugins/usbdmx/EuroliteProDevice.cpp delete mode 100644 plugins/usbdmx/SunliteDevice.cpp delete mode 100644 plugins/usbdmx/SunliteDevice.h diff --git a/plugins/usbdmx/AnymaDevice.cpp b/plugins/usbdmx/AnymaDevice.cpp deleted file mode 100644 index 91d60973ff..0000000000 --- a/plugins/usbdmx/AnymaDevice.cpp +++ /dev/null @@ -1,43 +0,0 @@ -/* - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * 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 Library 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - * - * AnymaDevice.cpp - * The Anyma uDMX device. - * Copyright (C) 2010 Simon Newton - */ - -#include "plugins/usbdmx/AnymaDevice.h" - -#include "plugins/usbdmx/AnymaWidget.h" -#include "plugins/usbdmx/GenericOutputPort.h" - -namespace ola { -namespace plugin { -namespace usbdmx { - -AnymaDevice::AnymaDevice(ola::AbstractPlugin *owner, - AnymaWidget *widget) - : Device(owner, "Anyma USB Device"), - m_device_id("anyma-" + widget->SerialNumber()), - m_port(new GenericOutputPort(this, 0, widget)) { -} - -bool AnymaDevice::StartHook() { - AddPort(m_port.release()); - return true; -} -} // namespace usbdmx -} // namespace plugin -} // namespace ola diff --git a/plugins/usbdmx/AnymaDevice.h b/plugins/usbdmx/AnymaDevice.h deleted file mode 100644 index 2511f1b1a1..0000000000 --- a/plugins/usbdmx/AnymaDevice.h +++ /dev/null @@ -1,57 +0,0 @@ -/* - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * 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 Library 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - * - * AnymaDevice.h - * The Anyma uDMX device. - * Copyright (C) 2010 Simon Newton - */ - -#ifndef PLUGINS_USBDMX_ANYMADEVICE_H_ -#define PLUGINS_USBDMX_ANYMADEVICE_H_ - -#include -#include -#include "ola/base/Macro.h" -#include "olad/Device.h" - -namespace ola { -namespace plugin { -namespace usbdmx { - -/** - * @brief An Anyma device. - */ -class AnymaDevice: public Device { - public: - AnymaDevice(ola::AbstractPlugin *owner, - class AnymaWidget *widget); - - std::string DeviceId() const { - return m_device_id; - } - - protected: - bool StartHook(); - - private: - const std::string m_device_id; - std::auto_ptr m_port; - - DISALLOW_COPY_AND_ASSIGN(AnymaDevice); -}; -} // namespace usbdmx -} // namespace plugin -} // namespace ola -#endif // PLUGINS_USBDMX_ANYMADEVICE_H_ diff --git a/plugins/usbdmx/AsyncPluginImpl.cpp b/plugins/usbdmx/AsyncPluginImpl.cpp index a0972ac761..2b75351d97 100644 --- a/plugins/usbdmx/AsyncPluginImpl.cpp +++ b/plugins/usbdmx/AsyncPluginImpl.cpp @@ -29,13 +29,11 @@ #include "ola/stl/STLUtils.h" #include "olad/PluginAdaptor.h" -#include "plugins/usbdmx/AnymaWidgetFactory.h" #include "plugins/usbdmx/AnymaWidget.h" -#include "plugins/usbdmx/AnymaDevice.h" +#include "plugins/usbdmx/AnymaWidgetFactory.h" #include "plugins/usbdmx/EuroliteProWidgetFactory.h" -#include "plugins/usbdmx/EuroliteProDevice.h" +#include "plugins/usbdmx/GenericDevice.h" #include "plugins/usbdmx/SunliteWidgetFactory.h" -#include "plugins/usbdmx/SunliteDevice.h" #include "plugins/usbdmx/VellemanDevice.h" @@ -229,7 +227,8 @@ bool AsyncPluginImpl::NewWidget(class Widget *widget) { } bool AsyncPluginImpl::NewWidget(class AnymaWidget *widget) { - AnymaDevice *device = new AnymaDevice(m_plugin, widget); + GenericDevice *device = new GenericDevice( + m_plugin, widget, "Anyma USB Device", "anyma-" + widget->SerialNumber()); if (!device->Start()) { delete device; diff --git a/plugins/usbdmx/EuroliteProDevice.cpp b/plugins/usbdmx/EuroliteProDevice.cpp deleted file mode 100644 index edd395244c..0000000000 --- a/plugins/usbdmx/EuroliteProDevice.cpp +++ /dev/null @@ -1,46 +0,0 @@ -/* - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * 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 Library 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - * - * EuroliteProDevice.cpp - * The EurolitePro usb driver - * Copyright (C) 2011 Simon Newton & Harry F - * Eurolite Pro USB DMX ArtNo. 51860120 - */ - -#include "plugins/usbdmx/EuroliteProDevice.h" - -#include -#include "ola/Logging.h" -#include "plugins/usbdmx/GenericOutputPort.h" -#include "plugins/usbdmx/EuroliteProWidget.h" - -namespace ola { -namespace plugin { -namespace usbdmx { - -EuroliteProDevice::EuroliteProDevice(ola::AbstractPlugin *owner, - EuroliteProWidget *widget) - : Device(owner, "EurolitePro USB Device"), - m_device_id("eurolite-" + widget->SerialNumber()), - m_port(new GenericOutputPort(this, 0, widget)) { -} - -bool EuroliteProDevice::StartHook() { - AddPort(m_port.release()); - return true; -} -} // namespace usbdmx -} // namespace plugin -} // namespace ola diff --git a/plugins/usbdmx/Makefile.mk b/plugins/usbdmx/Makefile.mk index 0e0b4adc6b..d51d533a08 100644 --- a/plugins/usbdmx/Makefile.mk +++ b/plugins/usbdmx/Makefile.mk @@ -3,30 +3,26 @@ if USE_LIBUSB lib_LTLIBRARIES += plugins/usbdmx/libolausbdmx.la plugins_usbdmx_libolausbdmx_la_SOURCES = \ - plugins/usbdmx/AnymaDevice.cpp \ - plugins/usbdmx/AnymaDevice.h \ plugins/usbdmx/AnymaWidget.cpp \ plugins/usbdmx/AnymaWidget.h \ plugins/usbdmx/AnymaWidgetFactory.cpp \ plugins/usbdmx/AnymaWidgetFactory.h \ plugins/usbdmx/AsyncPluginImpl.cpp \ plugins/usbdmx/AsyncPluginImpl.h \ - plugins/usbdmx/EuroliteProDevice.cpp \ - plugins/usbdmx/EuroliteProDevice.h \ plugins/usbdmx/EuroliteProWidget.cpp \ plugins/usbdmx/EuroliteProWidget.h \ plugins/usbdmx/EuroliteProWidgetFactory.cpp \ plugins/usbdmx/EuroliteProWidgetFactory.h \ plugins/usbdmx/FirmwareLoader.h \ - plugins/usbdmx/GenericOutputPort.h \ + plugins/usbdmx/GenericDevice.cpp \ + plugins/usbdmx/GenericDevice.h \ plugins/usbdmx/GenericOutputPort.cpp \ + plugins/usbdmx/GenericOutputPort.h \ plugins/usbdmx/LibUsbAdaptor.cpp \ plugins/usbdmx/LibUsbAdaptor.h \ plugins/usbdmx/LibUsbHelper.cpp \ plugins/usbdmx/LibUsbHelper.h \ plugins/usbdmx/PluginImplInterface.h \ - plugins/usbdmx/SunliteDevice.cpp \ - plugins/usbdmx/SunliteDevice.h \ plugins/usbdmx/SunliteFirmware.h \ plugins/usbdmx/SunliteFirmwareLoader.cpp \ plugins/usbdmx/SunliteFirmwareLoader.h \ diff --git a/plugins/usbdmx/SunliteDevice.cpp b/plugins/usbdmx/SunliteDevice.cpp deleted file mode 100644 index 0a29640b19..0000000000 --- a/plugins/usbdmx/SunliteDevice.cpp +++ /dev/null @@ -1,42 +0,0 @@ -/* - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * 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 Library 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - * - * SunliteDevice.cpp - * The Sunlite USBDMX2 device. - * Copyright (C) 2010 Simon Newton - */ - -#include "plugins/usbdmx/SunliteDevice.h" - -#include "plugins/usbdmx/GenericOutputPort.h" -#include "plugins/usbdmx/SunliteWidget.h" - -namespace ola { -namespace plugin { -namespace usbdmx { - -SunliteDevice::SunliteDevice(ola::AbstractPlugin *owner, - SunliteWidget *widget) - : Device(owner, "Sunlite USBDMX2 Device"), - m_port(new GenericOutputPort(this, 0, widget)) { -} - -bool SunliteDevice::StartHook() { - AddPort(m_port.release()); - return true; -} -} // namespace usbdmx -} // namespace plugin -} // namespace ola diff --git a/plugins/usbdmx/SunliteDevice.h b/plugins/usbdmx/SunliteDevice.h deleted file mode 100644 index 22a8c2e0f3..0000000000 --- a/plugins/usbdmx/SunliteDevice.h +++ /dev/null @@ -1,54 +0,0 @@ -/* - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * 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 Library 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - * - * SunliteDevice.h - * The Sunlite USBDMX2 device. - * Copyright (C) 2010 Simon Newton - */ - -#ifndef PLUGINS_USBDMX_SUNLITEDEVICE_H_ -#define PLUGINS_USBDMX_SUNLITEDEVICE_H_ - -#include -#include -#include "ola/base/Macro.h" -#include "olad/Device.h" - -namespace ola { -namespace plugin { -namespace usbdmx { - -/** - * @brief A Sunlite device - */ -class SunliteDevice: public Device { - public: - SunliteDevice(ola::AbstractPlugin *owner, - class SunliteWidget *widget); - - std::string DeviceId() const { return "usbdmx2"; } - - protected: - bool StartHook(); - - private: - std::auto_ptr m_port; - - DISALLOW_COPY_AND_ASSIGN(SunliteDevice); -}; -} // namespace usbdmx -} // namespace plugin -} // namespace ola -#endif // PLUGINS_USBDMX_SUNLITEDEVICE_H_ diff --git a/plugins/usbdmx/SyncPluginImpl.cpp b/plugins/usbdmx/SyncPluginImpl.cpp index 5cfc635e78..f527a610fe 100644 --- a/plugins/usbdmx/SyncPluginImpl.cpp +++ b/plugins/usbdmx/SyncPluginImpl.cpp @@ -37,15 +37,13 @@ #include "olad/PluginAdaptor.h" #include "olad/Preferences.h" -#include "plugins/usbdmx/AnymaDevice.h" #include "plugins/usbdmx/AnymaWidget.h" #include "plugins/usbdmx/EuroliteProWidget.h" +#include "plugins/usbdmx/GenericDevice.h" +#include "plugins/usbdmx/SunliteWidget.h" -#include "plugins/usbdmx/EuroliteProDevice.h" #include "plugins/usbdmx/FirmwareLoader.h" -#include "plugins/usbdmx/SunliteDevice.h" #include "plugins/usbdmx/SunliteFirmwareLoader.h" -#include "plugins/usbdmx/SunliteWidget.h" #include "plugins/usbdmx/UsbDevice.h" #include "plugins/usbdmx/VellemanDevice.h" @@ -174,7 +172,8 @@ void SyncPluginImpl::CheckDevice(libusb_device *usb_device) { delete widget; return; } - device = new SunliteDevice(m_plugin, widget); + device = new GenericDevice( + m_plugin, widget, "Sunlite USBDMX2 Device", "usbdmx2"); } else if (device_descriptor.idVendor == 0x16C0 && device_descriptor.idProduct == 0x05DC) { @@ -243,7 +242,8 @@ Device* SyncPluginImpl::NewAnymaDevice( delete widget; return NULL; } - return new AnymaDevice(m_plugin, widget); + return new GenericDevice( + m_plugin, widget, "Anyma USB Device", "anyma-" + widget->SerialNumber()); } Device* SyncPluginImpl::NewEuroliteProDevice( @@ -266,7 +266,9 @@ Device* SyncPluginImpl::NewEuroliteProDevice( return NULL; } - return new EuroliteProDevice(m_plugin, widget); + return new GenericDevice( + m_plugin, widget, "EurolitePro USB Device", + "eurolite-" + widget->SerialNumber()); } /** From 798a2d9799a124fa34611543ad4b0aefbe71f6f1 Mon Sep 17 00:00:00 2001 From: Simon Newton Date: Thu, 13 Nov 2014 18:33:21 -0800 Subject: [PATCH 06/34] Change the velleman widget to use the new style. --- plugins/usbdmx/AnymaWidgetFactory.cpp | 7 +- plugins/usbdmx/AnymaWidgetFactory.h | 4 +- plugins/usbdmx/AsyncPluginImpl.cpp | 74 ++-- plugins/usbdmx/AsyncPluginImpl.h | 6 +- plugins/usbdmx/EuroliteProWidgetFactory.cpp | 7 +- plugins/usbdmx/EuroliteProWidgetFactory.h | 4 +- .../{VellemanDevice.cpp => GenericDevice.cpp} | 37 +- .../{VellemanDevice.h => GenericDevice.h} | 39 +- plugins/usbdmx/Makefile.mk | 9 +- plugins/usbdmx/SunliteWidgetFactory.cpp | 12 +- plugins/usbdmx/SunliteWidgetFactory.h | 6 +- plugins/usbdmx/SyncPluginImpl.cpp | 21 +- plugins/usbdmx/VellemanOutputPort.cpp | 299 --------------- plugins/usbdmx/VellemanOutputPort.h | 82 ---- plugins/usbdmx/VellemanWidget.cpp | 358 ++++++++++++++++++ plugins/usbdmx/VellemanWidget.h | 98 +++++ plugins/usbdmx/VellemanWidgetFactory.cpp | 49 +++ .../{UsbDevice.h => VellemanWidgetFactory.h} | 45 +-- plugins/usbdmx/Widget.h | 44 +++ plugins/usbdmx/WidgetFactory.h | 4 + 20 files changed, 695 insertions(+), 510 deletions(-) rename plugins/usbdmx/{VellemanDevice.cpp => GenericDevice.cpp} (58%) rename plugins/usbdmx/{VellemanDevice.h => GenericDevice.h} (58%) delete mode 100644 plugins/usbdmx/VellemanOutputPort.cpp delete mode 100644 plugins/usbdmx/VellemanOutputPort.h create mode 100644 plugins/usbdmx/VellemanWidget.cpp create mode 100644 plugins/usbdmx/VellemanWidget.h create mode 100644 plugins/usbdmx/VellemanWidgetFactory.cpp rename plugins/usbdmx/{UsbDevice.h => VellemanWidgetFactory.h} (53%) create mode 100644 plugins/usbdmx/Widget.h diff --git a/plugins/usbdmx/AnymaWidgetFactory.cpp b/plugins/usbdmx/AnymaWidgetFactory.cpp index 28424a9358..f9d1b72d02 100644 --- a/plugins/usbdmx/AnymaWidgetFactory.cpp +++ b/plugins/usbdmx/AnymaWidgetFactory.cpp @@ -28,15 +28,14 @@ namespace ola { namespace plugin { namespace usbdmx { -const uint16_t AnymaWidgetFactory::ANYMA_VENDOR_ID = 0x16C0; -const uint16_t AnymaWidgetFactory::ANYMA_PRODUCT_ID = 0x05DC; +const uint16_t AnymaWidgetFactory::VENDOR_ID = 0x16C0; +const uint16_t AnymaWidgetFactory::PRODUCT_ID = 0x05DC; bool AnymaWidgetFactory::DeviceAdded( WidgetObserver *observer, libusb_device *usb_device, const struct libusb_device_descriptor &descriptor) { - if (descriptor.idVendor != ANYMA_VENDOR_ID || - descriptor.idProduct != ANYMA_PRODUCT_ID || + if (descriptor.idVendor != VENDOR_ID || descriptor.idProduct != PRODUCT_ID || HasDevice(usb_device)) { return false; } diff --git a/plugins/usbdmx/AnymaWidgetFactory.h b/plugins/usbdmx/AnymaWidgetFactory.h index bd9fa20f72..a5e4ed479d 100644 --- a/plugins/usbdmx/AnymaWidgetFactory.h +++ b/plugins/usbdmx/AnymaWidgetFactory.h @@ -43,8 +43,8 @@ class AnymaWidgetFactory : public BaseWidgetFactory { private: bool m_missing_serial_number; - static const uint16_t ANYMA_VENDOR_ID; - static const uint16_t ANYMA_PRODUCT_ID; + static const uint16_t VENDOR_ID; + static const uint16_t PRODUCT_ID; DISALLOW_COPY_AND_ASSIGN(AnymaWidgetFactory); }; diff --git a/plugins/usbdmx/AsyncPluginImpl.cpp b/plugins/usbdmx/AsyncPluginImpl.cpp index 2b75351d97..7c65909363 100644 --- a/plugins/usbdmx/AsyncPluginImpl.cpp +++ b/plugins/usbdmx/AsyncPluginImpl.cpp @@ -34,8 +34,8 @@ #include "plugins/usbdmx/EuroliteProWidgetFactory.h" #include "plugins/usbdmx/GenericDevice.h" #include "plugins/usbdmx/SunliteWidgetFactory.h" - -#include "plugins/usbdmx/VellemanDevice.h" +#include "plugins/usbdmx/VellemanWidget.h" +#include "plugins/usbdmx/VellemanWidgetFactory.h" namespace ola { namespace plugin { @@ -151,6 +151,7 @@ AsyncPluginImpl::AsyncPluginImpl(PluginAdaptor *plugin_adaptor, m_widget_factories.push_back(new AnymaWidgetFactory()); m_widget_factories.push_back(new EuroliteProWidgetFactory()); m_widget_factories.push_back(new SunliteWidgetFactory()); + m_widget_factories.push_back(new VellemanWidgetFactory()); } AsyncPluginImpl::~AsyncPluginImpl() { @@ -178,7 +179,7 @@ bool AsyncPluginImpl::Start() { /* m_plugin_adaptor->RegisterRepeatingTimeout( 3500, - NewSingleCallback(this, &AsyncPluginImpl::FindDevices)); + NewSingleCallback(this, &AsyncPluginImpl::FindUSBDevices)); */ } @@ -227,32 +228,29 @@ bool AsyncPluginImpl::NewWidget(class Widget *widget) { } bool AsyncPluginImpl::NewWidget(class AnymaWidget *widget) { - GenericDevice *device = new GenericDevice( - m_plugin, widget, "Anyma USB Device", "anyma-" + widget->SerialNumber()); - - if (!device->Start()) { - delete device; - return false; - } - - Device *old_device = STLReplacePtr(&m_widget_device_map, widget, device); - if (old_device) { - m_plugin_adaptor->UnregisterDevice(old_device); - old_device->Stop(); - delete old_device; - } - m_plugin_adaptor->RegisterDevice(device); - return true; + return StartAndRegisterDevice( + widget, + new GenericDevice(m_plugin, widget, "Anyma USB Device", + "anyma-" + widget->SerialNumber())); } bool AsyncPluginImpl::NewWidget(class EuroliteProWidget *widget) { - (void) widget; - return false; + return StartAndRegisterDevice( + widget, + new GenericDevice(m_plugin, widget, "EurolitePro USB Device", + "eurolite-" + widget->SerialNumber())); } bool AsyncPluginImpl::NewWidget(class SunliteWidget *widget) { - (void) widget; - return false; + return StartAndRegisterDevice( + widget, + new GenericDevice(m_plugin, widget, "Sunlite USBDMX2 Device", "usbdmx2")); +} + +bool AsyncPluginImpl::NewWidget(class VellemanWidget *widget) { + return StartAndRegisterDevice( + widget, + new GenericDevice(m_plugin, widget, "Velleman USB Device", "velleman")); } void AsyncPluginImpl::WidgetRemoved(class Widget *widget) { @@ -276,6 +274,10 @@ void AsyncPluginImpl::WidgetRemoved(class SunliteWidget *widget) { (void) widget; } +void AsyncPluginImpl::WidgetRemoved(class VellemanWidget *widget) { + (void) widget; +} + bool AsyncPluginImpl::SetupHotPlug() { #if defined(LIBUSB_API_VERSION) && (LIBUSB_API_VERSION >= 0x01000102) if (libusb_has_capability(LIBUSB_CAP_HAS_HOTPLUG) == 0) { @@ -305,7 +307,7 @@ bool AsyncPluginImpl::SetupHotPlug() { /* * Find known devices & register them */ -void AsyncPluginImpl::FindDevices() { +void AsyncPluginImpl::FindUSBDevices() { libusb_device **device_list; size_t device_count = libusb_get_device_list(NULL, &device_list); @@ -326,14 +328,6 @@ void AsyncPluginImpl::DeviceAdded(libusb_device *usb_device) { return; } } - - // Old style - /* - if (device_descriptor.idVendor == 0x10cf && - device_descriptor.idProduct == 0x8062) { - OLA_INFO << "Found a Velleman USB device"; - device = new VellemanDevice(m_plugin, usb_device); - */ } void AsyncPluginImpl::DeviceRemoved(libusb_device *usb_device) { @@ -343,6 +337,22 @@ void AsyncPluginImpl::DeviceRemoved(libusb_device *usb_device) { factory->DeviceRemoved(this, usb_device); } } + +bool AsyncPluginImpl::StartAndRegisterDevice(Widget *widget, Device *device) { + if (!device->Start()) { + delete device; + return false; + } + + Device *old_device = STLReplacePtr(&m_widget_device_map, widget, device); + if (old_device) { + m_plugin_adaptor->UnregisterDevice(old_device); + old_device->Stop(); + delete old_device; + } + m_plugin_adaptor->RegisterDevice(device); + return true; +} } // namespace usbdmx } // namespace plugin } // namespace ola diff --git a/plugins/usbdmx/AsyncPluginImpl.h b/plugins/usbdmx/AsyncPluginImpl.h index d041a9bc5e..3aeb461152 100644 --- a/plugins/usbdmx/AsyncPluginImpl.h +++ b/plugins/usbdmx/AsyncPluginImpl.h @@ -87,11 +87,13 @@ class AsyncPluginImpl: public PluginImplInterface, public WidgetObserver { bool NewWidget(class AnymaWidget *widget); bool NewWidget(class EuroliteProWidget *widget); bool NewWidget(class SunliteWidget *widget); + bool NewWidget(class VellemanWidget *widget); void WidgetRemoved(class Widget *widget); void WidgetRemoved(class AnymaWidget *widget); void WidgetRemoved(class EuroliteProWidget *widget); void WidgetRemoved(class SunliteWidget *widget); + void WidgetRemoved(class VellemanWidget *widget); private: typedef std::vector WidgetFactories; @@ -124,7 +126,9 @@ class AsyncPluginImpl: public PluginImplInterface, public WidgetObserver { void DeviceAdded(libusb_device *device); void DeviceRemoved(libusb_device *device); - void FindDevices(); + bool StartAndRegisterDevice(Widget *, Device *device); + + void FindUSBDevices(); DISALLOW_COPY_AND_ASSIGN(AsyncPluginImpl); }; diff --git a/plugins/usbdmx/EuroliteProWidgetFactory.cpp b/plugins/usbdmx/EuroliteProWidgetFactory.cpp index f0a215ebf6..20385930b1 100644 --- a/plugins/usbdmx/EuroliteProWidgetFactory.cpp +++ b/plugins/usbdmx/EuroliteProWidgetFactory.cpp @@ -27,15 +27,14 @@ namespace ola { namespace plugin { namespace usbdmx { -const uint16_t EuroliteProWidgetFactory::EUROLITE_PRODUCT_ID = 0xfa63; -const uint16_t EuroliteProWidgetFactory::EUROLITE_VENDOR_ID = 0x04d; +const uint16_t EuroliteProWidgetFactory::PRODUCT_ID = 0xfa63; +const uint16_t EuroliteProWidgetFactory::VENDOR_ID = 0x04d; bool EuroliteProWidgetFactory::DeviceAdded( WidgetObserver *observer, libusb_device *usb_device, const struct libusb_device_descriptor &descriptor) { - if (descriptor.idVendor != EUROLITE_VENDOR_ID || - descriptor.idProduct != EUROLITE_PRODUCT_ID || + if (descriptor.idVendor != VENDOR_ID || descriptor.idProduct != PRODUCT_ID || HasDevice(usb_device)) { return false; } diff --git a/plugins/usbdmx/EuroliteProWidgetFactory.h b/plugins/usbdmx/EuroliteProWidgetFactory.h index 608538d68b..38a265de33 100644 --- a/plugins/usbdmx/EuroliteProWidgetFactory.h +++ b/plugins/usbdmx/EuroliteProWidgetFactory.h @@ -43,8 +43,8 @@ class EuroliteProWidgetFactory const struct libusb_device_descriptor &descriptor); private: - static const uint16_t EUROLITE_PRODUCT_ID; - static const uint16_t EUROLITE_VENDOR_ID; + static const uint16_t PRODUCT_ID; + static const uint16_t VENDOR_ID; DISALLOW_COPY_AND_ASSIGN(EuroliteProWidgetFactory); }; diff --git a/plugins/usbdmx/VellemanDevice.cpp b/plugins/usbdmx/GenericDevice.cpp similarity index 58% rename from plugins/usbdmx/VellemanDevice.cpp rename to plugins/usbdmx/GenericDevice.cpp index 9ab068df66..f32ed29e93 100644 --- a/plugins/usbdmx/VellemanDevice.cpp +++ b/plugins/usbdmx/GenericDevice.cpp @@ -13,36 +13,31 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * - * VellemanDevice.cpp - * The Velleman usb driver - * Copyright (C) 2010 Simon Newton + * GenericDevice.cpp + * A Generic device that creates a single port. + * Copyright (C) 2014 Simon Newton */ -#include -#include -#include +#include "plugins/usbdmx/GenericDevice.h" -#include "ola/Logging.h" -#include "plugins/usbdmx/VellemanDevice.h" -#include "plugins/usbdmx/VellemanOutputPort.h" +#include "plugins/usbdmx/Widget.h" +#include "plugins/usbdmx/GenericOutputPort.h" namespace ola { namespace plugin { namespace usbdmx { +GenericDevice::GenericDevice(ola::AbstractPlugin *owner, + Widget *widget, + const std::string &device_name, + const std::string &device_id) + : Device(owner, device_name), + m_device_id(device_id), + m_port(new GenericOutputPort(this, 0, widget)) { +} -/* - * Start this device. - */ -bool VellemanDevice::StartHook() { - VellemanOutputPort *output_port = new VellemanOutputPort(this, - 0, - m_usb_device); - if (!output_port->Start()) { - delete output_port; - return false; - } - AddPort(output_port); +bool GenericDevice::StartHook() { + AddPort(m_port.release()); return true; } } // namespace usbdmx diff --git a/plugins/usbdmx/VellemanDevice.h b/plugins/usbdmx/GenericDevice.h similarity index 58% rename from plugins/usbdmx/VellemanDevice.h rename to plugins/usbdmx/GenericDevice.h index d5717067b0..96dcc7d4c4 100644 --- a/plugins/usbdmx/VellemanDevice.h +++ b/plugins/usbdmx/GenericDevice.h @@ -13,42 +13,47 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * - * VellemanDevice.h - * Interface for the Velleman device - * Copyright (C) 2010 Simon Newton + * GenericDevice.h + * A Generic device that creates a single port. + * Copyright (C) 2014 Simon Newton */ -#ifndef PLUGINS_USBDMX_VELLEMANDEVICE_H_ -#define PLUGINS_USBDMX_VELLEMANDEVICE_H_ +#ifndef PLUGINS_USBDMX_GENERICDEVICE_H_ +#define PLUGINS_USBDMX_GENERICDEVICE_H_ -#include +#include #include #include "ola/base/Macro.h" -#include "plugins/usbdmx/UsbDevice.h" +#include "olad/Device.h" namespace ola { namespace plugin { namespace usbdmx { -/* - * A Velleman device +/** + * @brief An Generic device. */ -class VellemanDevice: public UsbDevice { +class GenericDevice: public Device { public: - VellemanDevice(ola::AbstractPlugin *owner, - libusb_device *usb_device): - UsbDevice(owner, "Velleman USB Device", usb_device) { - } + GenericDevice(ola::AbstractPlugin *owner, + class Widget *widget, + const std::string &device_name, + const std::string &device_id); - std::string DeviceId() const { return "velleman"; } + std::string DeviceId() const { + return m_device_id; + } protected: bool StartHook(); private: - DISALLOW_COPY_AND_ASSIGN(VellemanDevice); + const std::string m_device_id; + std::auto_ptr m_port; + + DISALLOW_COPY_AND_ASSIGN(GenericDevice); }; } // namespace usbdmx } // namespace plugin } // namespace ola -#endif // PLUGINS_USBDMX_VELLEMANDEVICE_H_ +#endif // PLUGINS_USBDMX_GENERICDEVICE_H_ diff --git a/plugins/usbdmx/Makefile.mk b/plugins/usbdmx/Makefile.mk index d51d533a08..78ec40ab56 100644 --- a/plugins/usbdmx/Makefile.mk +++ b/plugins/usbdmx/Makefile.mk @@ -34,13 +34,12 @@ plugins_usbdmx_libolausbdmx_la_SOURCES = \ plugins/usbdmx/SyncPluginImpl.h \ plugins/usbdmx/ThreadedUsbSender.cpp \ plugins/usbdmx/ThreadedUsbSender.h \ - plugins/usbdmx/UsbDevice.h \ plugins/usbdmx/UsbDmxPlugin.cpp \ plugins/usbdmx/UsbDmxPlugin.h \ - plugins/usbdmx/VellemanDevice.cpp \ - plugins/usbdmx/VellemanDevice.h \ - plugins/usbdmx/VellemanOutputPort.cpp \ - plugins/usbdmx/VellemanOutputPort.h \ + plugins/usbdmx/VellemanWidget.cpp \ + plugins/usbdmx/VellemanWidget.h \ + plugins/usbdmx/VellemanWidgetFactory.cpp \ + plugins/usbdmx/VellemanWidgetFactory.h \ plugins/usbdmx/WidgetFactory.h plugins_usbdmx_libolausbdmx_la_CXXFLAGS = $(COMMON_CXXFLAGS) $(libusb_CFLAGS) diff --git a/plugins/usbdmx/SunliteWidgetFactory.cpp b/plugins/usbdmx/SunliteWidgetFactory.cpp index 4a8ab3eb0c..be676f76cb 100644 --- a/plugins/usbdmx/SunliteWidgetFactory.cpp +++ b/plugins/usbdmx/SunliteWidgetFactory.cpp @@ -27,21 +27,23 @@ namespace ola { namespace plugin { namespace usbdmx { -const uint16_t SunliteWidgetFactory::SUNLITE_VENDOR_ID = 0x0962; +const uint16_t SunliteWidgetFactory::EMPTY_PRODUCT_ID = 0x2000; +const uint16_t SunliteWidgetFactory::FULL_PRODUCT_ID = 0x2001; +const uint16_t SunliteWidgetFactory::VENDOR_ID = 0x0962; bool SunliteWidgetFactory::DeviceAdded( WidgetObserver *observer, libusb_device *usb_device, const struct libusb_device_descriptor &descriptor) { - if (descriptor.idVendor == SUNLITE_VENDOR_ID && - descriptor.idProduct == 0x2000) { + if (descriptor.idVendor == VENDOR_ID && + descriptor.idProduct == EMPTY_PRODUCT_ID) { OLA_INFO << "New empty SunliteDevice"; // TODO(simon): Make this async. SunliteFirmwareLoader loader(usb_device); loader.LoadFirmware(); return true; - } else if (descriptor.idVendor == SUNLITE_VENDOR_ID && - descriptor.idProduct == 0x2001 && + } else if (descriptor.idVendor == VENDOR_ID && + descriptor.idProduct == FULL_PRODUCT_ID && !HasDevice(usb_device)) { OLA_INFO << "Found a new Sunlite device"; diff --git a/plugins/usbdmx/SunliteWidgetFactory.h b/plugins/usbdmx/SunliteWidgetFactory.h index ce09a0cf93..76ccc39b5b 100644 --- a/plugins/usbdmx/SunliteWidgetFactory.h +++ b/plugins/usbdmx/SunliteWidgetFactory.h @@ -45,7 +45,11 @@ class SunliteWidgetFactory : public BaseWidgetFactory { libusb_device *device); private: - static const uint16_t SUNLITE_VENDOR_ID; + // The product ID for widgets that are missing their firmware. + static const uint16_t EMPTY_PRODUCT_ID; + // The product ID for widgets with the firmware. + static const uint16_t FULL_PRODUCT_ID; + static const uint16_t VENDOR_ID; DISALLOW_COPY_AND_ASSIGN(SunliteWidgetFactory); }; diff --git a/plugins/usbdmx/SyncPluginImpl.cpp b/plugins/usbdmx/SyncPluginImpl.cpp index f527a610fe..87a2fb97c6 100644 --- a/plugins/usbdmx/SyncPluginImpl.cpp +++ b/plugins/usbdmx/SyncPluginImpl.cpp @@ -20,7 +20,6 @@ #include "plugins/usbdmx/SyncPluginImpl.h" -#include #include #include #include @@ -32,20 +31,16 @@ #include "ola/Callback.h" #include "ola/Logging.h" #include "ola/StringUtils.h" -#include "ola/io/Descriptor.h" #include "ola/stl/STLUtils.h" #include "olad/PluginAdaptor.h" -#include "olad/Preferences.h" #include "plugins/usbdmx/AnymaWidget.h" #include "plugins/usbdmx/EuroliteProWidget.h" -#include "plugins/usbdmx/GenericDevice.h" -#include "plugins/usbdmx/SunliteWidget.h" - #include "plugins/usbdmx/FirmwareLoader.h" +#include "plugins/usbdmx/GenericDevice.h" #include "plugins/usbdmx/SunliteFirmwareLoader.h" -#include "plugins/usbdmx/UsbDevice.h" -#include "plugins/usbdmx/VellemanDevice.h" +#include "plugins/usbdmx/SunliteWidget.h" +#include "plugins/usbdmx/VellemanWidget.h" namespace ola { @@ -163,7 +158,15 @@ void SyncPluginImpl::CheckDevice(libusb_device *usb_device) { if (device_descriptor.idVendor == 0x10cf && device_descriptor.idProduct == 0x8062) { OLA_INFO << "Found a Velleman USB device"; - device = new VellemanDevice(m_plugin, usb_device); + SynchronousVellemanWidget *widget = new SynchronousVellemanWidget( + usb_device); + if (!widget->Init()) { + delete widget; + return; + } + device = new GenericDevice( + m_plugin, widget, "Velleman USB Device", "velleman"); + } else if (device_descriptor.idVendor == 0x0962 && device_descriptor.idProduct == 0x2001) { OLA_INFO << "Found a Sunlite device"; diff --git a/plugins/usbdmx/VellemanOutputPort.cpp b/plugins/usbdmx/VellemanOutputPort.cpp deleted file mode 100644 index 751213b8e0..0000000000 --- a/plugins/usbdmx/VellemanOutputPort.cpp +++ /dev/null @@ -1,299 +0,0 @@ -/* - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * 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 Library 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - * - * VellemanOutputPort.cpp - * Thread for the Velleman Output Port - * Copyright (C) 2010 Simon Newton - * - * See the comments in VellemanOutputPort.h - */ - -#include -#include -#include -#include - -#include "ola/Logging.h" -#include "plugins/usbdmx/VellemanOutputPort.h" -#include "plugins/usbdmx/VellemanDevice.h" - - -namespace ola { -namespace plugin { -namespace usbdmx { - -using std::string; - - -/* - * Create a new VellemanOutputPort object - */ -VellemanOutputPort::VellemanOutputPort(VellemanDevice *parent, - unsigned int id, - libusb_device *usb_device) - : BasicOutputPort(parent, id), - m_term(false), - m_chunk_size(8), // the standard unit uses 8 - m_usb_device(usb_device), - m_usb_handle(NULL) { -} - - -/* - * Cleanup - */ -VellemanOutputPort::~VellemanOutputPort() { - { - ola::thread::MutexLocker locker(&m_term_mutex); - m_term = true; - } - Join(); -} - - -/* - * Start this thread - */ -bool VellemanOutputPort::Start() { - libusb_device_handle *usb_handle; - libusb_config_descriptor *config; - - if (libusb_open(m_usb_device, &usb_handle)) { - OLA_WARN << "Failed to open Velleman usb device"; - return false; - } - - if (libusb_kernel_driver_active(usb_handle, 0)) { - if (libusb_detach_kernel_driver(usb_handle, 0)) { - OLA_WARN << "Failed to detach kernel driver"; - libusb_close(usb_handle); - return false; - } - } - - // this device only has one configuration - int ret_code = libusb_set_configuration(usb_handle, CONFIGURATION); - if (ret_code) { - OLA_WARN << "Velleman set config failed, with libusb error code " << - ret_code; - libusb_close(usb_handle); - return false; - } - - if (libusb_get_active_config_descriptor(m_usb_device, &config)) { - OLA_WARN << "Could not get active config descriptor"; - libusb_close(usb_handle); - return false; - } - - // determine the max packet size, see - // http://opendmx.net/index.php/Velleman_K8062_Upgrade - if (config && - config->interface && - config->interface->altsetting && - config->interface->altsetting->endpoint) { - uint16_t max_packet_size = - config->interface->altsetting->endpoint->wMaxPacketSize; - OLA_DEBUG << "Velleman K8062 max packet size is " << max_packet_size; - if (max_packet_size == UPGRADED_CHUNK_SIZE) - // this means the upgrade is present - m_chunk_size = max_packet_size; - } - libusb_free_config_descriptor(config); - - if (libusb_claim_interface(usb_handle, INTERFACE)) { - OLA_WARN << "Failed to claim Velleman usb device"; - libusb_close(usb_handle); - return false; - } - - m_usb_handle = usb_handle; - bool ret = ola::thread::Thread::Start(); - if (!ret) { - OLA_WARN << "pthread create failed"; - libusb_release_interface(m_usb_handle, INTERFACE); - libusb_close(usb_handle); - return false; - } - return true; -} - - -/* - * Run this thread - */ -void *VellemanOutputPort::Run() { - DmxBuffer buffer; - if (!m_usb_handle) - return NULL; - - while (1) { - { - ola::thread::MutexLocker locker(&m_term_mutex); - if (m_term) - break; - } - - { - ola::thread::MutexLocker locker(&m_data_mutex); - buffer.Set(m_buffer); - } - - if (buffer.Size()) { - if (!SendDMX(buffer)) { - OLA_WARN << "Send failed, stopping thread..."; - break; - } - } else { - // sleep for a bit - usleep(40000); - } - } - libusb_release_interface(m_usb_handle, 0); - libusb_close(m_usb_handle); - return NULL; -} - - -/* - * Store the data in the shared buffer - */ -bool VellemanOutputPort::WriteDMX(const DmxBuffer &buffer, uint8_t priority) { - ola::thread::MutexLocker locker(&m_data_mutex); - m_buffer.Set(buffer); - return true; - (void) priority; -} - - -/** - * Return the port description - */ -string VellemanOutputPort::Description() const { - if (m_chunk_size == UPGRADED_CHUNK_SIZE) - return "VX8062"; - else - return "K8062"; -} - - -/* - * Send the dmx out the widget - * @return true on success, false on failure - */ -bool VellemanOutputPort::SendDMX(const DmxBuffer &buffer) { - unsigned char usb_data[m_chunk_size]; - unsigned int size = buffer.Size(); - const uint8_t *data = buffer.GetRaw(); - unsigned int i = 0; - unsigned int n; - - // this could be up to 254 for the standard interface but then the shutdown - // process gets wacky. Limit it to 100 for the standard and 255 for the - // extended. - unsigned int max_compressed_channels = m_chunk_size == UPGRADED_CHUNK_SIZE ? - 254 : 100; - unsigned int compressed_channel_count = m_chunk_size - 2; - unsigned int channel_count = m_chunk_size - 1; - - memset(usb_data, 0, sizeof(usb_data)); - - if (m_chunk_size == UPGRADED_CHUNK_SIZE && size <= m_chunk_size - 2) { - // if the upgrade is present and we can fit the data in a single packet - // use the 7 message type - usb_data[0] = 7; - usb_data[1] = size; // number of channels in packet - memcpy(usb_data + 2, data, std::min(size, m_chunk_size - 2)); - } else { - // otherwise use 4 to signal the start of frame - for (n = 0; - n < max_compressed_channels && n < size - compressed_channel_count - && !data[n]; - n++) { - } - usb_data[0] = 4; - usb_data[1] = n + 1; // include start code - memcpy(usb_data + 2, data + n, compressed_channel_count); - i += n + compressed_channel_count; - } - - if (!SendDataChunk(usb_data)) - return false; - - while (i < size - channel_count) { - for (n = 0; - n < max_compressed_channels && n + i < size - compressed_channel_count - && !data[i + n]; - n++) { - } - if (n) { - // we have leading zeros - usb_data[0] = 5; - usb_data[1] = n; - memcpy(usb_data + 2, data + i + n, compressed_channel_count); - i += n + compressed_channel_count; - } else { - usb_data[0] = 2; - memcpy(usb_data + 1, data + i, channel_count); - i += channel_count; - } - if (!SendDataChunk(usb_data)) - return false; - } - - // send the last channels - if (m_chunk_size == UPGRADED_CHUNK_SIZE) { - // if running in extended mode we can use the 6 message type to send - // everything at once. - usb_data[0] = 6; - usb_data[1] = size - i; - memcpy(usb_data + 2, data + i, size - i); - if (!SendDataChunk(usb_data)) - return false; - - } else { - // else we use the 3 message type to send one at a time - for (; i != size; i++) { - usb_data[0] = 3; - usb_data[1] = data[i]; - if (!SendDataChunk(usb_data)) - return false; - } - } - return true; -} - - -/* - * Send 8 bytes to the usb device - * @returns false if there was an error, true otherwise - */ -bool VellemanOutputPort::SendDataChunk(uint8_t *usb_data) { - int transferred; - int ret = libusb_interrupt_transfer( - m_usb_handle, - ENDPOINT, - reinterpret_cast(usb_data), - m_chunk_size, - &transferred, - URB_TIMEOUT_MS); - if (ret) - OLA_INFO << "USB return code was " << ret << ", transferred " << - transferred; - return ret == 0; -} -} // namespace usbdmx -} // namespace plugin -} // namespace ola diff --git a/plugins/usbdmx/VellemanOutputPort.h b/plugins/usbdmx/VellemanOutputPort.h deleted file mode 100644 index 3fe2ae30d8..0000000000 --- a/plugins/usbdmx/VellemanOutputPort.h +++ /dev/null @@ -1,82 +0,0 @@ -/* - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * 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 Library 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - * - * VellemanOutputPort.h - * The output port for a Velleman 8062 device. - * Copyright (C) 2010 Simon Newton - * - * Because this interface is so slow we run the output in a separate thread. It - * takes around 8ms to respond to an urb and in the worst case we send 74 urbs - * per universe. - * - * It would be interesting to see if you can pipeline the urbs to improve the - * performance. - */ - -#ifndef PLUGINS_USBDMX_VELLEMANOUTPUTPORT_H_ -#define PLUGINS_USBDMX_VELLEMANOUTPUTPORT_H_ - -#include -#include -#include -#include "ola/base/Macro.h" -#include "ola/DmxBuffer.h" -#include "ola/thread/Thread.h" -#include "olad/Port.h" - -namespace ola { -namespace plugin { -namespace usbdmx { - -class VellemanDevice; - -class VellemanOutputPort: public BasicOutputPort, ola::thread::Thread { - public: - VellemanOutputPort(VellemanDevice *parent, - unsigned int id, - libusb_device *usb_device); - ~VellemanOutputPort(); - - bool Start(); - void *Run(); - - bool WriteDMX(const DmxBuffer &buffer, uint8_t priority); - std::string Description() const; - - private: - static const unsigned char ENDPOINT = 0x01; - // 25ms seems to be about the shortest we can go - static const unsigned int URB_TIMEOUT_MS = 25; - static const int CONFIGURATION = 1; - static const int INTERFACE = 0; - static const unsigned int UPGRADED_CHUNK_SIZE = 64; - - bool m_term; - unsigned int m_chunk_size; - libusb_device *m_usb_device; - libusb_device_handle *m_usb_handle; - DmxBuffer m_buffer; - ola::thread::Mutex m_data_mutex; - ola::thread::Mutex m_term_mutex; - - bool SendDMX(const DmxBuffer &buffer_old); - bool SendDataChunk(uint8_t *usb_data); - - DISALLOW_COPY_AND_ASSIGN(VellemanOutputPort); -}; -} // namespace usbdmx -} // namespace plugin -} // namespace ola -#endif // PLUGINS_USBDMX_VELLEMANOUTPUTPORT_H_ diff --git a/plugins/usbdmx/VellemanWidget.cpp b/plugins/usbdmx/VellemanWidget.cpp new file mode 100644 index 0000000000..79190115d3 --- /dev/null +++ b/plugins/usbdmx/VellemanWidget.cpp @@ -0,0 +1,358 @@ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * 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 Library 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * VellemanWidget.cpp + * The synchronous and asynchronous Velleman widgets. + * Copyright (C) 2014 Simon Newton + */ + +#include "plugins/usbdmx/VellemanWidget.h" + +#include +#include +#include + +#include "ola/Logging.h" +#include "ola/Constants.h" +#include "plugins/usbdmx/LibUsbHelper.h" +#include "plugins/usbdmx/ThreadedUsbSender.h" + +namespace ola { +namespace plugin { +namespace usbdmx { + +using std::string; + +namespace { + +static const unsigned char ENDPOINT = 0x01; +// 25ms seems to be about the shortest we can go +static const unsigned int URB_TIMEOUT_MS = 25; +static const int CONFIGURATION = 1; +static const int INTERFACE = 0; +static const unsigned int UPGRADED_CHUNK_SIZE = 64; + +/* + * Called by the AsynchronousSunliteWidget when the transfer completes. +void AsyncCallback(struct libusb_transfer *transfer) { + AsynchronousVellemanWidget *widget = + reinterpret_cast(transfer->user_data); + widget->TransferComplete(transfer); +} + */ + +} // namespace + +/* + * Sends messages to a Velleman device in a separate thread. + */ +class VellemanThreadedSender: public ThreadedUsbSender { + public: + VellemanThreadedSender(libusb_device *usb_device, + libusb_device_handle *handle, + unsigned int chunk_size) + : ThreadedUsbSender(usb_device, handle), + m_chunk_size(chunk_size) { + } + + private: + const unsigned int m_chunk_size; + + bool TransmitBuffer(libusb_device_handle *handle, + const DmxBuffer &buffer); + bool SendDataChunk(libusb_device_handle *handle, uint8_t *usb_data, + unsigned int chunk_size); +}; + +bool VellemanThreadedSender::TransmitBuffer(libusb_device_handle *handle, + const DmxBuffer &buffer) { + unsigned char usb_data[m_chunk_size]; + unsigned int size = buffer.Size(); + const uint8_t *data = buffer.GetRaw(); + unsigned int i = 0; + unsigned int n; + + // this could be up to 254 for the standard interface but then the shutdown + // process gets wacky. Limit it to 100 for the standard and 255 for the + // extended. + unsigned int max_compressed_channels = m_chunk_size == UPGRADED_CHUNK_SIZE ? + 254 : 100; + unsigned int compressed_channel_count = m_chunk_size - 2; + unsigned int channel_count = m_chunk_size - 1; + + memset(usb_data, 0, sizeof(usb_data)); + + if (m_chunk_size == UPGRADED_CHUNK_SIZE && size <= m_chunk_size - 2) { + // if the upgrade is present and we can fit the data in a single packet + // use the 7 message type + usb_data[0] = 7; + usb_data[1] = size; // number of channels in packet + memcpy(usb_data + 2, data, std::min(size, m_chunk_size - 2)); + } else { + // otherwise use 4 to signal the start of frame + for (n = 0; + n < max_compressed_channels && n < size - compressed_channel_count + && !data[n]; + n++) { + } + usb_data[0] = 4; + usb_data[1] = n + 1; // include start code + memcpy(usb_data + 2, data + n, compressed_channel_count); + i += n + compressed_channel_count; + } + + if (!SendDataChunk(handle, usb_data, m_chunk_size)) + return false; + + while (i < size - channel_count) { + for (n = 0; + n < max_compressed_channels && n + i < size - compressed_channel_count + && !data[i + n]; + n++) { + } + if (n) { + // we have leading zeros + usb_data[0] = 5; + usb_data[1] = n; + memcpy(usb_data + 2, data + i + n, compressed_channel_count); + i += n + compressed_channel_count; + } else { + usb_data[0] = 2; + memcpy(usb_data + 1, data + i, channel_count); + i += channel_count; + } + if (!SendDataChunk(handle, usb_data, m_chunk_size)) + return false; + } + + // send the last channels + if (m_chunk_size == UPGRADED_CHUNK_SIZE) { + // if running in extended mode we can use the 6 message type to send + // everything at once. + usb_data[0] = 6; + usb_data[1] = size - i; + memcpy(usb_data + 2, data + i, size - i); + if (!SendDataChunk(handle, usb_data, m_chunk_size)) + return false; + + } else { + // else we use the 3 message type to send one at a time + for (; i != size; i++) { + usb_data[0] = 3; + usb_data[1] = data[i]; + if (!SendDataChunk(handle, usb_data, m_chunk_size)) + return false; + } + } + return true; +} + +bool VellemanThreadedSender::SendDataChunk(libusb_device_handle *handle, + uint8_t *usb_data, + unsigned int chunk_size) { + int transferred; + int ret = libusb_interrupt_transfer( + handle, + ENDPOINT, + reinterpret_cast(usb_data), + chunk_size, + &transferred, + URB_TIMEOUT_MS); + if (ret) { + OLA_INFO << "USB return code was " << ret << ", transferred " + << transferred; + } + return ret == 0; +} + + +SynchronousVellemanWidget::SynchronousVellemanWidget( + libusb_device *usb_device) + : m_usb_device(usb_device) { +} + +bool SynchronousVellemanWidget::Init() { + libusb_device_handle *usb_handle; + + bool ok = LibUsbHelper::OpenDevice(m_usb_device, &usb_handle); + if (!ok) { + return false; + } + + if (libusb_kernel_driver_active(usb_handle, 0)) { + if (libusb_detach_kernel_driver(usb_handle, 0)) { + OLA_WARN << "Failed to detach kernel driver"; + libusb_close(usb_handle); + return false; + } + } + + // this device only has one configuration + int ret_code = libusb_set_configuration(usb_handle, CONFIGURATION); + if (ret_code) { + OLA_WARN << "Velleman set config failed, with libusb error code " << + ret_code; + libusb_close(usb_handle); + return false; + } + + libusb_config_descriptor *config; + if (libusb_get_active_config_descriptor(m_usb_device, &config)) { + OLA_WARN << "Could not get active config descriptor"; + libusb_close(usb_handle); + return false; + } + + // determine the max packet size, see + // http://opendmx.net/index.php/Velleman_K8062_Upgrade + // The standard size is 8. + unsigned int chunk_size = 8; + if (config && + config->interface && + config->interface->altsetting && + config->interface->altsetting->endpoint) { + uint16_t max_packet_size = + config->interface->altsetting->endpoint->wMaxPacketSize; + OLA_DEBUG << "Velleman K8062 max packet size is " << max_packet_size; + if (max_packet_size == UPGRADED_CHUNK_SIZE) + // this means the upgrade is present + chunk_size = max_packet_size; + } + libusb_free_config_descriptor(config); + + if (libusb_claim_interface(usb_handle, INTERFACE)) { + OLA_WARN << "Failed to claim Velleman usb device"; + libusb_close(usb_handle); + return false; + } + + std::auto_ptr sender( + new VellemanThreadedSender(m_usb_device, usb_handle, chunk_size)); + if (!sender->Start()) { + return false; + } + m_sender.reset(sender.release()); + return true; +} + +bool SynchronousVellemanWidget::SendDMX(const DmxBuffer &buffer) { + return m_sender.get() ? m_sender->SendDMX(buffer) : false; +} + + +AsynchronousVellemanWidget::AsynchronousVellemanWidget( + libusb_device *usb_device) + : VellemanWidget(), + m_usb_device(usb_device), + m_usb_handle(NULL), + m_control_setup_buffer(NULL), + m_transfer_state(IDLE) { + m_control_setup_buffer = + new uint8_t[LIBUSB_CONTROL_SETUP_SIZE + DMX_UNIVERSE_SIZE]; + + m_transfer = libusb_alloc_transfer(0); + libusb_ref_device(usb_device); +} + +AsynchronousVellemanWidget::~AsynchronousVellemanWidget() { + bool canceled = false; + OLA_INFO << "AsynchronousVellemanWidget shutdown"; + while (1) { + ola::thread::MutexLocker locker(&m_mutex); + if (m_transfer_state == IDLE) { + break; + } + if (!canceled) { + libusb_cancel_transfer(m_transfer); + canceled = true; + } + } + + libusb_free_transfer(m_transfer); + delete[] m_control_setup_buffer; + libusb_unref_device(m_usb_device); +} + +bool AsynchronousVellemanWidget::Init() { + bool ok = LibUsbHelper::OpenDeviceAndClaimInterface( + m_usb_device, 0, &m_usb_handle); + if (!ok) { + return false; + } + return true; +} + +bool AsynchronousVellemanWidget::SendDMX(const DmxBuffer &buffer) { + OLA_INFO << "Call to AsynchronousVellemanWidget::SendDMX"; + + if (!m_usb_handle) { + OLA_WARN << "AsynchronousVellemanWidget hasn't been initialized"; + return false; + } + + (void) buffer; + return false; + /* + ola::thread::MutexLocker locker(&m_mutex); + if (m_transfer_state != IDLE) { + return true; + } + + libusb_fill_control_setup( + m_control_setup_buffer, + LIBUSB_REQUEST_TYPE_VENDOR | LIBUSB_RECIPIENT_DEVICE | + LIBUSB_ENDPOINT_OUT, // bmRequestType + UDMX_SET_CHANNEL_RANGE, // bRequest + buffer.Size(), // wValue + 0, // wIndex + buffer.Size()); // wLength + + unsigned int length = DMX_UNIVERSE_SIZE; + buffer.Get(m_control_setup_buffer + LIBUSB_CONTROL_SETUP_SIZE, &length); + + libusb_fill_control_transfer( + m_transfer, + m_usb_handle, + m_control_setup_buffer, + &AsyncCallback, + this, + URB_TIMEOUT_MS); + + int ret = libusb_submit_transfer(m_transfer); + if (ret) { + OLA_WARN << "libusb_submit_transfer returned " << libusb_error_name(ret); + return false; + } + OLA_INFO << "submit ok"; + m_transfer_state = IN_PROGRESS; + return true; + */ +} + +void AsynchronousVellemanWidget::TransferComplete( + struct libusb_transfer *transfer) { + if (transfer != m_transfer) { + OLA_WARN << "Mismatched libusb transfer: " << transfer << " != " + << m_transfer; + return; + } + + OLA_INFO << "async transfer complete"; + ola::thread::MutexLocker locker(&m_mutex); + m_transfer_state = IDLE; +} +} // namespace usbdmx +} // namespace plugin +} // namespace ola diff --git a/plugins/usbdmx/VellemanWidget.h b/plugins/usbdmx/VellemanWidget.h new file mode 100644 index 0000000000..155e5456d1 --- /dev/null +++ b/plugins/usbdmx/VellemanWidget.h @@ -0,0 +1,98 @@ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * 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 Library 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * VellemanWidget.h + * The synchronous and asynchronous Velleman widgets. + * Copyright (C) 2014 Simon Newton + */ + +#ifndef PLUGINS_USBDMX_VELLEMANWIDGET_H_ +#define PLUGINS_USBDMX_VELLEMANWIDGET_H_ + +#include +#include + +#include "ola/DmxBuffer.h" +#include "ola/base/Macro.h" +#include "ola/thread/Mutex.h" +#include "plugins/usbdmx/Widget.h" + +namespace ola { +namespace plugin { +namespace usbdmx { + +/** + * @brief The interface for the Velleman Widgets + */ +class VellemanWidget: public Widget { + public: + virtual ~VellemanWidget() {} +}; + +/** + * @brief An Velleman widget that uses synchronous libusb operations. + * + * Internally this spawns a new thread to avoid blocking SendDMX() calls. + */ +class SynchronousVellemanWidget: public VellemanWidget { + public: + explicit SynchronousVellemanWidget(libusb_device *usb_device); + + bool Init(); + + bool SendDMX(const DmxBuffer &buffer); + + private: + libusb_device* const m_usb_device; + std::auto_ptr m_sender; + + DISALLOW_COPY_AND_ASSIGN(SynchronousVellemanWidget); +}; + +/** + * @brief An Velleman widget that uses asynchronous libusb operations. + */ +class AsynchronousVellemanWidget : public VellemanWidget { + public: + explicit AsynchronousVellemanWidget(libusb_device *usb_device); + ~AsynchronousVellemanWidget(); + + bool Init(); + + bool SendDMX(const DmxBuffer &buffer); + + void TransferComplete(struct libusb_transfer *transfer); + + private: + enum TransferState { + IDLE, + IN_PROGRESS, + }; + + libusb_device* const m_usb_device; + libusb_device_handle *m_usb_handle; + uint8_t *m_control_setup_buffer; + + TransferState m_transfer_state; + ola::thread::Mutex m_mutex; + + struct libusb_transfer *m_transfer; + + DISALLOW_COPY_AND_ASSIGN(AsynchronousVellemanWidget); +}; +} // namespace usbdmx +} // namespace plugin +} // namespace ola +#endif // PLUGINS_USBDMX_VELLEMANWIDGET_H_ diff --git a/plugins/usbdmx/VellemanWidgetFactory.cpp b/plugins/usbdmx/VellemanWidgetFactory.cpp new file mode 100644 index 0000000000..2de634cb58 --- /dev/null +++ b/plugins/usbdmx/VellemanWidgetFactory.cpp @@ -0,0 +1,49 @@ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * 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 Library 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * VellemanWidgetFactory.cpp + * The WidgetFactory for Velleman widgets. + * Copyright (C) 2014 Simon Newton + */ + +#include "plugins/usbdmx/VellemanWidgetFactory.h" + +#include "ola/Logging.h" +#include "plugins/usbdmx/VellemanWidget.h" +#include "plugins/usbdmx/LibUsbHelper.h" + +namespace ola { +namespace plugin { +namespace usbdmx { + +const uint16_t VellemanWidgetFactory::VENDOR_ID = 0x10cf; +const uint16_t VellemanWidgetFactory::PRODUCT_ID = 0x8062; + +bool VellemanWidgetFactory::DeviceAdded( + WidgetObserver *observer, + libusb_device *usb_device, + const struct libusb_device_descriptor &descriptor) { + if (descriptor.idVendor != VENDOR_ID || descriptor.idProduct != PRODUCT_ID || + HasDevice(usb_device)) { + return false; + } + + OLA_INFO << "Found a new Velleman device"; + return AddWidget(observer, usb_device, + new AsynchronousVellemanWidget(usb_device)); +} +} // namespace usbdmx +} // namespace plugin +} // namespace ola diff --git a/plugins/usbdmx/UsbDevice.h b/plugins/usbdmx/VellemanWidgetFactory.h similarity index 53% rename from plugins/usbdmx/UsbDevice.h rename to plugins/usbdmx/VellemanWidgetFactory.h index 30221149d1..795e834e68 100644 --- a/plugins/usbdmx/UsbDevice.h +++ b/plugins/usbdmx/VellemanWidgetFactory.h @@ -13,47 +13,40 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * - * UsbDevice.h - * Interface for the generic usb device - * Copyright (C) 2010 Simon Newton + * VellemanWidgetFactory.h + * The WidgetFactory for Velleman widgets. + * Copyright (C) 2014 Simon Newton */ -#ifndef PLUGINS_USBDMX_USBDEVICE_H_ -#define PLUGINS_USBDMX_USBDEVICE_H_ +#ifndef PLUGINS_USBDMX_VELLEMANWIDGETFACTORY_H_ +#define PLUGINS_USBDMX_VELLEMANWIDGETFACTORY_H_ -#include -#include #include "ola/base/Macro.h" -#include "olad/Device.h" +#include "plugins/usbdmx/WidgetFactory.h" namespace ola { namespace plugin { namespace usbdmx { -/* - * A Usb device, this is just like the generic Device class but it has a - * Start() method as well to do the USB setup. +/** + * @brief Manages Velleman Devices */ -class UsbDevice: public ola::Device { +class VellemanWidgetFactory : public BaseWidgetFactory { public: - UsbDevice(ola::AbstractPlugin *owner, - const std::string &name, - libusb_device *device): - Device(owner, name), - m_usb_device(device) { - libusb_ref_device(device); - } - virtual ~UsbDevice() { - libusb_unref_device(m_usb_device); - } + VellemanWidgetFactory() {} - protected: - libusb_device *m_usb_device; + bool DeviceAdded( + WidgetObserver *observer, + libusb_device *usb_device, + const struct libusb_device_descriptor &descriptor); private: - DISALLOW_COPY_AND_ASSIGN(UsbDevice); + static const uint16_t VENDOR_ID; + static const uint16_t PRODUCT_ID; + + DISALLOW_COPY_AND_ASSIGN(VellemanWidgetFactory); }; } // namespace usbdmx } // namespace plugin } // namespace ola -#endif // PLUGINS_USBDMX_USBDEVICE_H_ +#endif // PLUGINS_USBDMX_VELLEMANWIDGETFACTORY_H_ diff --git a/plugins/usbdmx/Widget.h b/plugins/usbdmx/Widget.h new file mode 100644 index 0000000000..9d0f62bf11 --- /dev/null +++ b/plugins/usbdmx/Widget.h @@ -0,0 +1,44 @@ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * 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 Library 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Widget.h + * A generic USB Widget. + * Copyright (C) 2014 Simon Newton + */ + +#ifndef PLUGINS_USBDMX_WIDGET_H_ +#define PLUGINS_USBDMX_WIDGET_H_ + +#include "ola/DmxBuffer.h" + +namespace ola { +namespace plugin { +namespace usbdmx { + +/** + * @brief A generic USB Widget. + */ +class Widget { + public: + virtual ~Widget() {} + + virtual bool Init() = 0; + + virtual bool SendDMX(const DmxBuffer &buffer) = 0; +}; +} // namespace usbdmx +} // namespace plugin +} // namespace ola +#endif // PLUGINS_USBDMX_WIDGET_H_ diff --git a/plugins/usbdmx/WidgetFactory.h b/plugins/usbdmx/WidgetFactory.h index 5dfcd69252..70da9aee6f 100644 --- a/plugins/usbdmx/WidgetFactory.h +++ b/plugins/usbdmx/WidgetFactory.h @@ -47,6 +47,7 @@ class WidgetObserver { virtual bool NewWidget(class AnymaWidget *widget) = 0; virtual bool NewWidget(class EuroliteProWidget *widget) = 0; virtual bool NewWidget(class SunliteWidget *widget) = 0; + virtual bool NewWidget(class VellemanWidget *widget) = 0; /** * @brief Called when a generic widget is removed. @@ -59,6 +60,7 @@ class WidgetObserver { virtual void WidgetRemoved(class AnymaWidget *widget) = 0; virtual void WidgetRemoved(class EuroliteProWidget *widget) = 0; virtual void WidgetRemoved(class SunliteWidget *widget) = 0; + virtual void WidgetRemoved(class VellemanWidget *widget) = 0; }; /** @@ -126,11 +128,13 @@ bool BaseWidgetFactory::AddWidget(WidgetObserver *observer, libusb_device *usb_device, WidgetType *widget) { if (!widget->Init()) { + OLA_INFO << "widget init failed"; delete widget; return false; } if (!observer->NewWidget(widget)) { + OLA_INFO << "observer rejected widget"; delete widget; return false; } From 26d399b2fcd90f1ecb31e50820c8c7d4e40bcfdb Mon Sep 17 00:00:00 2001 From: Simon Newton Date: Thu, 13 Nov 2014 19:20:35 -0800 Subject: [PATCH 07/34] Clean up doxygen for the new libusb plugin. --- plugins/usbdmx/AnymaWidget.h | 38 +++++-- plugins/usbdmx/AnymaWidgetFactory.h | 2 +- plugins/usbdmx/AsyncPluginImpl.cpp | 9 -- plugins/usbdmx/AsyncPluginImpl.h | 8 +- plugins/usbdmx/EuroliteProWidget.cpp | 3 +- plugins/usbdmx/EuroliteProWidget.h | 45 ++++++-- plugins/usbdmx/EuroliteProWidgetFactory.h | 2 +- plugins/usbdmx/SunliteWidget.h | 24 +++-- plugins/usbdmx/SunliteWidgetFactory.h | 2 +- plugins/usbdmx/VellemanWidget.h | 20 +++- plugins/usbdmx/VellemanWidgetFactory.h | 2 +- plugins/usbdmx/Widget.h | 11 +- plugins/usbdmx/WidgetFactory.h | 123 +++++++++++++++++----- 13 files changed, 219 insertions(+), 70 deletions(-) diff --git a/plugins/usbdmx/AnymaWidget.h b/plugins/usbdmx/AnymaWidget.h index 0bfadae5ca..f30210ddfa 100644 --- a/plugins/usbdmx/AnymaWidget.h +++ b/plugins/usbdmx/AnymaWidget.h @@ -34,23 +34,34 @@ namespace plugin { namespace usbdmx { /** - * @brief The interface for the Anyma Widgets + * @brief The base class for Anyma Widgets. */ class AnymaWidget: public Widget { public: + /** + * @brief Create a new AnymaWidget. + * @param serial the serial number of the widget. + */ explicit AnymaWidget(const std::string &serial) : m_serial(serial) {} virtual ~AnymaWidget() {} - virtual bool Init() = 0; - - virtual bool SendDMX(const DmxBuffer &buffer) = 0; - + /** + * @brief Get the serial number of this widget. + * @returns The serial number of the widget. + */ std::string SerialNumber() const { return m_serial; } + /** + * @brief The expected manufacturer string for an Anyma widget. + */ static const char EXPECTED_MANUFACTURER[]; + + /** + * @brief The expected product string for an Anyma widget. + */ static const char EXPECTED_PRODUCT[]; private: @@ -64,8 +75,13 @@ class AnymaWidget: public Widget { */ class SynchronousAnymaWidget: public AnymaWidget { public: + /** + * @brief Create a new SynchronousAnymaWidget. + * @param usb_device the libusb_device to use for the widget. + * @param serial the serial number of the widget. + */ SynchronousAnymaWidget(libusb_device *usb_device, - const std::string &serial); + const std::string &erial); bool Init(); @@ -83,6 +99,11 @@ class SynchronousAnymaWidget: public AnymaWidget { */ class AsynchronousAnymaWidget : public AnymaWidget { public: + /** + * @brief Create a new AsynchronousAnymaWidget. + * @param usb_device the libusb_device to use for the widget. + * @param serial the serial number of the widget. + */ AsynchronousAnymaWidget(libusb_device *usb_device, const std::string &serial); ~AsynchronousAnymaWidget(); @@ -91,6 +112,11 @@ class AsynchronousAnymaWidget : public AnymaWidget { bool SendDMX(const DmxBuffer &buffer); + /** + * @brief Called from the libusb callback when the asynchronous transfer + * completes. + * @param transfer the completed transfer. + */ void TransferComplete(struct libusb_transfer *transfer); private: diff --git a/plugins/usbdmx/AnymaWidgetFactory.h b/plugins/usbdmx/AnymaWidgetFactory.h index a5e4ed479d..a0c81464e6 100644 --- a/plugins/usbdmx/AnymaWidgetFactory.h +++ b/plugins/usbdmx/AnymaWidgetFactory.h @@ -29,7 +29,7 @@ namespace plugin { namespace usbdmx { /** - * @brief Manages Anyma Devices + * @brief Creates Anyma widgets. */ class AnymaWidgetFactory : public BaseWidgetFactory { public: diff --git a/plugins/usbdmx/AsyncPluginImpl.cpp b/plugins/usbdmx/AsyncPluginImpl.cpp index 7c65909363..1a5fb7484e 100644 --- a/plugins/usbdmx/AsyncPluginImpl.cpp +++ b/plugins/usbdmx/AsyncPluginImpl.cpp @@ -222,11 +222,6 @@ void AsyncPluginImpl::HotPlugEvent(struct libusb_device *usb_device, } #endif -bool AsyncPluginImpl::NewWidget(class Widget *widget) { - (void) widget; - return false; -} - bool AsyncPluginImpl::NewWidget(class AnymaWidget *widget) { return StartAndRegisterDevice( widget, @@ -253,10 +248,6 @@ bool AsyncPluginImpl::NewWidget(class VellemanWidget *widget) { new GenericDevice(m_plugin, widget, "Velleman USB Device", "velleman")); } -void AsyncPluginImpl::WidgetRemoved(class Widget *widget) { - (void) widget; -} - void AsyncPluginImpl::WidgetRemoved(class AnymaWidget *widget) { Device *device = STLLookupAndRemovePtr(&m_widget_device_map, widget); if (device) { diff --git a/plugins/usbdmx/AsyncPluginImpl.h b/plugins/usbdmx/AsyncPluginImpl.h index 3aeb461152..6dd18ef249 100644 --- a/plugins/usbdmx/AsyncPluginImpl.h +++ b/plugins/usbdmx/AsyncPluginImpl.h @@ -54,8 +54,6 @@ class AsyncPluginImpl: public PluginImplInterface, public WidgetObserver { * @brief Create a new AsyncPluginImpl. * @param plugin_adaptor The PluginAdaptor to use, ownership is not * transferred. - * @param preferences The Preference object to use, ownership is not - * transferred. * @param plugin The parent Plugin object which is used when creating * devices. * @param libusb_adaptor The adaptor to use when calling libusb. @@ -83,13 +81,11 @@ class AsyncPluginImpl: public PluginImplInterface, public WidgetObserver { libusb_hotplug_event event); #endif - bool NewWidget(class Widget *widget); bool NewWidget(class AnymaWidget *widget); bool NewWidget(class EuroliteProWidget *widget); bool NewWidget(class SunliteWidget *widget); bool NewWidget(class VellemanWidget *widget); - void WidgetRemoved(class Widget *widget); void WidgetRemoved(class AnymaWidget *widget); void WidgetRemoved(class EuroliteProWidget *widget); void WidgetRemoved(class SunliteWidget *widget); @@ -98,7 +94,7 @@ class AsyncPluginImpl: public PluginImplInterface, public WidgetObserver { private: typedef std::vector WidgetFactories; typedef std::map USBDeviceToFactoryMap; - typedef std::map WidgetToDeviceMap; + typedef std::map WidgetToDeviceMap; struct USBDeviceInformation { std::string manufacturer; @@ -126,7 +122,7 @@ class AsyncPluginImpl: public PluginImplInterface, public WidgetObserver { void DeviceAdded(libusb_device *device); void DeviceRemoved(libusb_device *device); - bool StartAndRegisterDevice(Widget *, Device *device); + bool StartAndRegisterDevice(class Widget *widget, Device *device); void FindUSBDevices(); diff --git a/plugins/usbdmx/EuroliteProWidget.cpp b/plugins/usbdmx/EuroliteProWidget.cpp index de1fb3ac42..2d567b72c4 100644 --- a/plugins/usbdmx/EuroliteProWidget.cpp +++ b/plugins/usbdmx/EuroliteProWidget.cpp @@ -31,8 +31,7 @@ namespace plugin { namespace usbdmx { const char EuroliteProWidget::EXPECTED_MANUFACTURER[] = "Eurolite"; -const char EuroliteProWidget::EXPECTED_PRODUCT[] = - "Eurolite DMX512 Pro"; +const char EuroliteProWidget::EXPECTED_PRODUCT[] = "Eurolite DMX512 Pro"; using std::string; diff --git a/plugins/usbdmx/EuroliteProWidget.h b/plugins/usbdmx/EuroliteProWidget.h index d8ebb74ea3..1a04484dea 100644 --- a/plugins/usbdmx/EuroliteProWidget.h +++ b/plugins/usbdmx/EuroliteProWidget.h @@ -39,21 +39,35 @@ class EuroliteProThreadedSender; */ class EuroliteProWidget : public Widget { public: + /** + * @brief Create a new EuroliteProWidget. + * @param serial the serial number of the widget. + */ explicit EuroliteProWidget(const std::string &serial) : m_serial(serial) {} - virtual ~EuroliteProWidget() {} - - virtual bool Init() = 0; - - virtual bool SendDMX(const DmxBuffer &buffer) = 0; + /** + * @brief Get the serial number of this widget. + * @returns The serial number of the widget. + */ std::string SerialNumber() const { return m_serial; } + /** + * @brief The expected manufacturer string for a EurolitePro widget. + */ static const char EXPECTED_MANUFACTURER[]; + + /** + * @brief The expected product string for a EurolitePro widget. + */ static const char EXPECTED_PRODUCT[]; - // 513 + header + code + size(2) + footer + /** + * @brief The size of a EurolitePro frame. + * + * This consists of 513 bytes of DMX data + header + code + size(2) + footer + */ enum { EUROLITE_PRO_FRAME_SIZE = 518 }; private: @@ -68,6 +82,11 @@ class EuroliteProWidget : public Widget { */ class SynchronousEuroliteProWidget: public EuroliteProWidget { public: + /** + * @brief Create a new SynchronousEuroliteProWidget. + * @param usb_device the libusb_device to use for the widget. + * @param serial the serial number of the widget. + */ SynchronousEuroliteProWidget(libusb_device *usb_device, const std::string &serial); @@ -87,14 +106,24 @@ class SynchronousEuroliteProWidget: public EuroliteProWidget { */ class AsynchronousEuroliteProWidget: public EuroliteProWidget { public: - explicit AsynchronousEuroliteProWidget(libusb_device *usb_device, - const std::string &serial); + /** + * @brief Create a new AsynchronousEuroliteProWidget. + * @param usb_device the libusb_device to use for the widget. + * @param serial the serial number of the widget. + */ + AsynchronousEuroliteProWidget(libusb_device *usb_device, + const std::string &serial); ~AsynchronousEuroliteProWidget(); bool Init(); bool SendDMX(const DmxBuffer &buffer); + /** + * @brief Called from the libusb callback when the asynchronous transfer + * completes. + * @param transfer the completed transfer. + */ void TransferComplete(struct libusb_transfer *transfer); private: diff --git a/plugins/usbdmx/EuroliteProWidgetFactory.h b/plugins/usbdmx/EuroliteProWidgetFactory.h index 38a265de33..836173a2d0 100644 --- a/plugins/usbdmx/EuroliteProWidgetFactory.h +++ b/plugins/usbdmx/EuroliteProWidgetFactory.h @@ -30,7 +30,7 @@ namespace plugin { namespace usbdmx { /** - * @brief Manages EurolitePro Devices + * @brief Creates EurolitePro widgets. */ class EuroliteProWidgetFactory : public BaseWidgetFactory { diff --git a/plugins/usbdmx/SunliteWidget.h b/plugins/usbdmx/SunliteWidget.h index af2b369d7e..ec940b48f2 100644 --- a/plugins/usbdmx/SunliteWidget.h +++ b/plugins/usbdmx/SunliteWidget.h @@ -38,12 +38,9 @@ class SunliteThreadedSender; */ class SunliteWidget : public Widget { public: - virtual ~SunliteWidget() {} - - virtual bool Init() = 0; - - virtual bool SendDMX(const DmxBuffer &buffer) = 0; - + /** + * @brief The size of a Sunlite frame. + */ enum {SUNLITE_PACKET_SIZE = 0x340}; }; @@ -55,6 +52,11 @@ class SunliteWidget : public Widget { */ class SynchronousSunliteWidget: public SunliteWidget { public: + /** + * @brief Create a new SynchronousSunliteWidget. + * @param usb_device the libusb_device to use for the widget. + * @param serial the serial number of the widget. + */ explicit SynchronousSunliteWidget(libusb_device *usb_device); bool Init(); @@ -73,6 +75,11 @@ class SynchronousSunliteWidget: public SunliteWidget { */ class AsynchronousSunliteWidget: public SunliteWidget { public: + /** + * @brief Create a new AsynchronousSunliteWidget. + * @param usb_device the libusb_device to use for the widget. + * @param serial the serial number of the widget. + */ explicit AsynchronousSunliteWidget(libusb_device *usb_device); ~AsynchronousSunliteWidget(); @@ -80,6 +87,11 @@ class AsynchronousSunliteWidget: public SunliteWidget { bool SendDMX(const DmxBuffer &buffer); + /** + * @brief Called from the libusb callback when the asynchronous transfer + * completes. + * @param transfer the completed transfer. + */ void TransferComplete(struct libusb_transfer *transfer); private: diff --git a/plugins/usbdmx/SunliteWidgetFactory.h b/plugins/usbdmx/SunliteWidgetFactory.h index 76ccc39b5b..73e2d33bc3 100644 --- a/plugins/usbdmx/SunliteWidgetFactory.h +++ b/plugins/usbdmx/SunliteWidgetFactory.h @@ -30,7 +30,7 @@ namespace plugin { namespace usbdmx { /** - * @brief Manages SunLite Devices + * @brief Creates SunLite widgets. */ class SunliteWidgetFactory : public BaseWidgetFactory { public: diff --git a/plugins/usbdmx/VellemanWidget.h b/plugins/usbdmx/VellemanWidget.h index 155e5456d1..9e3b5c8545 100644 --- a/plugins/usbdmx/VellemanWidget.h +++ b/plugins/usbdmx/VellemanWidget.h @@ -36,10 +36,7 @@ namespace usbdmx { /** * @brief The interface for the Velleman Widgets */ -class VellemanWidget: public Widget { - public: - virtual ~VellemanWidget() {} -}; +class VellemanWidget: public Widget {}; /** * @brief An Velleman widget that uses synchronous libusb operations. @@ -48,6 +45,11 @@ class VellemanWidget: public Widget { */ class SynchronousVellemanWidget: public VellemanWidget { public: + /** + * @brief Create a new SynchronousVellemanWidget. + * @param usb_device the libusb_device to use for the widget. + * @param serial the serial number of the widget. + */ explicit SynchronousVellemanWidget(libusb_device *usb_device); bool Init(); @@ -66,6 +68,11 @@ class SynchronousVellemanWidget: public VellemanWidget { */ class AsynchronousVellemanWidget : public VellemanWidget { public: + /** + * @brief Create a new AsynchronousVellemanWidget. + * @param usb_device the libusb_device to use for the widget. + * @param serial the serial number of the widget. + */ explicit AsynchronousVellemanWidget(libusb_device *usb_device); ~AsynchronousVellemanWidget(); @@ -73,6 +80,11 @@ class AsynchronousVellemanWidget : public VellemanWidget { bool SendDMX(const DmxBuffer &buffer); + /** + * @brief Called from the libusb callback when the asynchronous transfer + * completes. + * @param transfer the completed transfer. + */ void TransferComplete(struct libusb_transfer *transfer); private: diff --git a/plugins/usbdmx/VellemanWidgetFactory.h b/plugins/usbdmx/VellemanWidgetFactory.h index 795e834e68..c6b40772c6 100644 --- a/plugins/usbdmx/VellemanWidgetFactory.h +++ b/plugins/usbdmx/VellemanWidgetFactory.h @@ -29,7 +29,7 @@ namespace plugin { namespace usbdmx { /** - * @brief Manages Velleman Devices + * @brief Creates Velleman widgets. */ class VellemanWidgetFactory : public BaseWidgetFactory { public: diff --git a/plugins/usbdmx/Widget.h b/plugins/usbdmx/Widget.h index 9d0f62bf11..ab30f9b32c 100644 --- a/plugins/usbdmx/Widget.h +++ b/plugins/usbdmx/Widget.h @@ -28,14 +28,23 @@ namespace plugin { namespace usbdmx { /** - * @brief A generic USB Widget. + * @brief The interface for an generic USB Widget. */ class Widget { public: virtual ~Widget() {} + /** + * @brief Initialize the widget. + * @returns true if this widget was initialized correctly, false otherwise. + */ virtual bool Init() = 0; + /** + * @brief Send DMX data from this widget + * @param buffer The DmxBuffer containing the data to send. + * @returns true if the data was sent, false otherwise. + */ virtual bool SendDMX(const DmxBuffer &buffer) = 0; }; } // namespace usbdmx diff --git a/plugins/usbdmx/WidgetFactory.h b/plugins/usbdmx/WidgetFactory.h index 70da9aee6f..67aa8c018c 100644 --- a/plugins/usbdmx/WidgetFactory.h +++ b/plugins/usbdmx/WidgetFactory.h @@ -31,73 +31,136 @@ namespace plugin { namespace usbdmx { /** - * @brief Receives notifications when new widgets are detected. + * @brief Receives notifications when Widgets are added or removed. + * + * Classes implementing the WidgetObserver can be used with WidgetFactories to + * receive notifcations when widgets are added or removed. + * + * On adding a new Widget, the appropriate NewWidget() method is called. The + * observer can mark a widget as in-use by returning true. + * + * When widgets are removed, the appropriate WidgetRemoved() method is called. + * The observer must not access the removed widget object once the call to + * WidgetRemoved() completes. */ class WidgetObserver { public: virtual ~WidgetObserver() {} /** - * @brief Called when a new generic widget is found. - * @param widget the new Widget. - * @returns true if the widget is now in use, false if the widget was + * @brief Called when a new AnymaWidget is added. + * @param widget the new Widget, ownership is not transferred but the object + * may be used until the corresponding WidgetRemoved() call is made. + * @returns true if the widget has been claimed, false if the widget was * ignored. */ - virtual bool NewWidget(class Widget *widget) = 0; virtual bool NewWidget(class AnymaWidget *widget) = 0; + + /** + * @brief Called when a new EuroliteProWidget is added. + * @param widget the new Widget, ownership is not transferred but the object + * may be used until the corresponding WidgetRemoved() call is made. + * @returns true if the widget has been claimed, false if the widget was + * ignored. + */ virtual bool NewWidget(class EuroliteProWidget *widget) = 0; + + /** + * @brief Called when a new SunliteWidget is added. + * @param widget the new Widget, ownership is not transferred but the object + * may be used until the corresponding WidgetRemoved() call is made. + * @returns true if the widget has been claimed, false if the widget was + * ignored. + */ virtual bool NewWidget(class SunliteWidget *widget) = 0; + + /** + * @brief Called when a new VellemanWidget is added. + * @param widget the new Widget, ownership is not transferred but the object + * may be used until the corresponding WidgetRemoved() call is made. + * @returns true if the widget has been claimed, false if the widget was + * ignored. + */ virtual bool NewWidget(class VellemanWidget *widget) = 0; /** - * @brief Called when a generic widget is removed. + * @brief Called when an AnymaWidget is removed. * @param widget the Widget that has been removed. * * It is an error to use the widget once this call completes. */ - virtual void WidgetRemoved(class Widget *widget) = 0; - virtual void WidgetRemoved(class AnymaWidget *widget) = 0; + + /** + * @brief Called when a EuroliteProWidget is removed. + * @param widget the Widget that has been removed. + * + * It is an error to use the widget once this call completes. + */ virtual void WidgetRemoved(class EuroliteProWidget *widget) = 0; + + /** + * @brief Called when a SunliteWidget is removed. + * @param widget the Widget that has been removed. + * + * It is an error to use the widget once this call completes. + */ virtual void WidgetRemoved(class SunliteWidget *widget) = 0; + + /** + * @brief Called when a VellemanWidget is removed. + * @param widget the Widget that has been removed. + * + * It is an error to use the widget once this call completes. + */ virtual void WidgetRemoved(class VellemanWidget *widget) = 0; }; /** - * @brief + * @brief Creates new Widget objects to represent DMX USB hardware. + * + * WidgetFactories are called when new USB devices are located. By inspecting + * the device's vendor and product ID, they may choose to create a new Widget + * object. The WidgetFactory then calls the WidgetObserver object to indicate a + * new Widget has been added. + * + * When a USB device is removed, the factory that created a Widget from the + * device has it's DeviceRemoved() method called. The factory should then + * invoke WidgetRemoved on the observer object. */ class WidgetFactory { public: virtual ~WidgetFactory() {} /** - * @brief + * @brief Called when a new USB device is added. * @param observer The WidgetObserver to notify if this results in a new - * widget. - * @param device the libusb_device that was added. + * widget. + * @param usb_device the libusb_device that was added. * @param descriptor the libusb_device_descriptor that corresponds to the - * libusb_device. + * usb_device. * @returns True if this factory has claimed the usb_device, false otherwise. */ virtual bool DeviceAdded( WidgetObserver *observer, - libusb_device *device, + libusb_device *usb_device, const struct libusb_device_descriptor &descriptor) = 0; /** - * @brief - * @param observer The WidgetObserver to notify if this results in a widget - * removal. + * @brief Called when a USB device is removed. + * @param observer The WidgetObserver to notify if this action results in a + * widget removal. + * @param usb_device the libusb_device that was removed. */ virtual void DeviceRemoved(WidgetObserver *observer, - libusb_device *device) = 0; + libusb_device *usb_device) = 0; }; /** * @brief A partial implementation of WidgetFactory. * * This handles the mapping of libusb_devices to widgets and notifying the - * observer. + * observer when widgets are added or removed. */ template class BaseWidgetFactory : public WidgetFactory { @@ -108,10 +171,23 @@ class BaseWidgetFactory : public WidgetFactory { libusb_device *device); protected: - bool HasDevice(libusb_device *device) { - return STLContains(m_widget_map, device); + /** + * @brief Check if this factory is already using this device. + * @param usb_device The libusb_device to check for. + * @returns true if we already have a widget registered for this device, + * false otherwise. + */ + bool HasDevice(libusb_device *usb_device) { + return STLContains(m_widget_map, usb_device); } + /** + * @brief Initialize a widget and notify the observer. + * @param observer The WidgetObserver to notify of the new widget. + * @param usb_device the libusb_device associated with the widget. + * @param widget the new Widget, ownership is transferred. + * @returns True if the widget was added, false otherwise. + */ bool AddWidget(WidgetObserver *observer, libusb_device *usb_device, WidgetType *widget); @@ -150,9 +226,8 @@ bool BaseWidgetFactory::AddWidget(WidgetObserver *observer, } template -void BaseWidgetFactory::DeviceRemoved( - WidgetObserver *observer, - libusb_device *usb_device) { +void BaseWidgetFactory::DeviceRemoved(WidgetObserver *observer, + libusb_device *usb_device) { OLA_INFO << "Removing device " << usb_device; WidgetType *widget = STLLookupAndRemovePtr(&m_widget_map, usb_device); if (widget) { From 7b7555933375dd0432f3c1adce37684d792c78a8 Mon Sep 17 00:00:00 2001 From: Simon Newton Date: Thu, 13 Nov 2014 19:33:14 -0800 Subject: [PATCH 08/34] Fix the build. --- plugins/usbdmx/AnymaWidget.h | 2 +- plugins/usbdmx/GenericDevice.cpp | 1 + plugins/usbdmx/Makefile.mk | 1 + plugins/usbdmx/SunliteWidget.h | 2 -- plugins/usbdmx/VellemanWidget.h | 2 -- 5 files changed, 3 insertions(+), 5 deletions(-) diff --git a/plugins/usbdmx/AnymaWidget.h b/plugins/usbdmx/AnymaWidget.h index f30210ddfa..061298a184 100644 --- a/plugins/usbdmx/AnymaWidget.h +++ b/plugins/usbdmx/AnymaWidget.h @@ -81,7 +81,7 @@ class SynchronousAnymaWidget: public AnymaWidget { * @param serial the serial number of the widget. */ SynchronousAnymaWidget(libusb_device *usb_device, - const std::string &erial); + const std::string &serial); bool Init(); diff --git a/plugins/usbdmx/GenericDevice.cpp b/plugins/usbdmx/GenericDevice.cpp index f32ed29e93..3616314111 100644 --- a/plugins/usbdmx/GenericDevice.cpp +++ b/plugins/usbdmx/GenericDevice.cpp @@ -20,6 +20,7 @@ #include "plugins/usbdmx/GenericDevice.h" +#include #include "plugins/usbdmx/Widget.h" #include "plugins/usbdmx/GenericOutputPort.h" diff --git a/plugins/usbdmx/Makefile.mk b/plugins/usbdmx/Makefile.mk index 78ec40ab56..d39e3ff525 100644 --- a/plugins/usbdmx/Makefile.mk +++ b/plugins/usbdmx/Makefile.mk @@ -40,6 +40,7 @@ plugins_usbdmx_libolausbdmx_la_SOURCES = \ plugins/usbdmx/VellemanWidget.h \ plugins/usbdmx/VellemanWidgetFactory.cpp \ plugins/usbdmx/VellemanWidgetFactory.h \ + plugins/usbdmx/Widget.h \ plugins/usbdmx/WidgetFactory.h plugins_usbdmx_libolausbdmx_la_CXXFLAGS = $(COMMON_CXXFLAGS) $(libusb_CFLAGS) diff --git a/plugins/usbdmx/SunliteWidget.h b/plugins/usbdmx/SunliteWidget.h index ec940b48f2..7c0d41a775 100644 --- a/plugins/usbdmx/SunliteWidget.h +++ b/plugins/usbdmx/SunliteWidget.h @@ -55,7 +55,6 @@ class SynchronousSunliteWidget: public SunliteWidget { /** * @brief Create a new SynchronousSunliteWidget. * @param usb_device the libusb_device to use for the widget. - * @param serial the serial number of the widget. */ explicit SynchronousSunliteWidget(libusb_device *usb_device); @@ -78,7 +77,6 @@ class AsynchronousSunliteWidget: public SunliteWidget { /** * @brief Create a new AsynchronousSunliteWidget. * @param usb_device the libusb_device to use for the widget. - * @param serial the serial number of the widget. */ explicit AsynchronousSunliteWidget(libusb_device *usb_device); ~AsynchronousSunliteWidget(); diff --git a/plugins/usbdmx/VellemanWidget.h b/plugins/usbdmx/VellemanWidget.h index 9e3b5c8545..31ea7ebc53 100644 --- a/plugins/usbdmx/VellemanWidget.h +++ b/plugins/usbdmx/VellemanWidget.h @@ -48,7 +48,6 @@ class SynchronousVellemanWidget: public VellemanWidget { /** * @brief Create a new SynchronousVellemanWidget. * @param usb_device the libusb_device to use for the widget. - * @param serial the serial number of the widget. */ explicit SynchronousVellemanWidget(libusb_device *usb_device); @@ -71,7 +70,6 @@ class AsynchronousVellemanWidget : public VellemanWidget { /** * @brief Create a new AsynchronousVellemanWidget. * @param usb_device the libusb_device to use for the widget. - * @param serial the serial number of the widget. */ explicit AsynchronousVellemanWidget(libusb_device *usb_device); ~AsynchronousVellemanWidget(); From 6f9d518c07f35c1ba57b73b2423781ccf5267f30 Mon Sep 17 00:00:00 2001 From: Simon Newton Date: Thu, 13 Nov 2014 22:14:16 -0800 Subject: [PATCH 09/34] Another attempt at fixing the build. --- plugins/usbdmx/AnymaWidget.h | 1 + plugins/usbdmx/AsyncPluginImpl.cpp | 22 +++++++++++++--------- plugins/usbdmx/AsyncPluginImpl.h | 1 + plugins/usbdmx/EuroliteProWidget.cpp | 1 + plugins/usbdmx/EuroliteProWidget.h | 5 +++-- plugins/usbdmx/SunliteWidget.cpp | 1 + plugins/usbdmx/SunliteWidget.h | 3 ++- plugins/usbdmx/VellemanWidget.h | 1 + 8 files changed, 23 insertions(+), 12 deletions(-) diff --git a/plugins/usbdmx/AnymaWidget.h b/plugins/usbdmx/AnymaWidget.h index 061298a184..92e2e519f0 100644 --- a/plugins/usbdmx/AnymaWidget.h +++ b/plugins/usbdmx/AnymaWidget.h @@ -22,6 +22,7 @@ #define PLUGINS_USBDMX_ANYMAWIDGET_H_ #include +#include #include #include "ola/DmxBuffer.h" diff --git a/plugins/usbdmx/AsyncPluginImpl.cpp b/plugins/usbdmx/AsyncPluginImpl.cpp index 1a5fb7484e..0ce417ceac 100644 --- a/plugins/usbdmx/AsyncPluginImpl.cpp +++ b/plugins/usbdmx/AsyncPluginImpl.cpp @@ -249,24 +249,19 @@ bool AsyncPluginImpl::NewWidget(class VellemanWidget *widget) { } void AsyncPluginImpl::WidgetRemoved(class AnymaWidget *widget) { - Device *device = STLLookupAndRemovePtr(&m_widget_device_map, widget); - if (device) { - m_plugin_adaptor->UnregisterDevice(device); - device->Stop(); - delete device; - } + RemoveWidget(widget); } void AsyncPluginImpl::WidgetRemoved(class EuroliteProWidget *widget) { - (void) widget; + RemoveWidget(widget); } void AsyncPluginImpl::WidgetRemoved(class SunliteWidget *widget) { - (void) widget; + RemoveWidget(widget); } void AsyncPluginImpl::WidgetRemoved(class VellemanWidget *widget) { - (void) widget; + RemoveWidget(widget); } bool AsyncPluginImpl::SetupHotPlug() { @@ -344,6 +339,15 @@ bool AsyncPluginImpl::StartAndRegisterDevice(Widget *widget, Device *device) { m_plugin_adaptor->RegisterDevice(device); return true; } + +void AsyncPluginImpl::RemoveWidget(class Widget *widget) { + Device *device = STLLookupAndRemovePtr(&m_widget_device_map, widget); + if (device) { + m_plugin_adaptor->UnregisterDevice(device); + device->Stop(); + delete device; + } +} } // namespace usbdmx } // namespace plugin } // namespace ola diff --git a/plugins/usbdmx/AsyncPluginImpl.h b/plugins/usbdmx/AsyncPluginImpl.h index 6dd18ef249..ec066b71d3 100644 --- a/plugins/usbdmx/AsyncPluginImpl.h +++ b/plugins/usbdmx/AsyncPluginImpl.h @@ -123,6 +123,7 @@ class AsyncPluginImpl: public PluginImplInterface, public WidgetObserver { void DeviceRemoved(libusb_device *device); bool StartAndRegisterDevice(class Widget *widget, Device *device); + void RemoveWidget(class Widget *widget); void FindUSBDevices(); diff --git a/plugins/usbdmx/EuroliteProWidget.cpp b/plugins/usbdmx/EuroliteProWidget.cpp index 2d567b72c4..c27df718bc 100644 --- a/plugins/usbdmx/EuroliteProWidget.cpp +++ b/plugins/usbdmx/EuroliteProWidget.cpp @@ -25,6 +25,7 @@ #include "ola/Constants.h" #include "ola/Logging.h" #include "plugins/usbdmx/LibUsbHelper.h" +#include "plugins/usbdmx/ThreadedUsbSender.h" namespace ola { namespace plugin { diff --git a/plugins/usbdmx/EuroliteProWidget.h b/plugins/usbdmx/EuroliteProWidget.h index 1a04484dea..cfa175a7c2 100644 --- a/plugins/usbdmx/EuroliteProWidget.h +++ b/plugins/usbdmx/EuroliteProWidget.h @@ -22,10 +22,11 @@ #define PLUGINS_USBDMX_EUROLITEPROWIDGET_H_ #include +#include #include -#include "ola/base/Macro.h" #include "ola/DmxBuffer.h" -#include "plugins/usbdmx/ThreadedUsbSender.h" +#include "ola/base/Macro.h" +#include "ola/thread/Mutex.h" #include "plugins/usbdmx/Widget.h" namespace ola { diff --git a/plugins/usbdmx/SunliteWidget.cpp b/plugins/usbdmx/SunliteWidget.cpp index 3dfb4e498f..507805e928 100644 --- a/plugins/usbdmx/SunliteWidget.cpp +++ b/plugins/usbdmx/SunliteWidget.cpp @@ -23,6 +23,7 @@ #include "ola/Constants.h" #include "ola/Logging.h" #include "plugins/usbdmx/LibUsbHelper.h" +#include "plugins/usbdmx/ThreadedUsbSender.h" namespace ola { namespace plugin { diff --git a/plugins/usbdmx/SunliteWidget.h b/plugins/usbdmx/SunliteWidget.h index 7c0d41a775..ec72742a95 100644 --- a/plugins/usbdmx/SunliteWidget.h +++ b/plugins/usbdmx/SunliteWidget.h @@ -22,9 +22,10 @@ #define PLUGINS_USBDMX_SUNLITEWIDGET_H_ #include +#include #include "ola/DmxBuffer.h" #include "ola/base/Macro.h" -#include "plugins/usbdmx/ThreadedUsbSender.h" +#include "ola/thread/Mutex.h" #include "plugins/usbdmx/Widget.h" namespace ola { diff --git a/plugins/usbdmx/VellemanWidget.h b/plugins/usbdmx/VellemanWidget.h index 31ea7ebc53..ba1d880f5e 100644 --- a/plugins/usbdmx/VellemanWidget.h +++ b/plugins/usbdmx/VellemanWidget.h @@ -22,6 +22,7 @@ #define PLUGINS_USBDMX_VELLEMANWIDGET_H_ #include +#include #include #include "ola/DmxBuffer.h" From 929305653632f5835809d791599d9ed1b9891177 Mon Sep 17 00:00:00 2001 From: Simon Newton Date: Thu, 13 Nov 2014 22:32:27 -0800 Subject: [PATCH 10/34] Yet another attempt --- plugins/usbdmx/AsyncPluginImpl.cpp | 2 ++ plugins/usbdmx/AsyncPluginImpl.h | 1 + 2 files changed, 3 insertions(+) diff --git a/plugins/usbdmx/AsyncPluginImpl.cpp b/plugins/usbdmx/AsyncPluginImpl.cpp index 0ce417ceac..3bd648e8e8 100644 --- a/plugins/usbdmx/AsyncPluginImpl.cpp +++ b/plugins/usbdmx/AsyncPluginImpl.cpp @@ -46,6 +46,7 @@ namespace { /* * Called by libusb when a USB device is added / removed. */ +#ifdef OLA_LIBUSB_HAS_HOTPLUG_API int hotplug_callback(OLA_UNUSED struct libusb_context *ctx, struct libusb_device *dev, libusb_hotplug_event event, @@ -54,6 +55,7 @@ int hotplug_callback(OLA_UNUSED struct libusb_context *ctx, plugin->HotPlugEvent(dev, event); return 0; } +#endif } // namespace class LibUsbThread : private ola::thread::Thread { diff --git a/plugins/usbdmx/AsyncPluginImpl.h b/plugins/usbdmx/AsyncPluginImpl.h index ec066b71d3..7716ed4349 100644 --- a/plugins/usbdmx/AsyncPluginImpl.h +++ b/plugins/usbdmx/AsyncPluginImpl.h @@ -23,6 +23,7 @@ #include #include +#include #include #include #include From 8b1d26d00883cdcda517032cd50d8caacc70275f Mon Sep 17 00:00:00 2001 From: Simon Newton Date: Fri, 14 Nov 2014 07:43:37 -0800 Subject: [PATCH 11/34] Clean up some of the documentation. --- doxygen/namespaces.dox | 8 ++++---- include/olad/Device.h | 10 +++++++++- plugins/usbdmx/GenericDevice.h | 9 +++++++++ plugins/usbdmx/PluginImplInterface.h | 7 +++++-- plugins/usbdmx/ThreadedUsbSender.h | 29 ++++++++++++++++++++++++++++ 5 files changed, 56 insertions(+), 7 deletions(-) diff --git a/doxygen/namespaces.dox b/doxygen/namespaces.dox index 8b2061830d..95bdd0d761 100644 --- a/doxygen/namespaces.dox +++ b/doxygen/namespaces.dox @@ -33,16 +33,16 @@ * @brief Code for supported devices and protocols. * * @namespace ola::plugin::artnet - * @brief Code for the ArtNet protocol. + * @brief The ArtNet plugin. * * @namespace ola::plugin::dmx4linux * @brief Code for DMX4Linux devices. * * @namespace ola::plugin::dummy - * @brief Code for the dummy device. + * @brief The Dummy plugin. * * @namespace ola::plugin::e131 - * @brief Code for the E1.31 (sACN) protocol. + * @brief The E1.31 (sACN) plugin. * * @namespace ola::plugin::espnet * @brief Code for the ESPNet protocol. @@ -87,7 +87,7 @@ * @brief Code for UART DMX devices. * * @namespace ola::plugin::usbdmx - * @brief Code for USB DMX devices. + * @brief The plugin for hardware using libusb. * * @namespace ola::plugin::usbpro * @brief Code for Enttec USB Pro devices and others using the same protocol. diff --git a/include/olad/Device.h b/include/olad/Device.h index b063a94308..9edc5b7695 100644 --- a/include/olad/Device.h +++ b/include/olad/Device.h @@ -97,7 +97,10 @@ class Device: public AbstractDevice { AbstractPlugin *Owner() const { return m_owner; } std::string UniqueId() const; - // Returns an id which is unique within the plugin + /** + * @brief The device ID + * @returns an id which is unique within the plugin, + */ virtual std::string DeviceId() const = 0; bool IsEnabled() const { return m_enabled; } @@ -127,6 +130,11 @@ class Device: public AbstractDevice { ConfigureCallback *done); protected: + /** + * @brief Called during Start(). + * + * This allows the subclass to perform device initialization. + */ virtual bool StartHook() { return true; } virtual void PrePortStop() {} virtual void PostPortStop() {} diff --git a/plugins/usbdmx/GenericDevice.h b/plugins/usbdmx/GenericDevice.h index 96dcc7d4c4..7728d3de6f 100644 --- a/plugins/usbdmx/GenericDevice.h +++ b/plugins/usbdmx/GenericDevice.h @@ -32,9 +32,18 @@ namespace usbdmx { /** * @brief An Generic device. + * + * This simple generic device creates a single output port around a Widget. */ class GenericDevice: public Device { public: + /** + * @brief Create a new GenericDevice. + * @param owner The plugin this device belongs to + * @param widget The widget to use for this device. + * @param device_name The name of the device. + * @param device_id The id of the device. + */ GenericDevice(ola::AbstractPlugin *owner, class Widget *widget, const std::string &device_name, diff --git a/plugins/usbdmx/PluginImplInterface.h b/plugins/usbdmx/PluginImplInterface.h index 1564762784..c66aaab52d 100644 --- a/plugins/usbdmx/PluginImplInterface.h +++ b/plugins/usbdmx/PluginImplInterface.h @@ -34,19 +34,22 @@ namespace ola { namespace plugin { namespace usbdmx { +/** + * @brief The interface for an implementation of the USB DMX plugin. + */ class PluginImplInterface { public: virtual ~PluginImplInterface() {} /** * @brief Start the implementation. - * @returns true if succuessful, false otherwise. + * @returns true if successful, false otherwise. */ virtual bool Start() = 0; /** * @brief Stop the implementation. - * @returns true if succuessful, false otherwise. + * @returns true if successful, false otherwise. */ virtual bool Stop() = 0; }; diff --git a/plugins/usbdmx/ThreadedUsbSender.h b/plugins/usbdmx/ThreadedUsbSender.h index 336b667043..4771a95fca 100644 --- a/plugins/usbdmx/ThreadedUsbSender.h +++ b/plugins/usbdmx/ThreadedUsbSender.h @@ -44,16 +44,45 @@ namespace usbdmx { */ class ThreadedUsbSender: private ola::thread::Thread { public: + /** + * @brief Create a new ThreadedUsbSender. + * @param usb_device The usb_device to use. The ThreadedUsbSender takes a ref + * on the device, while the ThreadedUsbSender object exists. + * @param usb_handle The handle to use for the DMX transfer. + */ ThreadedUsbSender(libusb_device *usb_device, libusb_device_handle *usb_handle); virtual ~ThreadedUsbSender(); + /** + * @brief Start the new thread. + * @returns true if the thread is running, false otherwise. + */ bool Start(); + + /** + * @brief Entry point for the new thread. + * @returns NULL. + */ void *Run(); + /** + * @brief Buffer a DMX frame for sending. + * @param buffer the DmxBuffer to send. + * + * This should be called in the main thread. + */ bool SendDMX(const DmxBuffer &buffer); protected: + /** + * @brief Perform the DMX transfer. + * @param handle the libusb_device_handle to use for the transfer. + * @param buffer The DmxBuffer to transfer. + * @returns true if the transfer was completed, false otherwise. + * + * This is called from the sender thread. + */ virtual bool TransmitBuffer(libusb_device_handle *handle, const DmxBuffer &buffer) = 0; From 39722278f2e6dfa4b9bd8ccb32ff0aec215d7b7f Mon Sep 17 00:00:00 2001 From: Simon Newton Date: Fri, 14 Nov 2014 18:21:00 -0800 Subject: [PATCH 12/34] Handle libusb hotplug events in the correct thread. --- plugins/usbdmx/AnymaWidget.cpp | 1 + plugins/usbdmx/AsyncPluginImpl.cpp | 12 ++- plugins/usbdmx/AsyncPluginImpl.h | 3 + plugins/usbdmx/EuroliteProWidget.cpp | 1 + plugins/usbdmx/Makefile.mk | 2 + plugins/usbdmx/SunliteWidget.cpp | 1 + plugins/usbdmx/SyncronizedWidgetObserver.cpp | 81 ++++++++++++++ plugins/usbdmx/SyncronizedWidgetObserver.h | 107 +++++++++++++++++++ plugins/usbdmx/VellemanWidget.cpp | 1 + plugins/usbdmx/WidgetFactory.h | 1 + 10 files changed, 205 insertions(+), 5 deletions(-) create mode 100644 plugins/usbdmx/SyncronizedWidgetObserver.cpp create mode 100644 plugins/usbdmx/SyncronizedWidgetObserver.h diff --git a/plugins/usbdmx/AnymaWidget.cpp b/plugins/usbdmx/AnymaWidget.cpp index 5ac361fdfa..12ebeda43f 100644 --- a/plugins/usbdmx/AnymaWidget.cpp +++ b/plugins/usbdmx/AnymaWidget.cpp @@ -148,6 +148,7 @@ AsynchronousAnymaWidget::~AsynchronousAnymaWidget() { libusb_free_transfer(m_transfer); delete[] m_control_setup_buffer; + libusb_close(m_usb_handle); libusb_unref_device(m_usb_device); } diff --git a/plugins/usbdmx/AsyncPluginImpl.cpp b/plugins/usbdmx/AsyncPluginImpl.cpp index 3bd648e8e8..0436c3b7ab 100644 --- a/plugins/usbdmx/AsyncPluginImpl.cpp +++ b/plugins/usbdmx/AsyncPluginImpl.cpp @@ -90,7 +90,6 @@ void LibUsbThread::HotPlugStart() { } void LibUsbThread::HotPlugStop(libusb_hotplug_callback_handle handle) { - OLA_INFO << "stopping libusb thread"; { ola::thread::MutexLocker locker(&m_term_mutex); m_term = true; @@ -143,6 +142,7 @@ AsyncPluginImpl::AsyncPluginImpl(PluginAdaptor *plugin_adaptor, : m_plugin_adaptor(plugin_adaptor), m_plugin(plugin), m_libusb_adaptor(libusb_adaptor), + m_widget_observer(this, plugin_adaptor), m_context(NULL), m_use_hotplug(false), m_stopping(false) { @@ -214,8 +214,10 @@ bool AsyncPluginImpl::Stop() { #ifdef OLA_LIBUSB_HAS_HOTPLUG_API void AsyncPluginImpl::HotPlugEvent(struct libusb_device *usb_device, libusb_hotplug_event event) { + /* OLA_INFO << "Got USB hotplug event for " << usb_device << " : " << (event == LIBUSB_HOTPLUG_EVENT_DEVICE_ARRIVED ? "add" : "del"); + */ if (event == LIBUSB_HOTPLUG_EVENT_DEVICE_ARRIVED) { DeviceAdded(usb_device); } else if (event == LIBUSB_HOTPLUG_EVENT_DEVICE_LEFT) { @@ -306,12 +308,12 @@ void AsyncPluginImpl::FindUSBDevices() { } void AsyncPluginImpl::DeviceAdded(libusb_device *usb_device) { - struct libusb_device_descriptor device_descriptor; - libusb_get_device_descriptor(usb_device, &device_descriptor); + struct libusb_device_descriptor descriptor; + libusb_get_device_descriptor(usb_device, &descriptor); WidgetFactories::iterator iter = m_widget_factories.begin(); for (; iter != m_widget_factories.end(); ++iter) { - if ((*iter)->DeviceAdded(this, usb_device, device_descriptor)) { + if ((*iter)->DeviceAdded(&m_widget_observer, usb_device, descriptor)) { STLReplacePtr(&m_device_factory_map, usb_device, *iter); return; } @@ -322,7 +324,7 @@ void AsyncPluginImpl::DeviceRemoved(libusb_device *usb_device) { WidgetFactory *factory = STLLookupAndRemovePtr( &m_device_factory_map, usb_device); if (factory) { - factory->DeviceRemoved(this, usb_device); + factory->DeviceRemoved(&m_widget_observer, usb_device); } } diff --git a/plugins/usbdmx/AsyncPluginImpl.h b/plugins/usbdmx/AsyncPluginImpl.h index 7716ed4349..e13651276c 100644 --- a/plugins/usbdmx/AsyncPluginImpl.h +++ b/plugins/usbdmx/AsyncPluginImpl.h @@ -33,6 +33,7 @@ #include "ola/thread/Thread.h" #include "plugins/usbdmx/LibUsbAdaptor.h" #include "plugins/usbdmx/PluginImplInterface.h" +#include "plugins/usbdmx/SyncronizedWidgetObserver.h" #include "plugins/usbdmx/WidgetFactory.h" namespace ola { @@ -106,6 +107,8 @@ class AsyncPluginImpl: public PluginImplInterface, public WidgetObserver { PluginAdaptor* const m_plugin_adaptor; Plugin* const m_plugin; LibUsbAdaptor* const m_libusb_adaptor; + SyncronizedWidgetObserver m_widget_observer; + libusb_context *m_context; bool m_use_hotplug; bool m_stopping; diff --git a/plugins/usbdmx/EuroliteProWidget.cpp b/plugins/usbdmx/EuroliteProWidget.cpp index c27df718bc..d4138be9c1 100644 --- a/plugins/usbdmx/EuroliteProWidget.cpp +++ b/plugins/usbdmx/EuroliteProWidget.cpp @@ -212,6 +212,7 @@ AsynchronousEuroliteProWidget::~AsynchronousEuroliteProWidget() { } libusb_free_transfer(m_transfer); + libusb_close(m_usb_handle); libusb_unref_device(m_usb_device); } diff --git a/plugins/usbdmx/Makefile.mk b/plugins/usbdmx/Makefile.mk index d39e3ff525..ea928f9b11 100644 --- a/plugins/usbdmx/Makefile.mk +++ b/plugins/usbdmx/Makefile.mk @@ -32,6 +32,8 @@ plugins_usbdmx_libolausbdmx_la_SOURCES = \ plugins/usbdmx/SunliteWidgetFactory.h \ plugins/usbdmx/SyncPluginImpl.cpp \ plugins/usbdmx/SyncPluginImpl.h \ + plugins/usbdmx/SyncronizedWidgetObserver.cpp \ + plugins/usbdmx/SyncronizedWidgetObserver.h \ plugins/usbdmx/ThreadedUsbSender.cpp \ plugins/usbdmx/ThreadedUsbSender.h \ plugins/usbdmx/UsbDmxPlugin.cpp \ diff --git a/plugins/usbdmx/SunliteWidget.cpp b/plugins/usbdmx/SunliteWidget.cpp index 507805e928..2d1e8f695c 100644 --- a/plugins/usbdmx/SunliteWidget.cpp +++ b/plugins/usbdmx/SunliteWidget.cpp @@ -184,6 +184,7 @@ AsynchronousSunliteWidget::~AsynchronousSunliteWidget() { } libusb_free_transfer(m_transfer); + libusb_close(m_usb_handle); libusb_unref_device(m_usb_device); } diff --git a/plugins/usbdmx/SyncronizedWidgetObserver.cpp b/plugins/usbdmx/SyncronizedWidgetObserver.cpp new file mode 100644 index 0000000000..4b98e29bd1 --- /dev/null +++ b/plugins/usbdmx/SyncronizedWidgetObserver.cpp @@ -0,0 +1,81 @@ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * 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 Library 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * SyncronizedWidgetObserver.cpp + * Transfers widget add/remove events to another thread. + * Copyright (C) 2014 Simon Newton + */ + +#include "plugins/usbdmx/SyncronizedWidgetObserver.h" + +#include "ola/Callback.h" + +namespace ola { +namespace plugin { +namespace usbdmx { + +using ola::thread::Thread; + +SyncronizedWidgetObserver::SyncronizedWidgetObserver( + WidgetObserver *observer, + ola::io::SelectServerInterface *ss) + : m_observer(observer), + m_ss(ss), + m_main_thread_id(Thread::Self()) { +} + +template +bool SyncronizedWidgetObserver::DispatchNewWidget(WidgetClass*widget) { + if (Thread::Self() == m_main_thread_id) { + return m_observer->NewWidget(widget); + } else { + AddFuture f; + m_ss->Execute( + NewSingleCallback( + this, &SyncronizedWidgetObserver::HandleNewWidget, + widget, &f)); + return f.Get(); + } +} + +template +void SyncronizedWidgetObserver::DispatchWidgetRemoved(WidgetClass *widget) { + if (Thread::Self() == m_main_thread_id) { + m_observer->WidgetRemoved(widget); + } else { + RemoveFuture f; + m_ss->Execute( + NewSingleCallback( + this, &SyncronizedWidgetObserver::HandleWidgetRemoved, + widget, &f)); + f.Get(); + } +} + +template +void SyncronizedWidgetObserver::HandleNewWidget(WidgetClass*widget, + AddFuture *f) { + f->Set(m_observer->NewWidget(widget)); +} + +template +void SyncronizedWidgetObserver::HandleWidgetRemoved(WidgetClass *widget, + RemoveFuture *f) { + m_observer->WidgetRemoved(widget); + f->Set(); +} +} // namespace usbdmx +} // namespace plugin +} // namespace ola diff --git a/plugins/usbdmx/SyncronizedWidgetObserver.h b/plugins/usbdmx/SyncronizedWidgetObserver.h new file mode 100644 index 0000000000..e14e81f790 --- /dev/null +++ b/plugins/usbdmx/SyncronizedWidgetObserver.h @@ -0,0 +1,107 @@ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * 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 Library 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * SyncronizedWidgetObserver.h + * Transfers widget add/remove events to another thread. + * Copyright (C) 2014 Simon Newton + */ + +#ifndef PLUGINS_USBDMX_SYNCRONIZEDWIDGETOBSERVER_H_ +#define PLUGINS_USBDMX_SYNCRONIZEDWIDGETOBSERVER_H_ + +#include "plugins/usbdmx/WidgetFactory.h" + +#include "ola/io/SelectServerInterface.h" +#include "ola/thread/Future.h" +#include "ola/thread/Thread.h" + +namespace ola { +namespace plugin { +namespace usbdmx { + +/** + * @brief Transfers widget add/remove events to another thread. + * + * The SyncronizedWidgetObserver ensures that all widget add/removed events are + * handled in the thread that created the SyncronizedWidgetObserver object. + */ +class SyncronizedWidgetObserver : public WidgetObserver { + public: + /** + * @brief Create a new SyncronizedWidgetObserver. + * @param observer the observer to notify on add/remove events. + * @param ss The ss to use the schedule events on. + */ + SyncronizedWidgetObserver(WidgetObserver *observer, + ola::io::SelectServerInterface *ss); + + bool NewWidget(class AnymaWidget *widget) { + return DispatchNewWidget(widget); + } + + bool NewWidget(class EuroliteProWidget *widget) { + return DispatchNewWidget(widget); + } + + bool NewWidget(class SunliteWidget *widget) { + return DispatchNewWidget(widget); + } + + bool NewWidget(class VellemanWidget *widget) { + return DispatchNewWidget(widget); + } + + void WidgetRemoved(class AnymaWidget *widget) { + DispatchWidgetRemoved(widget); + } + + void WidgetRemoved(class EuroliteProWidget *widget) { + DispatchWidgetRemoved(widget); + } + + void WidgetRemoved(class SunliteWidget *widget) { + DispatchWidgetRemoved(widget); + } + + void WidgetRemoved(class VellemanWidget *widget) { + DispatchWidgetRemoved(widget); + } + + private: + typedef ola::thread::Future AddFuture; + typedef ola::thread::Future RemoveFuture; + + WidgetObserver* const m_observer; + ola::io::SelectServerInterface* const m_ss; + const ola::thread::ThreadId m_main_thread_id; + + template + bool DispatchNewWidget(WidgetClass *widget); + + template + void DispatchWidgetRemoved(WidgetClass *widget); + + template + void HandleNewWidget(WidgetClass *widget, AddFuture *f); + + template + void HandleWidgetRemoved(WidgetClass *widget, RemoveFuture *f); + + DISALLOW_COPY_AND_ASSIGN(SyncronizedWidgetObserver); +}; +} // namespace usbdmx +} // namespace plugin +} // namespace ola +#endif // PLUGINS_USBDMX_SYNCRONIZEDWIDGETOBSERVER_H_ diff --git a/plugins/usbdmx/VellemanWidget.cpp b/plugins/usbdmx/VellemanWidget.cpp index 79190115d3..0666239817 100644 --- a/plugins/usbdmx/VellemanWidget.cpp +++ b/plugins/usbdmx/VellemanWidget.cpp @@ -282,6 +282,7 @@ AsynchronousVellemanWidget::~AsynchronousVellemanWidget() { libusb_free_transfer(m_transfer); delete[] m_control_setup_buffer; + libusb_close(m_usb_handle); libusb_unref_device(m_usb_device); } diff --git a/plugins/usbdmx/WidgetFactory.h b/plugins/usbdmx/WidgetFactory.h index 67aa8c018c..1e091724fd 100644 --- a/plugins/usbdmx/WidgetFactory.h +++ b/plugins/usbdmx/WidgetFactory.h @@ -24,6 +24,7 @@ #include #include #include "ola/Logging.h" +#include "ola/base/Macro.h" #include "ola/stl/STLUtils.h" namespace ola { From 64a5f8f93e8e020be28e2966d6ac4f2cb34ff879 Mon Sep 17 00:00:00 2001 From: Simon Newton Date: Sun, 16 Nov 2014 12:22:29 -0800 Subject: [PATCH 13/34] Add support for async events when hotplug isn't supported. --- plugins/usbdmx/AnymaWidget.cpp | 48 ++-- plugins/usbdmx/AnymaWidget.h | 19 +- plugins/usbdmx/AnymaWidgetFactory.cpp | 24 +- plugins/usbdmx/AnymaWidgetFactory.h | 6 +- plugins/usbdmx/AsyncPluginImpl.cpp | 284 +++++++++----------- plugins/usbdmx/AsyncPluginImpl.h | 32 +-- plugins/usbdmx/EuroliteProWidget.cpp | 16 +- plugins/usbdmx/EuroliteProWidget.h | 15 +- plugins/usbdmx/EuroliteProWidgetFactory.cpp | 26 +- plugins/usbdmx/EuroliteProWidgetFactory.h | 5 +- plugins/usbdmx/GenericOutputPort.cpp | 5 - plugins/usbdmx/GenericOutputPort.h | 4 - plugins/usbdmx/LibUsbAdaptor.cpp | 175 +++++++++++- plugins/usbdmx/LibUsbAdaptor.h | 123 ++++++++- plugins/usbdmx/LibUsbHelper.cpp | 94 ------- plugins/usbdmx/LibUsbHelper.h | 62 ----- plugins/usbdmx/LibUsbThread.cpp | 130 +++++++++ plugins/usbdmx/LibUsbThread.h | 210 +++++++++++++++ plugins/usbdmx/Makefile.mk | 4 +- plugins/usbdmx/SunliteWidget.cpp | 16 +- plugins/usbdmx/SunliteWidget.h | 14 +- plugins/usbdmx/SunliteWidgetFactory.cpp | 12 +- plugins/usbdmx/SunliteWidgetFactory.h | 5 +- plugins/usbdmx/SyncPluginImpl.cpp | 16 +- plugins/usbdmx/SyncPluginImpl.h | 14 +- plugins/usbdmx/UsbDmxPlugin.cpp | 9 +- plugins/usbdmx/UsbDmxPlugin.h | 7 +- plugins/usbdmx/VellemanWidget.cpp | 13 +- plugins/usbdmx/VellemanWidget.h | 15 +- plugins/usbdmx/VellemanWidgetFactory.cpp | 13 +- plugins/usbdmx/VellemanWidgetFactory.h | 6 +- plugins/usbdmx/Widget.h | 20 ++ 32 files changed, 997 insertions(+), 445 deletions(-) create mode 100644 plugins/usbdmx/LibUsbThread.cpp create mode 100644 plugins/usbdmx/LibUsbThread.h diff --git a/plugins/usbdmx/AnymaWidget.cpp b/plugins/usbdmx/AnymaWidget.cpp index 12ebeda43f..eb0906c8e5 100644 --- a/plugins/usbdmx/AnymaWidget.cpp +++ b/plugins/usbdmx/AnymaWidget.cpp @@ -25,7 +25,7 @@ #include "ola/Logging.h" #include "ola/Constants.h" -#include "plugins/usbdmx/LibUsbHelper.h" +#include "plugins/usbdmx/LibUsbAdaptor.h" #include "plugins/usbdmx/ThreadedUsbSender.h" namespace ola { @@ -73,7 +73,7 @@ AnymaThreadedSender::AnymaThreadedSender( } bool AnymaThreadedSender::TransmitBuffer(libusb_device_handle *handle, - const DmxBuffer &buffer) { + const DmxBuffer &buffer) { int r = libusb_control_transfer( handle, LIBUSB_REQUEST_TYPE_VENDOR | LIBUSB_RECIPIENT_DEVICE | @@ -89,16 +89,17 @@ bool AnymaThreadedSender::TransmitBuffer(libusb_device_handle *handle, } -SynchronousAnymaWidget::SynchronousAnymaWidget(libusb_device *usb_device, +SynchronousAnymaWidget::SynchronousAnymaWidget(LibUsbAdaptor *adaptor, + libusb_device *usb_device, const string &serial) - : AnymaWidget(serial), + : AnymaWidget(adaptor, serial), m_usb_device(usb_device) { } bool SynchronousAnymaWidget::Init() { libusb_device_handle *usb_handle; - bool ok = LibUsbHelper::OpenDeviceAndClaimInterface( + bool ok = m_adaptor->OpenDeviceAndClaimInterface( m_usb_device, 0, &usb_handle); if (!ok) { return false; @@ -118,9 +119,10 @@ bool SynchronousAnymaWidget::SendDMX(const DmxBuffer &buffer) { } AsynchronousAnymaWidget::AsynchronousAnymaWidget( + LibUsbAdaptor *adaptor, libusb_device *usb_device, const string &serial) - : AnymaWidget(serial), + : AnymaWidget(adaptor, serial), m_usb_device(usb_device), m_usb_handle(NULL), m_control_setup_buffer(NULL), @@ -134,26 +136,30 @@ AsynchronousAnymaWidget::AsynchronousAnymaWidget( AsynchronousAnymaWidget::~AsynchronousAnymaWidget() { bool canceled = false; - OLA_INFO << "AsynchronousAnymaWidget shutdown"; while (1) { ola::thread::MutexLocker locker(&m_mutex); - if (m_transfer_state == IDLE) { + /* + OLA_INFO << "AsynchronousAnymaWidget shutdown, state is " + << m_transfer_state; + */ + if (m_transfer_state == IDLE || m_transfer_state == DISCONNECTED) { break; } if (!canceled) { libusb_cancel_transfer(m_transfer); canceled = true; + OLA_INFO << "Canceling transfer"; } } libusb_free_transfer(m_transfer); delete[] m_control_setup_buffer; - libusb_close(m_usb_handle); + m_adaptor->CloseHandle(m_usb_handle); libusb_unref_device(m_usb_device); } bool AsynchronousAnymaWidget::Init() { - bool ok = LibUsbHelper::OpenDeviceAndClaimInterface( + bool ok = m_adaptor->OpenDeviceAndClaimInterface( m_usb_device, 0, &m_usb_handle); if (!ok) { return false; @@ -162,8 +168,6 @@ bool AsynchronousAnymaWidget::Init() { } bool AsynchronousAnymaWidget::SendDMX(const DmxBuffer &buffer) { - OLA_INFO << "Call to AsynchronousAnymaWidget::SendDMX"; - if (!m_usb_handle) { OLA_WARN << "AsynchronousAnymaWidget hasn't been initialized"; return false; @@ -196,9 +200,13 @@ bool AsynchronousAnymaWidget::SendDMX(const DmxBuffer &buffer) { int ret = libusb_submit_transfer(m_transfer); if (ret) { OLA_WARN << "libusb_submit_transfer returned " << libusb_error_name(ret); + if (ret == LIBUSB_ERROR_NO_DEVICE) { + OLA_INFO << "State now DISCONNECTED"; + m_transfer_state = DISCONNECTED; + } return false; } - OLA_INFO << "submit ok"; + OLA_INFO << "submit ok, state now IN_PROGRESS"; m_transfer_state = IN_PROGRESS; return true; } @@ -211,9 +219,19 @@ void AsynchronousAnymaWidget::TransferComplete( return; } - OLA_INFO << "async transfer complete"; + + OLA_WARN << "Transfer returned " << transfer->status; + if (transfer->status != LIBUSB_TRANSFER_COMPLETED) { + OLA_WARN << "Transfer returned " << transfer->status; + } + + OLA_INFO << transfer->length << " " << (transfer->actual_length + + LIBUSB_CONTROL_SETUP_SIZE); + ola::thread::MutexLocker locker(&m_mutex); - m_transfer_state = IDLE; + m_transfer_state = transfer->status == LIBUSB_TRANSFER_NO_DEVICE ? + DISCONNECTED : IDLE; + OLA_INFO << "State now " << m_transfer_state; } } // namespace usbdmx } // namespace plugin diff --git a/plugins/usbdmx/AnymaWidget.h b/plugins/usbdmx/AnymaWidget.h index 92e2e519f0..2d54ca0ec8 100644 --- a/plugins/usbdmx/AnymaWidget.h +++ b/plugins/usbdmx/AnymaWidget.h @@ -37,13 +37,17 @@ namespace usbdmx { /** * @brief The base class for Anyma Widgets. */ -class AnymaWidget: public Widget { +class AnymaWidget: public BaseWidget { public: /** * @brief Create a new AnymaWidget. + * @param adaptor the LibUsbAdaptor to use. * @param serial the serial number of the widget. */ - explicit AnymaWidget(const std::string &serial) : m_serial(serial) {} + AnymaWidget(LibUsbAdaptor *adaptor, + const std::string &serial) + : BaseWidget(adaptor), + m_serial(serial) {} virtual ~AnymaWidget() {} @@ -78,11 +82,13 @@ class SynchronousAnymaWidget: public AnymaWidget { public: /** * @brief Create a new SynchronousAnymaWidget. + * @param adaptor the LibUsbAdaptor to use. * @param usb_device the libusb_device to use for the widget. * @param serial the serial number of the widget. */ - SynchronousAnymaWidget(libusb_device *usb_device, - const std::string &serial); + SynchronousAnymaWidget(LibUsbAdaptor *adaptor, + libusb_device *usb_device, + const std::string &serial); bool Init(); @@ -102,10 +108,12 @@ class AsynchronousAnymaWidget : public AnymaWidget { public: /** * @brief Create a new AsynchronousAnymaWidget. + * @param adaptor the LibUsbAdaptor to use. * @param usb_device the libusb_device to use for the widget. * @param serial the serial number of the widget. */ - AsynchronousAnymaWidget(libusb_device *usb_device, + AsynchronousAnymaWidget(LibUsbAdaptor *adaptor, + libusb_device *usb_device, const std::string &serial); ~AsynchronousAnymaWidget(); @@ -124,6 +132,7 @@ class AsynchronousAnymaWidget : public AnymaWidget { enum TransferState { IDLE, IN_PROGRESS, + DISCONNECTED, }; libusb_device* const m_usb_device; diff --git a/plugins/usbdmx/AnymaWidgetFactory.cpp b/plugins/usbdmx/AnymaWidgetFactory.cpp index f9d1b72d02..b0ed5e0b32 100644 --- a/plugins/usbdmx/AnymaWidgetFactory.cpp +++ b/plugins/usbdmx/AnymaWidgetFactory.cpp @@ -21,8 +21,12 @@ #include "plugins/usbdmx/AnymaWidgetFactory.h" #include "ola/Logging.h" +#include "ola/base/Flags.h" #include "plugins/usbdmx/AnymaWidget.h" -#include "plugins/usbdmx/LibUsbHelper.h" +#include "plugins/usbdmx/LibUsbAdaptor.h" + +DECLARE_bool(use_async_libusb); + namespace ola { namespace plugin { @@ -41,18 +45,17 @@ bool AnymaWidgetFactory::DeviceAdded( } OLA_INFO << "Found a new Anyma device"; - LibUsbHelper::DeviceInformation info; - if (!LibUsbHelper::GetDeviceInfo(usb_device, descriptor, &info)) { + LibUsbAdaptor::DeviceInformation info; + if (!m_adaptor->GetDeviceInfo(usb_device, descriptor, &info)) { return false; } - if (!LibUsbHelper::CheckManufacturer( + if (!m_adaptor->CheckManufacturer( AnymaWidget::EXPECTED_MANUFACTURER, info.manufacturer)) { return false; } - if (!LibUsbHelper::CheckProduct( - AnymaWidget::EXPECTED_PRODUCT, info.product)) { + if (!m_adaptor->CheckProduct(AnymaWidget::EXPECTED_PRODUCT, info.product)) { return false; } @@ -72,8 +75,13 @@ bool AnymaWidgetFactory::DeviceAdded( } } - return AddWidget(observer, usb_device, - new AsynchronousAnymaWidget(usb_device, info.serial)); + AnymaWidget *widget; + if (FLAGS_use_async_libusb) { + widget = new AsynchronousAnymaWidget(m_adaptor, usb_device, info.serial); + } else { + widget = new SynchronousAnymaWidget(m_adaptor, usb_device, info.serial); + } + return AddWidget(observer, usb_device, widget); } } // namespace usbdmx } // namespace plugin diff --git a/plugins/usbdmx/AnymaWidgetFactory.h b/plugins/usbdmx/AnymaWidgetFactory.h index a0c81464e6..f88097d540 100644 --- a/plugins/usbdmx/AnymaWidgetFactory.h +++ b/plugins/usbdmx/AnymaWidgetFactory.h @@ -33,7 +33,10 @@ namespace usbdmx { */ class AnymaWidgetFactory : public BaseWidgetFactory { public: - AnymaWidgetFactory() : m_missing_serial_number(false) {} + explicit AnymaWidgetFactory(class LibUsbAdaptor *adaptor) + : m_missing_serial_number(false), + m_adaptor(adaptor) { + } bool DeviceAdded( WidgetObserver *observer, @@ -42,6 +45,7 @@ class AnymaWidgetFactory : public BaseWidgetFactory { private: bool m_missing_serial_number; + class LibUsbAdaptor *m_adaptor; static const uint16_t VENDOR_ID; static const uint16_t PRODUCT_ID; diff --git a/plugins/usbdmx/AsyncPluginImpl.cpp b/plugins/usbdmx/AsyncPluginImpl.cpp index 0436c3b7ab..1d2c3fd320 100644 --- a/plugins/usbdmx/AsyncPluginImpl.cpp +++ b/plugins/usbdmx/AsyncPluginImpl.cpp @@ -24,6 +24,8 @@ #include #include +#include + #include "ola/Logging.h" #include "ola/StringUtils.h" #include "ola/stl/STLUtils.h" @@ -33,6 +35,8 @@ #include "plugins/usbdmx/AnymaWidgetFactory.h" #include "plugins/usbdmx/EuroliteProWidgetFactory.h" #include "plugins/usbdmx/GenericDevice.h" +#include "plugins/usbdmx/LibUsbAdaptor.h" +#include "plugins/usbdmx/LibUsbThread.h" #include "plugins/usbdmx/SunliteWidgetFactory.h" #include "plugins/usbdmx/VellemanWidget.h" #include "plugins/usbdmx/VellemanWidgetFactory.h" @@ -43,8 +47,8 @@ namespace usbdmx { namespace { -/* - * Called by libusb when a USB device is added / removed. +/** + * @brief Called by libusb when a USB device is added / removed. */ #ifdef OLA_LIBUSB_HAS_HOTPLUG_API int hotplug_callback(OLA_UNUSED struct libusb_context *ctx, @@ -58,102 +62,16 @@ int hotplug_callback(OLA_UNUSED struct libusb_context *ctx, #endif } // namespace -class LibUsbThread : private ola::thread::Thread { - public: - explicit LibUsbThread(libusb_context *context) - : m_context(context), - m_term(false), - m_device_count(0) { - } - - #ifdef OLA_LIBUSB_HAS_HOTPLUG_API - void HotPlugStart(); - void HotPlugStop(libusb_hotplug_callback_handle handle); - #endif - - void AddDevice(); - void RemoveDevice(libusb_device_handle *handle); - - void *Run(); - - private: - libusb_context *m_context; - bool m_term; // GUARDED_BY(m_term_mutex) - ola::thread::Mutex m_term_mutex; - unsigned int m_device_count; -}; - -#ifdef OLA_LIBUSB_HAS_HOTPLUG_API -void LibUsbThread::HotPlugStart() { - OLA_INFO << "STarting libusb thread"; - Start(); -} - -void LibUsbThread::HotPlugStop(libusb_hotplug_callback_handle handle) { - { - ola::thread::MutexLocker locker(&m_term_mutex); - m_term = true; - } - libusb_hotplug_deregister_callback(m_context, handle); - Join(); -} -#endif - -void LibUsbThread::AddDevice() { - m_device_count++; - if (m_device_count == 1) { - Start(); - } -} - -void LibUsbThread::RemoveDevice(libusb_device_handle *handle) { - if (m_device_count == 1) { - { - ola::thread::MutexLocker locker(&m_term_mutex); - m_term = true; - } - } - libusb_close(handle); - if (m_device_count == 1) { - Join(); - } - m_device_count--; -} - -void *LibUsbThread::Run() { - OLA_INFO << "----libusb event thread is running"; - while (1) { - { - ola::thread::MutexLocker locker(&m_term_mutex); - if (m_term) - break; - } - // TODO(simon): If hotplug isn't active, use libusb_handle_events_timeout - // here. - libusb_handle_events(m_context); - } - OLA_INFO << "----libusb thread exiting"; - return NULL; -} - AsyncPluginImpl::AsyncPluginImpl(PluginAdaptor *plugin_adaptor, Plugin *plugin, - LibUsbAdaptor *libusb_adaptor) + unsigned int debug_level) : m_plugin_adaptor(plugin_adaptor), m_plugin(plugin), - m_libusb_adaptor(libusb_adaptor), + m_debug_level(debug_level), m_widget_observer(this, plugin_adaptor), m_context(NULL), m_use_hotplug(false), - m_stopping(false) { - #ifdef OLA_LIBUSB_HAS_HOTPLUG_API - m_hotplug_handle = 0; - #endif - - m_widget_factories.push_back(new AnymaWidgetFactory()); - m_widget_factories.push_back(new EuroliteProWidgetFactory()); - m_widget_factories.push_back(new SunliteWidgetFactory()); - m_widget_factories.push_back(new VellemanWidgetFactory()); + m_scan_timeout(ola::thread::INVALID_TIMEOUT) { } AsyncPluginImpl::~AsyncPluginImpl() { @@ -166,62 +84,81 @@ bool AsyncPluginImpl::Start() { return false; } - m_libusb_adaptor->SetDebug(m_context); + OLA_DEBUG << "libusb debug level set to " << m_debug_level; + libusb_set_debug(m_context, m_debug_level); - m_use_hotplug = SetupHotPlug(); - OLA_INFO << "SetupHotPlug returned " << m_use_hotplug; + m_use_hotplug = HotplugSupported(); + OLA_INFO << "HotplugSupported returned " << m_use_hotplug; if (m_use_hotplug) { - m_usb_thread.reset((new LibUsbThread(m_context))); - m_usb_thread->HotPlugStart(); + m_usb_thread.reset(new LibUsbHotplugThread( + m_context, hotplug_callback, this)); } else { + m_usb_thread.reset(new LibUsbSimpleThread(m_context)); + } + m_usb_adaptor.reset(new AsyncronousLibUsbAdaptor(m_usb_thread.get())); + + // Setup the factories. + m_widget_factories.push_back(new AnymaWidgetFactory(m_usb_adaptor.get())); + m_widget_factories.push_back( + new EuroliteProWidgetFactory(m_usb_adaptor.get())); + m_widget_factories.push_back(new SunliteWidgetFactory(m_usb_adaptor.get())); + m_widget_factories.push_back(new VellemanWidgetFactory(m_usb_adaptor.get())); + + // If we're using hotplug, this starts the hotplug thread. + if (!m_usb_thread->Init()) { + STLDeleteElements(&m_widget_factories); + m_usb_adaptor.reset(); + m_usb_thread.reset(); + return false; + } + + if (!m_use_hotplug) { // Either we don't support hotplug or the setup failed. // As poor man's hotplug, we call libusb_get_device_list periodically to // check for new devices. + m_scan_timeout = m_plugin_adaptor->RegisterRepeatingTimeout( + TimeInterval(5, 0), + NewCallback(this, &AsyncPluginImpl::ScanUSBDevices)); - /* - m_plugin_adaptor->RegisterRepeatingTimeout( - 3500, - NewSingleCallback(this, &AsyncPluginImpl::FindUSBDevices)); - */ + // Call it immediately now. + ScanUSBDevices(); } return true; } bool AsyncPluginImpl::Stop() { - m_stopping = true; - - if (m_usb_thread.get()) { - if (m_use_hotplug) { - m_usb_thread->HotPlugStop(m_hotplug_handle); - } + if (m_scan_timeout != ola::thread::INVALID_TIMEOUT) { + m_plugin_adaptor->RemoveTimeout(m_scan_timeout); + m_scan_timeout = ola::thread::INVALID_TIMEOUT; } - m_usb_thread.reset(); - // I think we need a lock here + m_usb_thread->Shutdown(); + USBDeviceToFactoryMap::iterator iter = m_device_factory_map.begin(); for (; iter != m_device_factory_map.end(); ++iter) { iter->second->DeviceRemoved(this, iter->first); } m_device_factory_map.clear(); + m_usb_thread.reset(); + m_usb_adaptor.reset(); + STLDeleteElements(&m_widget_factories); + libusb_exit(m_context); m_context = NULL; return true; } - #ifdef OLA_LIBUSB_HAS_HOTPLUG_API void AsyncPluginImpl::HotPlugEvent(struct libusb_device *usb_device, libusb_hotplug_event event) { - /* OLA_INFO << "Got USB hotplug event for " << usb_device << " : " << (event == LIBUSB_HOTPLUG_EVENT_DEVICE_ARRIVED ? "add" : "del"); - */ if (event == LIBUSB_HOTPLUG_EVENT_DEVICE_ARRIVED) { - DeviceAdded(usb_device); + USBDeviceAdded(usb_device); } else if (event == LIBUSB_HOTPLUG_EVENT_DEVICE_LEFT) { - DeviceRemoved(usb_device); + USBDeviceRemoved(usb_device); } } #endif @@ -268,59 +205,47 @@ void AsyncPluginImpl::WidgetRemoved(class VellemanWidget *widget) { RemoveWidget(widget); } -bool AsyncPluginImpl::SetupHotPlug() { -#if defined(LIBUSB_API_VERSION) && (LIBUSB_API_VERSION >= 0x01000102) - if (libusb_has_capability(LIBUSB_CAP_HAS_HOTPLUG) == 0) { - return false; - } - - OLA_INFO << "Calling libusb_hotplug_register_callback"; - int rc = libusb_hotplug_register_callback( - NULL, - static_cast(LIBUSB_HOTPLUG_EVENT_DEVICE_ARRIVED | - LIBUSB_HOTPLUG_EVENT_DEVICE_LEFT), - LIBUSB_HOTPLUG_ENUMERATE, LIBUSB_HOTPLUG_MATCH_ANY, - LIBUSB_HOTPLUG_MATCH_ANY, LIBUSB_HOTPLUG_MATCH_ANY, - hotplug_callback, this, &m_hotplug_handle); - - if (LIBUSB_SUCCESS != rc) { - OLA_WARN << "Error creating a hotplug callback"; - return false; - } - OLA_INFO << "libusb_hotplug_register_callback passed"; - return true; +/** + * @brief Check if this platform supports hotplug. + * @returns true if hotplug is supported and enabled on this platform, false + * otherwise. + */ +bool AsyncPluginImpl::HotplugSupported() { +#ifdef OLA_LIBUSB_HAS_HOTPLUG_API + return libusb_has_capability(LIBUSB_CAP_HAS_HOTPLUG) != 0; #else return false; #endif } -/* - * Find known devices & register them +/** + * @brief Signal a new USB device has been added. + * + * This can be called from either the libusb thread or the main thread. However + * only one of those will be active at once, so we can avoid locking. */ -void AsyncPluginImpl::FindUSBDevices() { - libusb_device **device_list; - size_t device_count = libusb_get_device_list(NULL, &device_list); - - for (unsigned int i = 0; i < device_count; i++) { - DeviceAdded(device_list[i]); - } - libusb_free_device_list(device_list, 1); // unref devices -} - -void AsyncPluginImpl::DeviceAdded(libusb_device *usb_device) { +bool AsyncPluginImpl::USBDeviceAdded(libusb_device *usb_device) { struct libusb_device_descriptor descriptor; libusb_get_device_descriptor(usb_device, &descriptor); WidgetFactories::iterator iter = m_widget_factories.begin(); + OLA_INFO << "Checking " << m_widget_factories.size() << " factories"; for (; iter != m_widget_factories.end(); ++iter) { if ((*iter)->DeviceAdded(&m_widget_observer, usb_device, descriptor)) { STLReplacePtr(&m_device_factory_map, usb_device, *iter); - return; + return true; } } + return false; } -void AsyncPluginImpl::DeviceRemoved(libusb_device *usb_device) { +/** + * @brief Signal a USB device has been removed. + * + * This can be called from either the libusb thread or the main thread. However + * only one of those will be active at once, so we can avoid locking. + */ +void AsyncPluginImpl::USBDeviceRemoved(libusb_device *usb_device) { WidgetFactory *factory = STLLookupAndRemovePtr( &m_device_factory_map, usb_device); if (factory) { @@ -328,6 +253,13 @@ void AsyncPluginImpl::DeviceRemoved(libusb_device *usb_device) { } } +/* + * @brief Signal widget / device addition. + * @param widget The widget that was added. + * @param device The new olad device that uses this new widget. + * + * This is run within the main thread. + */ bool AsyncPluginImpl::StartAndRegisterDevice(Widget *widget, Device *device) { if (!device->Start()) { delete device; @@ -344,6 +276,12 @@ bool AsyncPluginImpl::StartAndRegisterDevice(Widget *widget, Device *device) { return true; } +/* + * @brief Signal widget removal. + * @param widget The widget that was removed. + * + * This is run within the main thread. + */ void AsyncPluginImpl::RemoveWidget(class Widget *widget) { Device *device = STLLookupAndRemovePtr(&m_widget_device_map, widget); if (device) { @@ -352,6 +290,52 @@ void AsyncPluginImpl::RemoveWidget(class Widget *widget) { delete device; } } + +/* + * If hotplug isn't supported, this is called periodically to checked for + * USB devices that have been added or removed. + * + * This is run within the main thread, since the libusb thread only runs if at + * least one USB device is used. + */ +bool AsyncPluginImpl::ScanUSBDevices() { + OLA_INFO << "Scanning USB devices...."; + std::set current_device_ids; + + libusb_device **device_list; + size_t device_count = libusb_get_device_list(NULL, &device_list); + + OLA_INFO << "Got " << device_count << " devices"; + for (unsigned int i = 0; i < device_count; i++) { + libusb_device *usb_device = device_list[i]; + + USBDeviceID device_id(libusb_get_bus_number(usb_device), + libusb_get_device_address(usb_device)); + + current_device_ids.insert(device_id); + + if (!STLContains(m_seen_usb_devices, device_id)) { + OLA_INFO << " " << usb_device; + bool claimed = USBDeviceAdded(usb_device); + STLReplace(&m_seen_usb_devices, device_id, claimed ? usb_device : NULL); + } + } + libusb_free_device_list(device_list, 1); // unref devices + + USBDeviceIDs::iterator iter = m_seen_usb_devices.begin(); + while (iter != m_seen_usb_devices.end()) { + if (!STLContains(current_device_ids, iter->first)) { + if (iter->second) { + USBDeviceRemoved(iter->second); + } + m_seen_usb_devices.erase(iter++); + } else { + iter++; + } + } + return true; +} + } // namespace usbdmx } // namespace plugin } // namespace ola diff --git a/plugins/usbdmx/AsyncPluginImpl.h b/plugins/usbdmx/AsyncPluginImpl.h index e13651276c..c692f3b0fd 100644 --- a/plugins/usbdmx/AsyncPluginImpl.h +++ b/plugins/usbdmx/AsyncPluginImpl.h @@ -31,7 +31,6 @@ #include "ola/base/Macro.h" #include "ola/thread/Thread.h" -#include "plugins/usbdmx/LibUsbAdaptor.h" #include "plugins/usbdmx/PluginImplInterface.h" #include "plugins/usbdmx/SyncronizedWidgetObserver.h" #include "plugins/usbdmx/WidgetFactory.h" @@ -58,11 +57,11 @@ class AsyncPluginImpl: public PluginImplInterface, public WidgetObserver { * transferred. * @param plugin The parent Plugin object which is used when creating * devices. - * @param libusb_adaptor The adaptor to use when calling libusb. + * @param debug_level the debug level to use for libusb. */ AsyncPluginImpl(PluginAdaptor *plugin_adaptor, Plugin *plugin, - LibUsbAdaptor *libusb_adaptor); + unsigned int debug_level); ~AsyncPluginImpl(); bool Start(); @@ -97,39 +96,36 @@ class AsyncPluginImpl: public PluginImplInterface, public WidgetObserver { typedef std::vector WidgetFactories; typedef std::map USBDeviceToFactoryMap; typedef std::map WidgetToDeviceMap; - - struct USBDeviceInformation { - std::string manufacturer; - std::string product; - std::string serial; - }; + typedef std::pair USBDeviceID; + typedef std::map USBDeviceIDs; PluginAdaptor* const m_plugin_adaptor; Plugin* const m_plugin; - LibUsbAdaptor* const m_libusb_adaptor; + const unsigned int m_debug_level; + SyncronizedWidgetObserver m_widget_observer; libusb_context *m_context; bool m_use_hotplug; - bool m_stopping; std::auto_ptr m_usb_thread; + std::auto_ptr m_usb_adaptor; WidgetFactories m_widget_factories; USBDeviceToFactoryMap m_device_factory_map; WidgetToDeviceMap m_widget_device_map; - #ifdef OLA_LIBUSB_HAS_HOTPLUG_API - libusb_hotplug_callback_handle m_hotplug_handle; - #endif + // Members used if hotplug is not supported + ola::thread::timeout_id m_scan_timeout; + USBDeviceIDs m_seen_usb_devices; - bool SetupHotPlug(); - void DeviceAdded(libusb_device *device); - void DeviceRemoved(libusb_device *device); + bool HotplugSupported(); + bool USBDeviceAdded(libusb_device *device); + void USBDeviceRemoved(libusb_device *device); bool StartAndRegisterDevice(class Widget *widget, Device *device); void RemoveWidget(class Widget *widget); - void FindUSBDevices(); + bool ScanUSBDevices(); DISALLOW_COPY_AND_ASSIGN(AsyncPluginImpl); }; diff --git a/plugins/usbdmx/EuroliteProWidget.cpp b/plugins/usbdmx/EuroliteProWidget.cpp index d4138be9c1..810f09c47b 100644 --- a/plugins/usbdmx/EuroliteProWidget.cpp +++ b/plugins/usbdmx/EuroliteProWidget.cpp @@ -24,7 +24,7 @@ #include "ola/Constants.h" #include "ola/Logging.h" -#include "plugins/usbdmx/LibUsbHelper.h" +#include "plugins/usbdmx/LibUsbAdaptor.h" #include "plugins/usbdmx/ThreadedUsbSender.h" namespace ola { @@ -78,7 +78,7 @@ void CreateFrame( * 1 but we check them all just in case. */ bool LocateInterface(libusb_device *usb_device, - int *interface_number) { + int *interface_number) { struct libusb_config_descriptor *device_config; if (libusb_get_config_descriptor(usb_device, 0, &device_config) != 0) { OLA_WARN << "Failed to get device config descriptor"; @@ -133,7 +133,7 @@ EuroliteProThreadedSender::EuroliteProThreadedSender( } bool EuroliteProThreadedSender::TransmitBuffer(libusb_device_handle *handle, - const DmxBuffer &buffer) { + const DmxBuffer &buffer) { uint8_t frame[EuroliteProWidget::EUROLITE_PRO_FRAME_SIZE]; CreateFrame(buffer, frame); @@ -153,9 +153,10 @@ bool EuroliteProThreadedSender::TransmitBuffer(libusb_device_handle *handle, } SynchronousEuroliteProWidget::SynchronousEuroliteProWidget( + LibUsbAdaptor *adaptor, libusb_device *usb_device, const string &serial) - : EuroliteProWidget(serial), + : EuroliteProWidget(adaptor, serial), m_usb_device(usb_device) { } @@ -167,7 +168,7 @@ bool SynchronousEuroliteProWidget::Init() { return false; } - bool ok = LibUsbHelper::OpenDeviceAndClaimInterface( + bool ok = m_adaptor->OpenDeviceAndClaimInterface( m_usb_device, interface_number, &usb_handle); if (!ok) { return false; @@ -187,9 +188,10 @@ bool SynchronousEuroliteProWidget::SendDMX(const DmxBuffer &buffer) { } AsynchronousEuroliteProWidget::AsynchronousEuroliteProWidget( + class LibUsbAdaptor *adaptor, libusb_device *usb_device, const string &serial) - : EuroliteProWidget(serial), + : EuroliteProWidget(adaptor, serial), m_usb_device(usb_device), m_usb_handle(NULL), m_transfer_state(IDLE) { @@ -222,7 +224,7 @@ bool AsynchronousEuroliteProWidget::Init() { return false; } - bool ok = LibUsbHelper::OpenDeviceAndClaimInterface( + bool ok = m_adaptor->OpenDeviceAndClaimInterface( m_usb_device, 0, &m_usb_handle); if (!ok) { return false; diff --git a/plugins/usbdmx/EuroliteProWidget.h b/plugins/usbdmx/EuroliteProWidget.h index cfa175a7c2..3b4050f606 100644 --- a/plugins/usbdmx/EuroliteProWidget.h +++ b/plugins/usbdmx/EuroliteProWidget.h @@ -38,13 +38,17 @@ class EuroliteProThreadedSender; /** * @brief The EurolitePro Widget. */ -class EuroliteProWidget : public Widget { +class EuroliteProWidget : public BaseWidget { public: /** * @brief Create a new EuroliteProWidget. + * @param adaptor the LibUsbAdaptor to use. * @param serial the serial number of the widget. */ - explicit EuroliteProWidget(const std::string &serial) : m_serial(serial) {} + EuroliteProWidget(LibUsbAdaptor *adaptor, + const std::string &serial) + : BaseWidget(adaptor), + m_serial(serial) {} /** * @brief Get the serial number of this widget. @@ -85,10 +89,12 @@ class SynchronousEuroliteProWidget: public EuroliteProWidget { public: /** * @brief Create a new SynchronousEuroliteProWidget. + * @param adaptor the LibUsbAdaptor to use. * @param usb_device the libusb_device to use for the widget. * @param serial the serial number of the widget. */ - SynchronousEuroliteProWidget(libusb_device *usb_device, + SynchronousEuroliteProWidget(LibUsbAdaptor *adaptor, + libusb_device *usb_device, const std::string &serial); bool Init(); @@ -112,7 +118,8 @@ class AsynchronousEuroliteProWidget: public EuroliteProWidget { * @param usb_device the libusb_device to use for the widget. * @param serial the serial number of the widget. */ - AsynchronousEuroliteProWidget(libusb_device *usb_device, + AsynchronousEuroliteProWidget(class LibUsbAdaptor *adaptor, + libusb_device *usb_device, const std::string &serial); ~AsynchronousEuroliteProWidget(); diff --git a/plugins/usbdmx/EuroliteProWidgetFactory.cpp b/plugins/usbdmx/EuroliteProWidgetFactory.cpp index 20385930b1..3e30994f73 100644 --- a/plugins/usbdmx/EuroliteProWidgetFactory.cpp +++ b/plugins/usbdmx/EuroliteProWidgetFactory.cpp @@ -21,7 +21,10 @@ #include "plugins/usbdmx/EuroliteProWidgetFactory.h" #include "ola/Logging.h" -#include "plugins/usbdmx/LibUsbHelper.h" +#include "ola/base/Flags.h" +#include "plugins/usbdmx/LibUsbAdaptor.h" + +DECLARE_bool(use_async_libusb); namespace ola { namespace plugin { @@ -40,17 +43,17 @@ bool EuroliteProWidgetFactory::DeviceAdded( } OLA_INFO << "Found a new EurolitePro device"; - LibUsbHelper::DeviceInformation info; - if (!LibUsbHelper::GetDeviceInfo(usb_device, descriptor, &info)) { + LibUsbAdaptor::DeviceInformation info; + if (!m_adaptor->GetDeviceInfo(usb_device, descriptor, &info)) { return false; } - if (!LibUsbHelper::CheckManufacturer( + if (!m_adaptor->CheckManufacturer( EuroliteProWidget::EXPECTED_MANUFACTURER, info.manufacturer)) { return false; } - if (!LibUsbHelper::CheckProduct( + if (!m_adaptor->CheckProduct( EuroliteProWidget::EXPECTED_PRODUCT, info.product)) { return false; } @@ -70,10 +73,15 @@ bool EuroliteProWidgetFactory::DeviceAdded( std::ostringstream serial_str; serial_str << bus_number << "-" << device_address; - return AddWidget( - observer, - usb_device, - new AsynchronousEuroliteProWidget(usb_device, serial_str.str())); + EuroliteProWidget *widget = NULL; + if (FLAGS_use_async_libusb) { + widget = new AsynchronousEuroliteProWidget(m_adaptor, usb_device, + serial_str.str()); + } else { + widget = new SynchronousEuroliteProWidget(m_adaptor, usb_device, + serial_str.str()); + } + return AddWidget(observer, usb_device, widget); } } // namespace usbdmx } // namespace plugin diff --git a/plugins/usbdmx/EuroliteProWidgetFactory.h b/plugins/usbdmx/EuroliteProWidgetFactory.h index 836173a2d0..d99b3c3a74 100644 --- a/plugins/usbdmx/EuroliteProWidgetFactory.h +++ b/plugins/usbdmx/EuroliteProWidgetFactory.h @@ -35,7 +35,8 @@ namespace usbdmx { class EuroliteProWidgetFactory : public BaseWidgetFactory { public: - EuroliteProWidgetFactory() {} + explicit EuroliteProWidgetFactory(class LibUsbAdaptor *adaptor) + : m_adaptor(adaptor) {} bool DeviceAdded( WidgetObserver *observer, @@ -43,6 +44,8 @@ class EuroliteProWidgetFactory const struct libusb_device_descriptor &descriptor); private: + class LibUsbAdaptor *m_adaptor; + static const uint16_t PRODUCT_ID; static const uint16_t VENDOR_ID; diff --git a/plugins/usbdmx/GenericOutputPort.cpp b/plugins/usbdmx/GenericOutputPort.cpp index f65c307542..dd6cc304b8 100644 --- a/plugins/usbdmx/GenericOutputPort.cpp +++ b/plugins/usbdmx/GenericOutputPort.cpp @@ -35,11 +35,6 @@ GenericOutputPort::GenericOutputPort(Device *parent, m_widget(widget) { } -GenericOutputPort::~GenericOutputPort() { - // TODO(simon): stop the thread here?? - OLA_INFO << "GenericOutputPort::~GenericOutputPort()"; -} - bool GenericOutputPort::WriteDMX(const DmxBuffer &buffer, OLA_UNUSED uint8_t priority) { m_widget->SendDMX(buffer); diff --git a/plugins/usbdmx/GenericOutputPort.h b/plugins/usbdmx/GenericOutputPort.h index 0756c1b5f6..221a39cd77 100644 --- a/plugins/usbdmx/GenericOutputPort.h +++ b/plugins/usbdmx/GenericOutputPort.h @@ -48,10 +48,6 @@ class GenericOutputPort: public BasicOutputPort { GenericOutputPort(Device *parent, unsigned int id, class Widget *widget); - /** - * @brief Cleanup. - */ - ~GenericOutputPort(); bool WriteDMX(const DmxBuffer &buffer, uint8_t priority); diff --git a/plugins/usbdmx/LibUsbAdaptor.cpp b/plugins/usbdmx/LibUsbAdaptor.cpp index 8ea5682476..ffdab3448a 100644 --- a/plugins/usbdmx/LibUsbAdaptor.cpp +++ b/plugins/usbdmx/LibUsbAdaptor.cpp @@ -13,23 +13,186 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * - * LibUsbAdaptor.h - * The wrapper around libusb that abstracts syncronous vs asyncronous calls. + * LibUsbAdaptor.cpp + * The wrapper around libusb calls. * Copyright (C) 2014 Simon Newton */ #include "plugins/usbdmx/LibUsbAdaptor.h" -#include #include +#include +#include + +#include "plugins/usbdmx/LibUsbThread.h" namespace ola { namespace plugin { namespace usbdmx { -void LibUsbAdaptor::SetDebug(libusb_context *context) { - OLA_DEBUG << "libusb debug level set to " << m_debug_level; - libusb_set_debug(context, m_debug_level); +using std::string; + +namespace { + +/** + * @brief A wrapper around libusb_get_string_descriptor_ascii. + */ +bool GetStringDescriptorAscii(libusb_device_handle *usb_handle, + uint8_t desc_index, + string *data) { + enum { buffer_size = 32 }; // static arrays FTW! + unsigned char buffer[buffer_size]; + int r = libusb_get_string_descriptor_ascii( + usb_handle, + desc_index, + buffer, + buffer_size); + + if (r <= 0) { + OLA_INFO << "libusb_get_string_descriptor_ascii returned " << r; + return false; + } + data->assign(reinterpret_cast(buffer)); + return true; +} + +/** + * @brief A wrapper around libusb_open. + */ +bool Open(libusb_device *usb_device, + libusb_device_handle **usb_handle) { + if (libusb_open(usb_device, usb_handle)) { + OLA_WARN << "Failed to open libusb device: " << usb_device; + return false; + } + return true; +} + +/** + * @brief A wrapper around libusb_close. + */ +void Close(libusb_device_handle *usb_handle) { + libusb_close(usb_handle); +} + +bool OpenHandleAndClaimInterface( + libusb_device *usb_device, + int interface, + libusb_device_handle **usb_handle) { + if (!Open(usb_device, usb_handle)) { + return false; + } + + if (libusb_claim_interface(*usb_handle, 0)) { + OLA_WARN << "Failed to claim interface " << interface + << " for libusb device: " << usb_device; + Close(*usb_handle); + return false; + } + return true; +} +} // namespace + +// LibUsbAdaptor +// ----------------------------------------------------------------------------- + +bool LibUsbAdaptor::GetDeviceInfo( + struct libusb_device *usb_device, + const struct libusb_device_descriptor &device_descriptor, + DeviceInformation *device_info) { + // Since the calls on the handle are syncronous, we don't bother adding the + // handle to the thread. + libusb_device_handle *usb_handle; + if (!Open(usb_device, &usb_handle)) { + return false; + } + + if (!GetStringDescriptorAscii(usb_handle, device_descriptor.iManufacturer, + &device_info->manufacturer)) { + OLA_INFO << "Failed to get manufacturer name"; + } + + if (!GetStringDescriptorAscii(usb_handle, device_descriptor.iProduct, + &device_info->product)) { + OLA_INFO << "Failed to get product name"; + } + + if (!GetStringDescriptorAscii(usb_handle, device_descriptor.iSerialNumber, + &device_info->serial)) { + OLA_WARN << "Failed to read serial number, the device probably doesn't " + << "have one"; + } + + Close(usb_handle); + return true; +} + +bool LibUsbAdaptor::CheckManufacturer(const string &expected, + const string &actual) { + if (expected != actual) { + OLA_WARN << "Manufacturer mismatch: " << expected << " != " << actual; + return false; + } + return true; +} + +bool LibUsbAdaptor::CheckProduct(const string &expected, const string &actual) { + if (expected != actual) { + OLA_WARN << "Product mismatch: " << expected << " != " << actual; + return false; + } + return true; +} + +// SyncronousLibUsbAdaptor +// ----------------------------------------------------------------------------- + +bool SyncronousLibUsbAdaptor::OpenDevice( + libusb_device *usb_device, + libusb_device_handle **usb_handle) { + return Open(usb_device, usb_handle); +} + +bool SyncronousLibUsbAdaptor::OpenDeviceAndClaimInterface( + libusb_device *usb_device, + int interface, + libusb_device_handle **usb_handle) { + return OpenHandleAndClaimInterface(usb_device, interface, usb_handle); +} + +void SyncronousLibUsbAdaptor::CloseHandle(libusb_device_handle *usb_handle) { + Close(usb_handle); +} + +// AsyncronousLibUsbAdaptor +// ----------------------------------------------------------------------------- + +AsyncronousLibUsbAdaptor::AsyncronousLibUsbAdaptor(LibUsbThread *thread) + : m_thread(thread) { +} + +bool AsyncronousLibUsbAdaptor::OpenDevice(libusb_device *usb_device, + libusb_device_handle **usb_handle) { + bool ok = Open(usb_device, usb_handle); + if (ok) { + m_thread->OpenHandle(); + } + return ok; +} + +bool AsyncronousLibUsbAdaptor::OpenDeviceAndClaimInterface( + libusb_device *usb_device, + int interface, + libusb_device_handle **usb_handle) { + bool ok = OpenHandleAndClaimInterface(usb_device, interface, usb_handle); + if (ok) { + m_thread->OpenHandle(); + } + return ok; +} + +void AsyncronousLibUsbAdaptor::CloseHandle(libusb_device_handle *handle) { + m_thread->CloseHandle(handle); } } // namespace usbdmx } // namespace plugin diff --git a/plugins/usbdmx/LibUsbAdaptor.h b/plugins/usbdmx/LibUsbAdaptor.h index 871528320f..8a18eca6a3 100644 --- a/plugins/usbdmx/LibUsbAdaptor.h +++ b/plugins/usbdmx/LibUsbAdaptor.h @@ -14,7 +14,7 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * * LibUsbAdaptor.h - * The wrapper around libusb that abstracts syncronous vs asyncronous calls. + * The wrapper around libusb calls. * Copyright (C) 2014 Simon Newton */ @@ -22,24 +22,133 @@ #define PLUGINS_USBDMX_LIBUSBADAPTOR_H_ #include +#include + #include "ola/base/Macro.h" namespace ola { namespace plugin { namespace usbdmx { +/** + * @brief Wraps calls to libusb so we can test the code. + */ class LibUsbAdaptor { public: - explicit LibUsbAdaptor(unsigned int debug_level) - : m_debug_level(debug_level) { - } + struct DeviceInformation { + std::string manufacturer; + std::string product; + std::string serial; + }; + + virtual ~LibUsbAdaptor() {} + + /** + * @brief Open a libusb device. + * @param usb_device The usb device to open. + * @param[out] usb_handle the new device handle. + * @returns true if the device was opened, false otherwise. + */ + virtual bool OpenDevice(libusb_device *usb_device, + libusb_device_handle **usb_handle) = 0; + + /** + * @brief Open a libusb device and claim an interface. + * @param usb_device The usb device to open. + * @param interface the interface index to claim. + * @param[out] usb_handle the new device handle. + * @returns true if the device was opened and the interface claimed, + * false otherwise. + */ + virtual bool OpenDeviceAndClaimInterface( + libusb_device *usb_device, + int interface, + libusb_device_handle **usb_handle) = 0; + + /** + * @brief Close a libusb handle. + * @param usb_handle the handle to close. + */ + virtual void CloseHandle(libusb_device_handle *usb_handle) = 0; + + /** + * @brief Fetch the manufacturer, product and serial strings from a device. + * @param usb_device The USB device to get information for. + * @param device_descriptor The descriptor to use + * @param[out] device_info The DeviceInformation struct to populate. + * @returns true if we fetched the information, false otherwise. + */ + static bool GetDeviceInfo( + struct libusb_device *usb_device, + const struct libusb_device_descriptor &device_descriptor, + DeviceInformation *device_info); + + /** + * @brief Check if the manufacturer string matches the expected value. + * @param expected The expected manufacturer string. + * @param actual The actual manufacturer string. + * @returns true if the strings matched, false otherwise. + */ + static bool CheckManufacturer(const std::string &expected, + const std::string &actual); + + /** + * @brief Check if the product string matches the expected value. + * @param expected The expected product string. + * @param actual The actual product string. + * @returns true if the strings matched, false otherwise. + */ + static bool CheckProduct(const std::string &expected, + const std::string &actual); +}; + + +/** + * @brief A LibUsbAdaptor for use with Syncronous widgets. + * + * When using syncronous mode, we don't have the requirement of interacting + * with a LibUsbThread. + */ +class SyncronousLibUsbAdaptor : public LibUsbAdaptor { + public: + SyncronousLibUsbAdaptor() {} + + bool OpenDevice(libusb_device *usb_device, + libusb_device_handle **usb_handle); + + bool OpenDeviceAndClaimInterface(libusb_device *usb_device, + int interface, + libusb_device_handle **usb_handle); + + void CloseHandle(libusb_device_handle *usb_handle); + + private: + DISALLOW_COPY_AND_ASSIGN(SyncronousLibUsbAdaptor); +}; + +/** + * @brief A LibUsbAdaptor for use with Asyncronous widgets. + * + * Asyncronous mode requires notifying the LibUsbThread when handles are opened + * and closed. + */ +class AsyncronousLibUsbAdaptor : public LibUsbAdaptor { + public: + explicit AsyncronousLibUsbAdaptor(class LibUsbThread *thread); + + bool OpenDevice(libusb_device *usb_device, + libusb_device_handle **usb_handle); + + bool OpenDeviceAndClaimInterface(libusb_device *usb_device, + int interface, + libusb_device_handle **usb_handle); - void SetDebug(libusb_context *context); + void CloseHandle(libusb_device_handle *usb_handle); private: - unsigned int m_debug_level; + class LibUsbThread *m_thread; - DISALLOW_COPY_AND_ASSIGN(LibUsbAdaptor); + DISALLOW_COPY_AND_ASSIGN(AsyncronousLibUsbAdaptor); }; } // namespace usbdmx } // namespace plugin diff --git a/plugins/usbdmx/LibUsbHelper.cpp b/plugins/usbdmx/LibUsbHelper.cpp index 3bbdbab861..421e26d712 100644 --- a/plugins/usbdmx/LibUsbHelper.cpp +++ b/plugins/usbdmx/LibUsbHelper.cpp @@ -20,107 +20,13 @@ #include "plugins/usbdmx/LibUsbHelper.h" #include -#include #include "ola/Logging.h" namespace ola { namespace plugin { namespace usbdmx { -using std::string; -namespace { -bool GetDescriptorString(libusb_device_handle *usb_handle, - uint8_t desc_index, - string *data) { - enum { buffer_size = 32 }; // static arrays FTW! - unsigned char buffer[buffer_size]; - int r = libusb_get_string_descriptor_ascii( - usb_handle, - desc_index, - buffer, - buffer_size); - - if (r <= 0) { - OLA_INFO << "libusb_get_string_descriptor_ascii returned " << r; - return false; - } - data->assign(reinterpret_cast(buffer)); - return true; -} -} // namespace - -bool LibUsbHelper::GetDeviceInfo( - struct libusb_device *usb_device, - const struct libusb_device_descriptor &device_descriptor, - DeviceInformation *device_info) { - libusb_device_handle *usb_handle; - if (!OpenDevice(usb_device, &usb_handle)) { - return false; - } - - if (!GetDescriptorString(usb_handle, device_descriptor.iManufacturer, - &device_info->manufacturer)) { - OLA_INFO << "Failed to get manufacturer name"; - } - - if (!GetDescriptorString(usb_handle, device_descriptor.iProduct, - &device_info->product)) { - OLA_INFO << "Failed to get product name"; - } - - if (!GetDescriptorString(usb_handle, device_descriptor.iSerialNumber, - &device_info->serial)) { - OLA_WARN << "Failed to read serial number, the device probably doesn't " - << "have one"; - } - - libusb_close(usb_handle); - return true; -} - -bool LibUsbHelper::CheckManufacturer(const string &expected, - const string &actual) { - if (expected != actual) { - OLA_WARN << "Manufacturer mismatch: " << expected << " != " << actual; - return false; - } - return true; -} - -bool LibUsbHelper::CheckProduct(const string &expected, const string &actual) { - if (expected != actual) { - OLA_WARN << "Product mismatch: " << expected << " != " << actual; - return false; - } - return true; -} - -bool LibUsbHelper::OpenDevice(libusb_device *usb_device, - libusb_device_handle **usb_handle) { - if (libusb_open(usb_device, usb_handle)) { - OLA_WARN << "Failed to open libusb device: " << usb_device; - return false; - } - return true; -} - -bool LibUsbHelper::OpenDeviceAndClaimInterface( - libusb_device *usb_device, - int interface, - libusb_device_handle **usb_handle) { - if (!OpenDevice(usb_device, usb_handle)) { - return false; - } - - if (libusb_claim_interface(*usb_handle, 0)) { - OLA_WARN << "Failed to claim interface " << interface - << " for libusb device: " << usb_device; - libusb_close(*usb_handle); - return false; - } - return true; -} } // namespace usbdmx } // namespace plugin } // namespace ola diff --git a/plugins/usbdmx/LibUsbHelper.h b/plugins/usbdmx/LibUsbHelper.h index b4b7c0ab1f..f53ebd9995 100644 --- a/plugins/usbdmx/LibUsbHelper.h +++ b/plugins/usbdmx/LibUsbHelper.h @@ -29,68 +29,6 @@ namespace ola { namespace plugin { namespace usbdmx { -/** - * @brief Some helper methods for device enumeration. - */ -class LibUsbHelper { - public: - struct DeviceInformation { - std::string manufacturer; - std::string product; - std::string serial; - }; - - /** - * @brief Fetch the manufacturer, product and serial strings from a device. - * @param usb_device The USB device to get information for. - * @param device_descriptor The descriptor to use - * @param[out] device_info The DeviceInformation struct to populate. - * @returns true if we fetched the information, false otherwise. - */ - static bool GetDeviceInfo( - struct libusb_device *usb_device, - const struct libusb_device_descriptor &device_descriptor, - DeviceInformation *device_info); - - /** - * @brief Check if the manufacturer string matches the expected value. - * @param expected The expected manufacturer string. - * @param actual The actual manufacturer string. - * @returns true if the strings matched, false otherwise. - */ - static bool CheckManufacturer(const std::string &expected, - const std::string &actual); - - /** - * @brief Check if the product string matches the expected value. - * @param expected The expected product string. - * @param actual The actual product string. - * @returns true if the strings matched, false otherwise. - */ - static bool CheckProduct(const std::string &expected, - const std::string &actual); - - /** - * @brief Open a libusb device. - * @param usb_device The usb device to open. - * @param[out] usb_handle the new device handle. - * @returns true if the device was opened, false otherwise. - */ - static bool OpenDevice(libusb_device *usb_device, - libusb_device_handle **usb_handle); - - /** - * @brief Open a libusb device and claim an interface. - * @param usb_device The usb device to open. - * @param interface the interface index to claim. - * @param[out] usb_handle the new device handle. - * @returns true if the device was opened and the interface claimed, - * false otherwise. - */ - static bool OpenDeviceAndClaimInterface(libusb_device *usb_device, - int interface, - libusb_device_handle **usb_handle); -}; } // namespace usbdmx } // namespace plugin } // namespace ola diff --git a/plugins/usbdmx/LibUsbThread.cpp b/plugins/usbdmx/LibUsbThread.cpp new file mode 100644 index 0000000000..6b93ef1c0c --- /dev/null +++ b/plugins/usbdmx/LibUsbThread.cpp @@ -0,0 +1,130 @@ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * 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 Library 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * AsyncPluginImpl.cpp + * The asynchronous libusb implementation. + * Copyright (C) 2014 Simon Newton + */ + +#include "plugins/usbdmx/LibUsbThread.h" + +#include "ola/Logging.h" +#include "ola/StringUtils.h" +#include "ola/stl/STLUtils.h" + + +namespace ola { +namespace plugin { +namespace usbdmx { + +// LibUsbThread +// ----------------------------------------------------------------------------- + +void *LibUsbThread::Run() { + OLA_INFO << "----libusb event thread is running"; + while (1) { + { + ola::thread::MutexLocker locker(&m_term_mutex); + if (m_term) + break; + } + libusb_handle_events(m_context); + } + OLA_INFO << "----libusb thread exiting"; + return NULL; +} + +void LibUsbThread::LaunchThread() { + Start(); +} + +void LibUsbThread::JoinThread() { + Join(); + m_term = false; +} + +// LibUsbHotplugThread +// ----------------------------------------------------------------------------- + +LibUsbHotplugThread::LibUsbHotplugThread(libusb_context *context, + libusb_hotplug_callback_fn callback_fn, + void *user_data) + : LibUsbThread(context), + m_hotplug_handle(0), + m_callback_fn(callback_fn), + m_user_data(user_data) { + OLA_INFO << "-- Starting libusb thread"; +} + +LibUsbHotplugThread::~LibUsbHotplugThread() { +} + +bool LibUsbHotplugThread::Init() { + int rc = libusb_hotplug_register_callback( + NULL, + static_cast(LIBUSB_HOTPLUG_EVENT_DEVICE_ARRIVED | + LIBUSB_HOTPLUG_EVENT_DEVICE_LEFT), + LIBUSB_HOTPLUG_ENUMERATE, LIBUSB_HOTPLUG_MATCH_ANY, + LIBUSB_HOTPLUG_MATCH_ANY, LIBUSB_HOTPLUG_MATCH_ANY, + m_callback_fn, m_user_data, &m_hotplug_handle); + + if (LIBUSB_SUCCESS != rc) { + OLA_WARN << "Error creating a hotplug callback"; + return false; + } + OLA_INFO << "libusb_hotplug_register_callback passed"; + LaunchThread(); + return true; +} + +void LibUsbHotplugThread::Shutdown() { + OLA_INFO << "-- Stopping libusb thread"; + SetTerminate(); + libusb_hotplug_deregister_callback(Context(), m_hotplug_handle); + JoinThread(); +} + +void LibUsbHotplugThread::CloseHandle(libusb_device_handle *handle) { + libusb_close(handle); +} + +// LibUsbSimpleThread +// ----------------------------------------------------------------------------- + +void LibUsbSimpleThread::OpenHandle() { + m_device_count++; + if (m_device_count == 1) { + OLA_INFO << "-- Starting libusb thread"; + LaunchThread(); + } +} + +void LibUsbSimpleThread::CloseHandle(libusb_device_handle *handle) { + OLA_INFO << "LibUsbSimpleThread::CloseHandle, count is " << m_device_count; + if (m_device_count == 1) { + SetTerminate(); + } + libusb_close(handle); + if (m_device_count == 1) { + OLA_INFO << "-- Stopping libusb thread"; + JoinThread(); + } + m_device_count--; + OLA_INFO << "exit LibUsbSimpleThread::CloseHandle, count is " + << m_device_count; +} +} // namespace usbdmx +} // namespace plugin +} // namespace ola diff --git a/plugins/usbdmx/LibUsbThread.h b/plugins/usbdmx/LibUsbThread.h new file mode 100644 index 0000000000..9bfcf4228b --- /dev/null +++ b/plugins/usbdmx/LibUsbThread.h @@ -0,0 +1,210 @@ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * 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 Library 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * AsyncPluginImpl.h + * The asynchronous libusb implementation. + * Copyright (C) 2014 Simon Newton + */ + +#ifndef PLUGINS_USBDMX_LIBUSBTHREAD_H_ +#define PLUGINS_USBDMX_LIBUSBTHREAD_H_ + +#include + +#include "ola/base/Macro.h" +#include "ola/thread/Thread.h" + +namespace ola { +namespace plugin { +namespace usbdmx { + +/** + * @brief The base class for the dedicated libusb thread. + * + * Asynchronous I/O for libusb requires either i) a dedicated thread ii) + * integration with the i/o event loop. From the libusb documentation, i) has + * the advantage that it works on Windows, so we do that. + * + * However, there is no easy way to interrupt libusb_handle_events(). Instead + * we use either libusb_close (for the non-hotplug case) or + * libusb_hotplug_deregister_callback() (for the hotplug case) to wake + * libusb_handle_events(). + * + * Both these techniques have require care to avoid deadlocks / race + * conditions. For the non-hotplug case, it's imperative that libusb_open() and + * libusb_close() are paired with calls to OpenHandle() and CloseHandle(). + * + * http://libusb.sourceforge.net/api-1.0/group__asyncio.html covers both + * approaches. + */ +class LibUsbThread : private ola::thread::Thread { + public: + /** + * @brief Base constructor + * @param context the libusb context to use. + */ + explicit LibUsbThread(libusb_context *context) + : m_context(context), + m_term(false) { + } + + /** + * @brief Destructor. + */ + virtual ~LibUsbThread() {} + + /** + * @brief Initialize the thread. + */ + virtual bool Init() { + return true; + } + + /** + * @brief Shutdown the thread. + */ + virtual void Shutdown() {} + + /** + * @brief The entry point to the libusb thread. + * + * Don't call this directly. It's executed when the thread starts. + */ + void *Run(); + + /** + * @brief This must be called whenever libusb_open() is called. + */ + virtual void OpenHandle() = 0; + + /** + * @brief This must be called whenever libusb_close() is called. + */ + virtual void CloseHandle(libusb_device_handle *handle) = 0; + + protected: + /** + * @brief Indicate that the libusb thread should terminate. + * + * This doesn't wake up libusb_handle_events(), it simply sets m_term to + * true. + */ + void SetTerminate() { + ola::thread::MutexLocker locker(&m_term_mutex); + m_term = true; + } + + /** + * @brief Start the libusb thread. + */ + void LaunchThread(); + + /** + * @brief Join the libusb thread. + */ + void JoinThread(); + + /** + * @brief Return the libusb_context this thread uses. + * @returns A libusb_context. + */ + libusb_context* Context() const { return m_context; } + + private: + libusb_context *m_context; + bool m_term; // GUARDED_BY(m_term_mutex) + ola::thread::Mutex m_term_mutex; +}; + +#if defined(LIBUSB_API_VERSION) && (LIBUSB_API_VERSION >= 0x01000102) + +/** + * @brief The hotplug version of the LibUsbThread. + */ +class LibUsbHotplugThread : public LibUsbThread { + public: + /** + * @brief Create a new LibUsbHotplugThread.o + * @param context the libusb context to use. + * @param callback_fn The callback function to run when hotplug events occur. + * @param user_data User data to pass to the callback function. + * + * The thread is started in Init(). When the object is + * destroyed, the handle is de-registered as part of the thread shutdown + * sequence. + */ + LibUsbHotplugThread(libusb_context *context, + libusb_hotplug_callback_fn callback_fn, + void *user_data); + + /** + * @brief Destructor. + */ + ~LibUsbHotplugThread(); + + bool Init(); + + void Shutdown(); + + void OpenHandle() {} + + void CloseHandle(libusb_device_handle *handle); + + private: + libusb_hotplug_callback_handle m_hotplug_handle; + libusb_hotplug_callback_fn m_callback_fn; + void *m_user_data; + + DISALLOW_COPY_AND_ASSIGN(LibUsbHotplugThread); +}; + +#endif + +/** + * @brief The non-hotplug version of LibUsbThread. + * + * The libusb thread is only run when one of more handles are open. Otherwise + * there is no way to interrupt libusb_handle_events(). See the libusb Async + * documentation at http://libusb.sourceforge.net/api-1.0/group__asyncio.html + * for more information. + */ +class LibUsbSimpleThread : public LibUsbThread { + public: + /** + * @brief Create a new LibUsbHotplugThread.o + * @param context the libusb context to use. + * @param handle the callback handle for hotplug events. + * + * The thread is starts as soon as this object is created. When the object is + * destroyed, the handle is de-registered as part of the thread shutdown + * sequence. + */ + explicit LibUsbSimpleThread(libusb_context *context) + : LibUsbThread(context), + m_device_count(0) { + } + + void OpenHandle(); + void CloseHandle(libusb_device_handle *handle); + + private: + unsigned int m_device_count; + + DISALLOW_COPY_AND_ASSIGN(LibUsbSimpleThread); +}; +} // namespace usbdmx +} // namespace plugin +} // namespace ola +#endif // PLUGINS_USBDMX_LIBUSBTHREAD_H_ diff --git a/plugins/usbdmx/Makefile.mk b/plugins/usbdmx/Makefile.mk index ea928f9b11..2928365f39 100644 --- a/plugins/usbdmx/Makefile.mk +++ b/plugins/usbdmx/Makefile.mk @@ -20,8 +20,8 @@ plugins_usbdmx_libolausbdmx_la_SOURCES = \ plugins/usbdmx/GenericOutputPort.h \ plugins/usbdmx/LibUsbAdaptor.cpp \ plugins/usbdmx/LibUsbAdaptor.h \ - plugins/usbdmx/LibUsbHelper.cpp \ - plugins/usbdmx/LibUsbHelper.h \ + plugins/usbdmx/LibUsbThread.cpp \ + plugins/usbdmx/LibUsbThread.h \ plugins/usbdmx/PluginImplInterface.h \ plugins/usbdmx/SunliteFirmware.h \ plugins/usbdmx/SunliteFirmwareLoader.cpp \ diff --git a/plugins/usbdmx/SunliteWidget.cpp b/plugins/usbdmx/SunliteWidget.cpp index 2d1e8f695c..b58441bbc6 100644 --- a/plugins/usbdmx/SunliteWidget.cpp +++ b/plugins/usbdmx/SunliteWidget.cpp @@ -22,7 +22,7 @@ #include "ola/Constants.h" #include "ola/Logging.h" -#include "plugins/usbdmx/LibUsbHelper.h" +#include "plugins/usbdmx/LibUsbAdaptor.h" #include "plugins/usbdmx/ThreadedUsbSender.h" namespace ola { @@ -133,14 +133,16 @@ bool SunliteThreadedSender::TransmitBuffer(libusb_device_handle *handle, } -SynchronousSunliteWidget::SynchronousSunliteWidget(libusb_device *usb_device) - : m_usb_device(usb_device) { +SynchronousSunliteWidget::SynchronousSunliteWidget(LibUsbAdaptor *adaptor, + libusb_device *usb_device) + : SunliteWidget(adaptor), + m_usb_device(usb_device) { } bool SynchronousSunliteWidget::Init() { libusb_device_handle *usb_handle; - bool ok = LibUsbHelper::OpenDeviceAndClaimInterface( + bool ok = m_adaptor->OpenDeviceAndClaimInterface( m_usb_device, 0, &usb_handle); if (!ok) { return false; @@ -160,8 +162,10 @@ bool SynchronousSunliteWidget::SendDMX(const DmxBuffer &buffer) { } AsynchronousSunliteWidget::AsynchronousSunliteWidget( + LibUsbAdaptor *adaptor, libusb_device *usb_device) - : m_usb_device(usb_device), + : SunliteWidget(adaptor), + m_usb_device(usb_device), m_usb_handle(NULL), m_transfer_state(IDLE) { InitPacket(m_packet); @@ -189,7 +193,7 @@ AsynchronousSunliteWidget::~AsynchronousSunliteWidget() { } bool AsynchronousSunliteWidget::Init() { - bool ok = LibUsbHelper::OpenDeviceAndClaimInterface( + bool ok = m_adaptor->OpenDeviceAndClaimInterface( m_usb_device, 0, &m_usb_handle); if (!ok) { return false; diff --git a/plugins/usbdmx/SunliteWidget.h b/plugins/usbdmx/SunliteWidget.h index ec72742a95..ddfd2bedca 100644 --- a/plugins/usbdmx/SunliteWidget.h +++ b/plugins/usbdmx/SunliteWidget.h @@ -37,8 +37,12 @@ class SunliteThreadedSender; /** * @brief The interface for the Sunlite Widgets */ -class SunliteWidget : public Widget { +class SunliteWidget : public BaseWidget { public: + explicit SunliteWidget(LibUsbAdaptor *adaptor) + : BaseWidget(adaptor) { + } + /** * @brief The size of a Sunlite frame. */ @@ -55,9 +59,11 @@ class SynchronousSunliteWidget: public SunliteWidget { public: /** * @brief Create a new SynchronousSunliteWidget. + * @param adaptor the LibUsbAdaptor to use. * @param usb_device the libusb_device to use for the widget. */ - explicit SynchronousSunliteWidget(libusb_device *usb_device); + SynchronousSunliteWidget(LibUsbAdaptor *adaptor, + libusb_device *usb_device); bool Init(); @@ -77,9 +83,11 @@ class AsynchronousSunliteWidget: public SunliteWidget { public: /** * @brief Create a new AsynchronousSunliteWidget. + * @param adaptor the LibUsbAdaptor to use. * @param usb_device the libusb_device to use for the widget. */ - explicit AsynchronousSunliteWidget(libusb_device *usb_device); + AsynchronousSunliteWidget(LibUsbAdaptor *adaptor, + libusb_device *usb_device); ~AsynchronousSunliteWidget(); bool Init(); diff --git a/plugins/usbdmx/SunliteWidgetFactory.cpp b/plugins/usbdmx/SunliteWidgetFactory.cpp index be676f76cb..257f274deb 100644 --- a/plugins/usbdmx/SunliteWidgetFactory.cpp +++ b/plugins/usbdmx/SunliteWidgetFactory.cpp @@ -21,8 +21,11 @@ #include "plugins/usbdmx/SunliteWidgetFactory.h" #include "ola/Logging.h" +#include "ola/base/Flags.h" #include "plugins/usbdmx/SunliteFirmwareLoader.h" +DECLARE_bool(use_async_libusb); + namespace ola { namespace plugin { namespace usbdmx { @@ -47,8 +50,13 @@ bool SunliteWidgetFactory::DeviceAdded( !HasDevice(usb_device)) { OLA_INFO << "Found a new Sunlite device"; - return AddWidget(observer, usb_device, - new AsynchronousSunliteWidget(usb_device)); + SunliteWidget *widget = NULL; + if (FLAGS_use_async_libusb) { + widget = new AsynchronousSunliteWidget(m_adaptor, usb_device); + } else { + widget = new SynchronousSunliteWidget(m_adaptor, usb_device); + } + return AddWidget(observer, usb_device, widget); } return false; } diff --git a/plugins/usbdmx/SunliteWidgetFactory.h b/plugins/usbdmx/SunliteWidgetFactory.h index 73e2d33bc3..5e68be83ab 100644 --- a/plugins/usbdmx/SunliteWidgetFactory.h +++ b/plugins/usbdmx/SunliteWidgetFactory.h @@ -34,7 +34,8 @@ namespace usbdmx { */ class SunliteWidgetFactory : public BaseWidgetFactory { public: - SunliteWidgetFactory() {} + explicit SunliteWidgetFactory(class LibUsbAdaptor *adaptor) + : m_adaptor(adaptor) {} bool DeviceAdded( WidgetObserver *observer, @@ -45,6 +46,8 @@ class SunliteWidgetFactory : public BaseWidgetFactory { libusb_device *device); private: + class LibUsbAdaptor* const m_adaptor; + // The product ID for widgets that are missing their firmware. static const uint16_t EMPTY_PRODUCT_ID; // The product ID for widgets with the firmware. diff --git a/plugins/usbdmx/SyncPluginImpl.cpp b/plugins/usbdmx/SyncPluginImpl.cpp index 87a2fb97c6..13876e946d 100644 --- a/plugins/usbdmx/SyncPluginImpl.cpp +++ b/plugins/usbdmx/SyncPluginImpl.cpp @@ -54,10 +54,10 @@ using std::vector; SyncPluginImpl::SyncPluginImpl(PluginAdaptor *plugin_adaptor, Plugin *plugin, - LibUsbAdaptor *libusb_adaptor) + unsigned int debug_level) : m_plugin_adaptor(plugin_adaptor), m_plugin(plugin), - m_libusb_adaptor(libusb_adaptor), + m_debug_level(debug_level), m_context(NULL), m_anyma_devices_missing_serial_numbers(false) { } @@ -68,7 +68,8 @@ bool SyncPluginImpl::Start() { return false; } - m_libusb_adaptor->SetDebug(m_context); + OLA_DEBUG << "libusb debug level set to " << m_debug_level; + libusb_set_debug(m_context, m_debug_level); if (LoadFirmware()) { // we loaded firmware for at least one device, set up a callback to run in @@ -159,7 +160,7 @@ void SyncPluginImpl::CheckDevice(libusb_device *usb_device) { device_descriptor.idProduct == 0x8062) { OLA_INFO << "Found a Velleman USB device"; SynchronousVellemanWidget *widget = new SynchronousVellemanWidget( - usb_device); + &m_usb_adaptor, usb_device); if (!widget->Init()) { delete widget; return; @@ -170,7 +171,8 @@ void SyncPluginImpl::CheckDevice(libusb_device *usb_device) { } else if (device_descriptor.idVendor == 0x0962 && device_descriptor.idProduct == 0x2001) { OLA_INFO << "Found a Sunlite device"; - SynchronousSunliteWidget *widget = new SynchronousSunliteWidget(usb_device); + SynchronousSunliteWidget *widget = new SynchronousSunliteWidget( + &m_usb_adaptor, usb_device); if (!widget->Init()) { delete widget; return; @@ -240,7 +242,7 @@ Device* SyncPluginImpl::NewAnymaDevice( } SynchronousAnymaWidget *widget = new SynchronousAnymaWidget( - usb_device, info.serial); + &m_usb_adaptor, usb_device, info.serial); if (!widget->Init()) { delete widget; return NULL; @@ -263,7 +265,7 @@ Device* SyncPluginImpl::NewEuroliteProDevice( serial_str << bus_number << "-" << device_address; SynchronousEuroliteProWidget *widget = new SynchronousEuroliteProWidget( - usb_device, serial_str.str()); + &m_usb_adaptor, usb_device, serial_str.str()); if (!widget->Init()) { delete widget; return NULL; diff --git a/plugins/usbdmx/SyncPluginImpl.h b/plugins/usbdmx/SyncPluginImpl.h index f86194ca9a..8b5f38fe00 100644 --- a/plugins/usbdmx/SyncPluginImpl.h +++ b/plugins/usbdmx/SyncPluginImpl.h @@ -27,9 +27,8 @@ #include #include #include "ola/base/Macro.h" -#include "plugins/usbdmx/PluginImplInterface.h" #include "plugins/usbdmx/LibUsbAdaptor.h" - +#include "plugins/usbdmx/PluginImplInterface.h" namespace ola { @@ -54,11 +53,11 @@ class SyncPluginImpl: public PluginImplInterface { * transferred. * @param plugin The parent Plugin object which is used when creating * devices. - * @param libusb_adaptor The adaptor to use when calling libusb. + * @param debug_level the debug level to use for libusb. */ SyncPluginImpl(PluginAdaptor *plugin_adaptor, Plugin *plugin, - LibUsbAdaptor *libusb_adaptor); + unsigned int debug_level); bool Start(); bool Stop(); @@ -70,9 +69,10 @@ class SyncPluginImpl: public PluginImplInterface { std::string serial; }; - PluginAdaptor *m_plugin_adaptor; - Plugin *m_plugin; - LibUsbAdaptor *m_libusb_adaptor; + PluginAdaptor* const m_plugin_adaptor; + Plugin* const m_plugin; + const unsigned int m_debug_level; + SyncronousLibUsbAdaptor m_usb_adaptor; libusb_context *m_context; diff --git a/plugins/usbdmx/UsbDmxPlugin.cpp b/plugins/usbdmx/UsbDmxPlugin.cpp index f64706c32b..a1609cfc10 100644 --- a/plugins/usbdmx/UsbDmxPlugin.cpp +++ b/plugins/usbdmx/UsbDmxPlugin.cpp @@ -14,7 +14,7 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * * UsbDmxPlugin.cpp - * The UsbDmx plugin for OLA. + * A plugin that uses libusb to communicate with USB devices. * Copyright (C) 2006 Simon Newton */ @@ -26,7 +26,6 @@ #include "ola/base/Flags.h" #include "olad/Preferences.h" #include "plugins/usbdmx/AsyncPluginImpl.h" -#include "plugins/usbdmx/LibUsbAdaptor.h" #include "plugins/usbdmx/PluginImplInterface.h" #include "plugins/usbdmx/SyncPluginImpl.h" @@ -63,18 +62,16 @@ bool UsbDmxPlugin::StartHook() { debug_level = LIBUSB_DEFAULT_DEBUG_LEVEL; } - std::auto_ptr libusb_adaptor(new LibUsbAdaptor(debug_level)); std::auto_ptr impl; if (FLAGS_use_async_libusb) { impl.reset( - new AsyncPluginImpl(m_plugin_adaptor, this, libusb_adaptor.get())); + new AsyncPluginImpl(m_plugin_adaptor, this, debug_level)); } else { impl.reset( - new SyncPluginImpl(m_plugin_adaptor, this, libusb_adaptor.get())); + new SyncPluginImpl(m_plugin_adaptor, this, debug_level)); } if (impl->Start()) { - m_libusb_adaptor.reset(libusb_adaptor.release()); m_impl.reset(impl.release()); return true; } else { diff --git a/plugins/usbdmx/UsbDmxPlugin.h b/plugins/usbdmx/UsbDmxPlugin.h index 46fd9a23c3..84b289f251 100644 --- a/plugins/usbdmx/UsbDmxPlugin.h +++ b/plugins/usbdmx/UsbDmxPlugin.h @@ -14,7 +14,7 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * * UsbDmxPlugin.h - * Interface for the usbdmx plugin class + * A plugin that uses libusb to communicate with USB devices. * Copyright (C) 2010 Simon Newton */ @@ -32,9 +32,9 @@ namespace plugin { namespace usbdmx { /** - * @brief A plugin which uses libusb to comunicate with devices. + * @brief A plugin that uses libusb to communicate with USB devices. * - * This Plugin supports a number of USB dongles including + * This plugin supports a number of USB dongles including * - Anyma * - Eurolite * - Sunlite @@ -56,7 +56,6 @@ class UsbDmxPlugin: public ola::Plugin { std::string PluginPrefix() const { return PLUGIN_PREFIX; } private: - std::auto_ptr m_libusb_adaptor; std::auto_ptr m_impl; bool StartHook(); diff --git a/plugins/usbdmx/VellemanWidget.cpp b/plugins/usbdmx/VellemanWidget.cpp index 0666239817..dfb638669e 100644 --- a/plugins/usbdmx/VellemanWidget.cpp +++ b/plugins/usbdmx/VellemanWidget.cpp @@ -26,7 +26,7 @@ #include "ola/Logging.h" #include "ola/Constants.h" -#include "plugins/usbdmx/LibUsbHelper.h" +#include "plugins/usbdmx/LibUsbAdaptor.h" #include "plugins/usbdmx/ThreadedUsbSender.h" namespace ola { @@ -179,14 +179,16 @@ bool VellemanThreadedSender::SendDataChunk(libusb_device_handle *handle, SynchronousVellemanWidget::SynchronousVellemanWidget( + LibUsbAdaptor *adaptor, libusb_device *usb_device) - : m_usb_device(usb_device) { + : VellemanWidget(adaptor), + m_usb_device(usb_device) { } bool SynchronousVellemanWidget::Init() { libusb_device_handle *usb_handle; - bool ok = LibUsbHelper::OpenDevice(m_usb_device, &usb_handle); + bool ok = m_adaptor->OpenDevice(m_usb_device, &usb_handle); if (!ok) { return false; } @@ -253,8 +255,9 @@ bool SynchronousVellemanWidget::SendDMX(const DmxBuffer &buffer) { AsynchronousVellemanWidget::AsynchronousVellemanWidget( + LibUsbAdaptor *adaptor, libusb_device *usb_device) - : VellemanWidget(), + : VellemanWidget(adaptor), m_usb_device(usb_device), m_usb_handle(NULL), m_control_setup_buffer(NULL), @@ -287,7 +290,7 @@ AsynchronousVellemanWidget::~AsynchronousVellemanWidget() { } bool AsynchronousVellemanWidget::Init() { - bool ok = LibUsbHelper::OpenDeviceAndClaimInterface( + bool ok = m_adaptor->OpenDeviceAndClaimInterface( m_usb_device, 0, &m_usb_handle); if (!ok) { return false; diff --git a/plugins/usbdmx/VellemanWidget.h b/plugins/usbdmx/VellemanWidget.h index ba1d880f5e..c1a75a1b99 100644 --- a/plugins/usbdmx/VellemanWidget.h +++ b/plugins/usbdmx/VellemanWidget.h @@ -37,7 +37,12 @@ namespace usbdmx { /** * @brief The interface for the Velleman Widgets */ -class VellemanWidget: public Widget {}; +class VellemanWidget: public BaseWidget { + public: + explicit VellemanWidget(LibUsbAdaptor *adaptor) + : BaseWidget(adaptor) { + } +}; /** * @brief An Velleman widget that uses synchronous libusb operations. @@ -48,9 +53,11 @@ class SynchronousVellemanWidget: public VellemanWidget { public: /** * @brief Create a new SynchronousVellemanWidget. + * @param adaptor the LibUsbAdaptor to use. * @param usb_device the libusb_device to use for the widget. */ - explicit SynchronousVellemanWidget(libusb_device *usb_device); + SynchronousVellemanWidget(LibUsbAdaptor *adaptor, + libusb_device *usb_device); bool Init(); @@ -70,9 +77,11 @@ class AsynchronousVellemanWidget : public VellemanWidget { public: /** * @brief Create a new AsynchronousVellemanWidget. + * @param adaptor the LibUsbAdaptor to use. * @param usb_device the libusb_device to use for the widget. */ - explicit AsynchronousVellemanWidget(libusb_device *usb_device); + AsynchronousVellemanWidget(LibUsbAdaptor *adaptor, + libusb_device *usb_device); ~AsynchronousVellemanWidget(); bool Init(); diff --git a/plugins/usbdmx/VellemanWidgetFactory.cpp b/plugins/usbdmx/VellemanWidgetFactory.cpp index 2de634cb58..44967915c6 100644 --- a/plugins/usbdmx/VellemanWidgetFactory.cpp +++ b/plugins/usbdmx/VellemanWidgetFactory.cpp @@ -21,9 +21,12 @@ #include "plugins/usbdmx/VellemanWidgetFactory.h" #include "ola/Logging.h" +#include "ola/base/Flags.h" #include "plugins/usbdmx/VellemanWidget.h" #include "plugins/usbdmx/LibUsbHelper.h" +DECLARE_bool(use_async_libusb); + namespace ola { namespace plugin { namespace usbdmx { @@ -41,8 +44,14 @@ bool VellemanWidgetFactory::DeviceAdded( } OLA_INFO << "Found a new Velleman device"; - return AddWidget(observer, usb_device, - new AsynchronousVellemanWidget(usb_device)); + VellemanWidget *widget = NULL; + if (FLAGS_use_async_libusb) { + widget = new AsynchronousVellemanWidget(m_adaptor, usb_device); + + } else { + widget = new SynchronousVellemanWidget(m_adaptor, usb_device); + } + return AddWidget(observer, usb_device, widget); } } // namespace usbdmx } // namespace plugin diff --git a/plugins/usbdmx/VellemanWidgetFactory.h b/plugins/usbdmx/VellemanWidgetFactory.h index c6b40772c6..0bd008e910 100644 --- a/plugins/usbdmx/VellemanWidgetFactory.h +++ b/plugins/usbdmx/VellemanWidgetFactory.h @@ -33,7 +33,9 @@ namespace usbdmx { */ class VellemanWidgetFactory : public BaseWidgetFactory { public: - VellemanWidgetFactory() {} + explicit VellemanWidgetFactory(class LibUsbAdaptor *adaptor) + : m_adaptor(adaptor) { + } bool DeviceAdded( WidgetObserver *observer, @@ -41,6 +43,8 @@ class VellemanWidgetFactory : public BaseWidgetFactory { const struct libusb_device_descriptor &descriptor); private: + class LibUsbAdaptor* const m_adaptor; + static const uint16_t VENDOR_ID; static const uint16_t PRODUCT_ID; diff --git a/plugins/usbdmx/Widget.h b/plugins/usbdmx/Widget.h index ab30f9b32c..95ffabb9b0 100644 --- a/plugins/usbdmx/Widget.h +++ b/plugins/usbdmx/Widget.h @@ -47,6 +47,26 @@ class Widget { */ virtual bool SendDMX(const DmxBuffer &buffer) = 0; }; + +/** + * @brief A base widget class. + * + * This holds a pointer to a LibUsbAdaptor so we don't have to duplicate that + * code in all the Widgets. + */ +class BaseWidget : public Widget { + public: + /** + * @brief Create a new BaseWidget. + * @param adaptor the LibUsbAdaptor to use. + */ + explicit BaseWidget(class LibUsbAdaptor *adaptor) + : m_adaptor(adaptor) { + } + + protected: + class LibUsbAdaptor* const m_adaptor; +}; } // namespace usbdmx } // namespace plugin } // namespace ola From 16e45cb88692a18d505e07a5adfb5311467cc0a4 Mon Sep 17 00:00:00 2001 From: Simon Newton Date: Sun, 16 Nov 2014 12:41:35 -0800 Subject: [PATCH 14/34] Fix the build. --- plugins/usbdmx/VellemanWidgetFactory.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/plugins/usbdmx/VellemanWidgetFactory.cpp b/plugins/usbdmx/VellemanWidgetFactory.cpp index 44967915c6..3cedb4f158 100644 --- a/plugins/usbdmx/VellemanWidgetFactory.cpp +++ b/plugins/usbdmx/VellemanWidgetFactory.cpp @@ -23,7 +23,6 @@ #include "ola/Logging.h" #include "ola/base/Flags.h" #include "plugins/usbdmx/VellemanWidget.h" -#include "plugins/usbdmx/LibUsbHelper.h" DECLARE_bool(use_async_libusb); From 4f59bd0244813eb8eab72a32f4f7456c24445405 Mon Sep 17 00:00:00 2001 From: Simon Newton Date: Sun, 16 Nov 2014 12:47:48 -0800 Subject: [PATCH 15/34] Fix the build on platforms without hotplug support. --- plugins/usbdmx/AsyncPluginImpl.cpp | 6 ++++++ plugins/usbdmx/EuroliteProWidget.cpp | 1 + plugins/usbdmx/LibUsbThread.cpp | 3 +++ plugins/usbdmx/SunliteWidget.cpp | 2 ++ plugins/usbdmx/VellemanWidget.cpp | 1 + 5 files changed, 13 insertions(+) diff --git a/plugins/usbdmx/AsyncPluginImpl.cpp b/plugins/usbdmx/AsyncPluginImpl.cpp index 1d2c3fd320..f036188dde 100644 --- a/plugins/usbdmx/AsyncPluginImpl.cpp +++ b/plugins/usbdmx/AsyncPluginImpl.cpp @@ -90,8 +90,14 @@ bool AsyncPluginImpl::Start() { m_use_hotplug = HotplugSupported(); OLA_INFO << "HotplugSupported returned " << m_use_hotplug; if (m_use_hotplug) { +#ifdef OLA_LIBUSB_HAS_HOTPLUG_API m_usb_thread.reset(new LibUsbHotplugThread( m_context, hotplug_callback, this)); +#else + OLA_FATAL << "Mismatch between m_use_hotplug and " + " OLA_LIBUSB_HAS_HOTPLUG_API"; + return false; +#endif } else { m_usb_thread.reset(new LibUsbSimpleThread(m_context)); } diff --git a/plugins/usbdmx/EuroliteProWidget.cpp b/plugins/usbdmx/EuroliteProWidget.cpp index 810f09c47b..f07ca44cf6 100644 --- a/plugins/usbdmx/EuroliteProWidget.cpp +++ b/plugins/usbdmx/EuroliteProWidget.cpp @@ -20,6 +20,7 @@ #include "plugins/usbdmx/EuroliteProWidget.h" +#include #include #include "ola/Constants.h" diff --git a/plugins/usbdmx/LibUsbThread.cpp b/plugins/usbdmx/LibUsbThread.cpp index 6b93ef1c0c..c384f5cb1c 100644 --- a/plugins/usbdmx/LibUsbThread.cpp +++ b/plugins/usbdmx/LibUsbThread.cpp @@ -58,6 +58,7 @@ void LibUsbThread::JoinThread() { // LibUsbHotplugThread // ----------------------------------------------------------------------------- +#if defined(LIBUSB_API_VERSION) && (LIBUSB_API_VERSION >= 0x01000102) LibUsbHotplugThread::LibUsbHotplugThread(libusb_context *context, libusb_hotplug_callback_fn callback_fn, void *user_data) @@ -100,6 +101,8 @@ void LibUsbHotplugThread::CloseHandle(libusb_device_handle *handle) { libusb_close(handle); } +#endif + // LibUsbSimpleThread // ----------------------------------------------------------------------------- diff --git a/plugins/usbdmx/SunliteWidget.cpp b/plugins/usbdmx/SunliteWidget.cpp index b58441bbc6..467ff00eba 100644 --- a/plugins/usbdmx/SunliteWidget.cpp +++ b/plugins/usbdmx/SunliteWidget.cpp @@ -20,6 +20,8 @@ #include "plugins/usbdmx/SunliteWidget.h" +#include + #include "ola/Constants.h" #include "ola/Logging.h" #include "plugins/usbdmx/LibUsbAdaptor.h" diff --git a/plugins/usbdmx/VellemanWidget.cpp b/plugins/usbdmx/VellemanWidget.cpp index dfb638669e..c76f420c7b 100644 --- a/plugins/usbdmx/VellemanWidget.cpp +++ b/plugins/usbdmx/VellemanWidget.cpp @@ -20,6 +20,7 @@ #include "plugins/usbdmx/VellemanWidget.h" +#include #include #include #include From 3d3d2bbd9fb29df15b8292c49540a6fab9bc9487 Mon Sep 17 00:00:00 2001 From: Simon Newton Date: Sun, 16 Nov 2014 14:39:18 -0800 Subject: [PATCH 16/34] Switch the Sync plugin to use the widget factories as well. --- plugins/usbdmx/SyncPluginImpl.cpp | 305 +++++++----------------------- plugins/usbdmx/SyncPluginImpl.h | 53 +++--- plugins/usbdmx/UsbDmxPlugin.cpp | 2 +- 3 files changed, 98 insertions(+), 262 deletions(-) diff --git a/plugins/usbdmx/SyncPluginImpl.cpp b/plugins/usbdmx/SyncPluginImpl.cpp index 13876e946d..f92f472860 100644 --- a/plugins/usbdmx/SyncPluginImpl.cpp +++ b/plugins/usbdmx/SyncPluginImpl.cpp @@ -30,24 +30,23 @@ #include "ola/Callback.h" #include "ola/Logging.h" -#include "ola/StringUtils.h" #include "ola/stl/STLUtils.h" #include "olad/PluginAdaptor.h" #include "plugins/usbdmx/AnymaWidget.h" +#include "plugins/usbdmx/AnymaWidgetFactory.h" #include "plugins/usbdmx/EuroliteProWidget.h" -#include "plugins/usbdmx/FirmwareLoader.h" +#include "plugins/usbdmx/EuroliteProWidgetFactory.h" #include "plugins/usbdmx/GenericDevice.h" -#include "plugins/usbdmx/SunliteFirmwareLoader.h" #include "plugins/usbdmx/SunliteWidget.h" +#include "plugins/usbdmx/SunliteWidgetFactory.h" #include "plugins/usbdmx/VellemanWidget.h" - +#include "plugins/usbdmx/VellemanWidgetFactory.h" namespace ola { namespace plugin { namespace usbdmx { -using ola::io::DeviceDescriptor; using std::pair; using std::string; using std::vector; @@ -58,8 +57,15 @@ SyncPluginImpl::SyncPluginImpl(PluginAdaptor *plugin_adaptor, : m_plugin_adaptor(plugin_adaptor), m_plugin(plugin), m_debug_level(debug_level), - m_context(NULL), - m_anyma_devices_missing_serial_numbers(false) { + m_context(NULL) { + m_widget_factories.push_back(new AnymaWidgetFactory(&m_usb_adaptor)); + m_widget_factories.push_back(new EuroliteProWidgetFactory(&m_usb_adaptor)); + m_widget_factories.push_back(new SunliteWidgetFactory(&m_usb_adaptor)); + m_widget_factories.push_back(new VellemanWidgetFactory(&m_usb_adaptor)); +} + +SyncPluginImpl::~SyncPluginImpl() { + STLDeleteElements(&m_widget_factories); } bool SyncPluginImpl::Start() { @@ -71,23 +77,25 @@ bool SyncPluginImpl::Start() { OLA_DEBUG << "libusb debug level set to " << m_debug_level; libusb_set_debug(m_context, m_debug_level); - if (LoadFirmware()) { - // we loaded firmware for at least one device, set up a callback to run in - // a couple of seconds to re-scan for devices + unsigned int devices_claimed = ScanForDevices(); + if (devices_claimed != m_devices.size()) { + // This indicates there is firmware loading going on, schedule a callback + // to check for 'new' devices once the firmware has loaded. + m_plugin_adaptor->RegisterSingleTimeout( 3500, - NewSingleCallback(this, &SyncPluginImpl::FindDevices)); + NewSingleCallback(this, &SyncPluginImpl::ReScanForDevices)); } - FindDevices(); return true; } bool SyncPluginImpl::Stop() { - vector::iterator iter; + WidgetToDeviceMap::iterator iter; for (iter = m_devices.begin(); iter != m_devices.end(); ++iter) { - m_plugin_adaptor->UnregisterDevice(*iter); - (*iter)->Stop(); - delete *iter; + m_plugin_adaptor->UnregisterDevice(iter->second); + iter->second->Stop(); + delete iter->second; + delete iter->first; } m_devices.clear(); m_registered_devices.clear(); @@ -97,257 +105,88 @@ bool SyncPluginImpl::Stop() { return true; } +bool SyncPluginImpl::NewWidget(class AnymaWidget *widget) { + return StartAndRegisterDevice( + widget, + new GenericDevice(m_plugin, widget, "Anyma USB Device", + "anyma-" + widget->SerialNumber())); +} +bool SyncPluginImpl::NewWidget(class EuroliteProWidget *widget) { + return StartAndRegisterDevice( + widget, + new GenericDevice(m_plugin, widget, "EurolitePro USB Device", + "eurolite-" + widget->SerialNumber())); +} -/* - * Load firmware onto devices if required. - * @returns true if we loaded firmware for one or more devices - */ -bool SyncPluginImpl::LoadFirmware() { - libusb_device **device_list; - size_t device_count = libusb_get_device_list(m_context, &device_list); - FirmwareLoader *loader; - bool loaded = false; - - for (unsigned int i = 0; i < device_count; i++) { - libusb_device *usb_device = device_list[i]; - loader = NULL; - struct libusb_device_descriptor device_descriptor; - libusb_get_device_descriptor(usb_device, &device_descriptor); - - if (device_descriptor.idVendor == 0x0962 && - device_descriptor.idProduct == 0x2000) { - loader = new SunliteFirmwareLoader(usb_device); - } - - if (loader) { - loader->LoadFirmware(); - loaded = true; - delete loader; - } - } - libusb_free_device_list(device_list, 1); // unref devices - return loaded; +bool SyncPluginImpl::NewWidget(class SunliteWidget *widget) { + return StartAndRegisterDevice( + widget, + new GenericDevice(m_plugin, widget, "Sunlite USBDMX2 Device", "usbdmx2")); } +bool SyncPluginImpl::NewWidget(class VellemanWidget *widget) { + return StartAndRegisterDevice( + widget, + new GenericDevice(m_plugin, widget, "Velleman USB Device", "velleman")); +} /* - * Find known devices & register them + * @brief Look for USB devices. */ -void SyncPluginImpl::FindDevices() { +unsigned int SyncPluginImpl::ScanForDevices() { libusb_device **device_list; size_t device_count = libusb_get_device_list(m_context, &device_list); + unsigned int claimed_device_count = 0; for (unsigned int i = 0; i < device_count; i++) { - CheckDevice(device_list[i]); + if (CheckDevice(device_list[i])) { + claimed_device_count++; + } } libusb_free_device_list(device_list, 1); // unref devices + return claimed_device_count; } -void SyncPluginImpl::CheckDevice(libusb_device *usb_device) { +bool SyncPluginImpl::CheckDevice(libusb_device *usb_device) { struct libusb_device_descriptor device_descriptor; libusb_get_device_descriptor(usb_device, &device_descriptor); - Device *device = NULL; pair bus_dev_id(libusb_get_bus_number(usb_device), libusb_get_device_address(usb_device)); if (STLContains(m_registered_devices, bus_dev_id)) { - return; - } - - if (device_descriptor.idVendor == 0x10cf && - device_descriptor.idProduct == 0x8062) { - OLA_INFO << "Found a Velleman USB device"; - SynchronousVellemanWidget *widget = new SynchronousVellemanWidget( - &m_usb_adaptor, usb_device); - if (!widget->Init()) { - delete widget; - return; - } - device = new GenericDevice( - m_plugin, widget, "Velleman USB Device", "velleman"); - - } else if (device_descriptor.idVendor == 0x0962 && - device_descriptor.idProduct == 0x2001) { - OLA_INFO << "Found a Sunlite device"; - SynchronousSunliteWidget *widget = new SynchronousSunliteWidget( - &m_usb_adaptor, usb_device); - if (!widget->Init()) { - delete widget; - return; - } - device = new GenericDevice( - m_plugin, widget, "Sunlite USBDMX2 Device", "usbdmx2"); - - } else if (device_descriptor.idVendor == 0x16C0 && - device_descriptor.idProduct == 0x05DC) { - OLA_INFO << "Found an Anyma device"; - device = NewAnymaDevice(usb_device, device_descriptor); - } else if (device_descriptor.idVendor == 0x04d8 && - device_descriptor.idProduct == 0xfa63) { - OLA_INFO << "Found a EUROLITE device"; - device = NewEuroliteProDevice(usb_device); - } - - if (device) { - if (!device->Start()) { - delete device; - return; - } - m_registered_devices.insert(bus_dev_id); - m_devices.push_back(device); - m_plugin_adaptor->RegisterDevice(device); - } -} - -/** - * Create a new AnymaDevice. Some Anyma devices don't have serial numbers, so - * we can only support one of those. - */ -Device* SyncPluginImpl::NewAnymaDevice( - libusb_device *usb_device, - const struct libusb_device_descriptor &device_descriptor) { - libusb_device_handle *usb_handle; - if (libusb_open(usb_device, &usb_handle)) { - OLA_WARN << "Failed to open Anyma usb device"; - return NULL; - } - - USBDeviceInformation info; - GetDeviceInfo(usb_handle, device_descriptor, &info); - - if (!MatchManufacturer(AnymaWidget::EXPECTED_MANUFACTURER, - info.manufacturer)) { - libusb_close(usb_handle); - return NULL; - } - - if (!MatchProduct(AnymaWidget::EXPECTED_PRODUCT, info.product)) { - libusb_close(usb_handle); - return NULL; + return false; } - if (info.serial.empty()) { - if (m_anyma_devices_missing_serial_numbers) { - OLA_WARN << "Failed to read serial number or serial number empty. " - << "We can only support one device without a serial number."; - return NULL; - } else { - OLA_WARN << "Failed to read serial number from " << info.manufacturer - << " : " << info.product - << " the device probably doesn't have one"; - m_anyma_devices_missing_serial_numbers = true; + WidgetFactories::iterator iter = m_widget_factories.begin(); + OLA_INFO << "Checking " << m_widget_factories.size() << " factories"; + for (; iter != m_widget_factories.end(); ++iter) { + if ((*iter)->DeviceAdded(this, usb_device, device_descriptor)) { + m_registered_devices.insert(bus_dev_id); + return true; } } - - SynchronousAnymaWidget *widget = new SynchronousAnymaWidget( - &m_usb_adaptor, usb_device, info.serial); - if (!widget->Init()) { - delete widget; - return NULL; - } - return new GenericDevice( - m_plugin, widget, "Anyma USB Device", "anyma-" + widget->SerialNumber()); -} - -Device* SyncPluginImpl::NewEuroliteProDevice( - struct libusb_device *usb_device) { - - // There is no Serialnumber--> work around: bus+device number - int bus_number = libusb_get_bus_number(usb_device); - int device_address = libusb_get_device_address(usb_device); - - OLA_INFO << "Bus_number: " << bus_number << ", Device_address: " << - device_address; - - std::ostringstream serial_str; - serial_str << bus_number << "-" << device_address; - - SynchronousEuroliteProWidget *widget = new SynchronousEuroliteProWidget( - &m_usb_adaptor, usb_device, serial_str.str()); - if (!widget->Init()) { - delete widget; - return NULL; - } - - return new GenericDevice( - m_plugin, widget, "EurolitePro USB Device", - "eurolite-" + widget->SerialNumber()); -} - -/** - * Get the Manufacturer, Product and Serial number strings for a device. - */ -void SyncPluginImpl::GetDeviceInfo( - libusb_device_handle *usb_handle, - const struct libusb_device_descriptor &device_descriptor, - USBDeviceInformation *device_info) { - if (!GetDescriptorString(usb_handle, device_descriptor.iManufacturer, - &device_info->manufacturer)) - OLA_INFO << "Failed to get manufacturer name"; - - if (!GetDescriptorString(usb_handle, device_descriptor.iProduct, - &device_info->product)) - OLA_INFO << "Failed to get product name"; - - if (!GetDescriptorString(usb_handle, device_descriptor.iSerialNumber, - &device_info->serial)) - OLA_WARN << "Failed to read serial number, the device probably doesn't " - << "have one"; -} - - -/** - * Check if the manufacturer string matches the expected value. Log a message - * if it doesn't. - */ -bool SyncPluginImpl::MatchManufacturer(const string &expected, - const string &actual) { - if (expected != actual) { - OLA_WARN << "Manufacturer mismatch: " << expected << " != " << actual; - return false; - } - return true; + return false; } - -/** - * Check if the manufacturer string matches the expected value. Log a message - * if it doesn't. - */ -bool SyncPluginImpl::MatchProduct(const string &expected, - const string &actual) { - if (expected != actual) { - OLA_WARN << "Product mismatch: " << expected << " != " << actual; - return false; - } - return true; +void SyncPluginImpl::ReScanForDevices() { + ScanForDevices(); } /* - * Return a string descriptor. - * @param usb_handle the usb handle to the device - * @param desc_index the index of the descriptor - * @param data where to store the output string - * @returns true if we got the value, false otherwise + * @brief Signal widget / device addition. + * @param widget The widget that was added. + * @param device The new olad device that uses this new widget. */ -bool SyncPluginImpl::GetDescriptorString(libusb_device_handle *usb_handle, - uint8_t desc_index, - string *data) { - enum { buffer_size = 32 }; // static arrays FTW! - unsigned char buffer[buffer_size]; - int r = libusb_get_string_descriptor_ascii( - usb_handle, - desc_index, - buffer, - buffer_size); - - if (r <= 0) { - OLA_INFO << "libusb_get_string_descriptor_ascii returned " << r; +bool SyncPluginImpl::StartAndRegisterDevice(Widget *widget, Device *device) { + if (!device->Start()) { + delete device; return false; } - data->assign(reinterpret_cast(buffer)); + + STLReplace(&m_devices, widget, device); + m_plugin_adaptor->RegisterDevice(device); return true; } } // namespace usbdmx diff --git a/plugins/usbdmx/SyncPluginImpl.h b/plugins/usbdmx/SyncPluginImpl.h index 8b5f38fe00..c54438b7bc 100644 --- a/plugins/usbdmx/SyncPluginImpl.h +++ b/plugins/usbdmx/SyncPluginImpl.h @@ -22,13 +22,16 @@ #define PLUGINS_USBDMX_SYNCPLUGINIMPL_H_ #include +#include #include #include #include #include + #include "ola/base/Macro.h" #include "plugins/usbdmx/LibUsbAdaptor.h" #include "plugins/usbdmx/PluginImplInterface.h" +#include "plugins/usbdmx/WidgetFactory.h" namespace ola { @@ -45,7 +48,7 @@ namespace usbdmx { * * This does not support hotplug. */ -class SyncPluginImpl: public PluginImplInterface { +class SyncPluginImpl: public PluginImplInterface, public WidgetObserver { public: /** * @brief Create a new SyncPluginImpl. @@ -59,47 +62,41 @@ class SyncPluginImpl: public PluginImplInterface { Plugin *plugin, unsigned int debug_level); + ~SyncPluginImpl(); + bool Start(); bool Stop(); + bool NewWidget(class AnymaWidget *widget); + bool NewWidget(class EuroliteProWidget *widget); + bool NewWidget(class SunliteWidget *widget); + bool NewWidget(class VellemanWidget *widget); + + void WidgetRemoved(OLA_UNUSED class AnymaWidget *widget) {} + void WidgetRemoved(OLA_UNUSED class EuroliteProWidget *widget) {} + void WidgetRemoved(OLA_UNUSED class SunliteWidget *widget) {} + void WidgetRemoved(OLA_UNUSED class VellemanWidget *widget) {} + private: - struct USBDeviceInformation { - std::string manufacturer; - std::string product; - std::string serial; - }; + typedef std::vector WidgetFactories; + typedef std::map WidgetToDeviceMap; PluginAdaptor* const m_plugin_adaptor; Plugin* const m_plugin; const unsigned int m_debug_level; SyncronousLibUsbAdaptor m_usb_adaptor; + WidgetFactories m_widget_factories; libusb_context *m_context; - bool m_anyma_devices_missing_serial_numbers; - std::vector m_devices; // list of our devices + WidgetToDeviceMap m_devices; std::set > m_registered_devices; - bool LoadFirmware(); - void FindDevices(); - void CheckDevice(libusb_device *device); - - class Device* NewAnymaDevice( - struct libusb_device *usb_device, - const struct libusb_device_descriptor &device_descriptor); - class Device* NewEuroliteProDevice( - struct libusb_device *usb_device); - - void GetDeviceInfo( - struct libusb_device_handle *usb_handle, - const struct libusb_device_descriptor &device_descriptor, - USBDeviceInformation *device_info); - bool MatchManufacturer(const std::string &expected, - const std::string &actual); - bool MatchProduct(const std::string &expected, const std::string &actual); - bool GetDescriptorString(libusb_device_handle *usb_handle, - uint8_t desc_index, - std::string *data); + unsigned int ScanForDevices(); + void ReScanForDevices(); + bool CheckDevice(libusb_device *device); + + bool StartAndRegisterDevice(class Widget *widget, Device *device); DISALLOW_COPY_AND_ASSIGN(SyncPluginImpl); }; diff --git a/plugins/usbdmx/UsbDmxPlugin.cpp b/plugins/usbdmx/UsbDmxPlugin.cpp index d19ec95a07..12fdaf0839 100644 --- a/plugins/usbdmx/UsbDmxPlugin.cpp +++ b/plugins/usbdmx/UsbDmxPlugin.cpp @@ -33,7 +33,7 @@ #include "plugins/usbdmx/PluginImplInterface.h" #include "plugins/usbdmx/SyncPluginImpl.h" -DEFINE_default_bool(use_async_libusb, true, +DEFINE_default_bool(use_async_libusb, false, "Use the asyncronous libusb calls."); namespace ola { From 1b4eeac168850b34b772b5a23db57df6b1d7ac27 Mon Sep 17 00:00:00 2001 From: Simon Newton Date: Sun, 16 Nov 2014 14:46:21 -0800 Subject: [PATCH 17/34] Fix some documentation. --- plugins/usbdmx/EuroliteProWidget.h | 1 + plugins/usbdmx/LibUsbThread.h | 1 - 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/usbdmx/EuroliteProWidget.h b/plugins/usbdmx/EuroliteProWidget.h index 3b4050f606..3030ec79e2 100644 --- a/plugins/usbdmx/EuroliteProWidget.h +++ b/plugins/usbdmx/EuroliteProWidget.h @@ -115,6 +115,7 @@ class AsynchronousEuroliteProWidget: public EuroliteProWidget { public: /** * @brief Create a new AsynchronousEuroliteProWidget. + * @param adaptor the LibUsbAdaptor to use. * @param usb_device the libusb_device to use for the widget. * @param serial the serial number of the widget. */ diff --git a/plugins/usbdmx/LibUsbThread.h b/plugins/usbdmx/LibUsbThread.h index 9bfcf4228b..85b8d97238 100644 --- a/plugins/usbdmx/LibUsbThread.h +++ b/plugins/usbdmx/LibUsbThread.h @@ -185,7 +185,6 @@ class LibUsbSimpleThread : public LibUsbThread { /** * @brief Create a new LibUsbHotplugThread.o * @param context the libusb context to use. - * @param handle the callback handle for hotplug events. * * The thread is starts as soon as this object is created. When the object is * destroyed, the handle is de-registered as part of the thread shutdown From 9306e337da2cb398c5977d22e65187793612c0c9 Mon Sep 17 00:00:00 2001 From: Simon Newton Date: Sun, 16 Nov 2014 14:50:33 -0800 Subject: [PATCH 18/34] Remove the old LibUsbUtils --- plugins/usbdmx/LibUsbUtils.cpp | 58 ---------------------------------- plugins/usbdmx/LibUsbUtils.h | 38 ---------------------- plugins/usbdmx/Makefile.mk | 2 -- 3 files changed, 98 deletions(-) delete mode 100644 plugins/usbdmx/LibUsbUtils.cpp delete mode 100644 plugins/usbdmx/LibUsbUtils.h diff --git a/plugins/usbdmx/LibUsbUtils.cpp b/plugins/usbdmx/LibUsbUtils.cpp deleted file mode 100644 index cb117e5b92..0000000000 --- a/plugins/usbdmx/LibUsbUtils.cpp +++ /dev/null @@ -1,58 +0,0 @@ -/* - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * 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 Library 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - * - * LibUsbUtils.cpp - * libusb Util functions. - * Copyright (C) 2014 Peter Newman - */ - -#include - -#include "ola/Logging.h" -#include "plugins/usbdmx/LibUsbUtils.h" - -namespace ola { -namespace plugin { -namespace usbdmx { - -using std::string; - -/** - * Return a string descriptor. - * @param usb_handle the usb handle to the device - * @param desc_index the index of the descriptor - * @param data where to store the output string - * @returns true if we got the value, false otherwise - */ -bool GetDescriptorString(libusb_device_handle *usb_handle, - uint8_t desc_index, - string *data) { - enum { buffer_size = 32 }; // static arrays FTW! - unsigned char buffer[buffer_size]; - int r = libusb_get_string_descriptor_ascii(usb_handle, - desc_index, - buffer, - buffer_size); - - if (r <= 0) { - OLA_INFO << "libusb_get_string_descriptor_ascii returned " << r; - return false; - } - data->assign(reinterpret_cast(buffer)); - return true; -} -} // namespace usbdmx -} // namespace plugin -} // namespace ola diff --git a/plugins/usbdmx/LibUsbUtils.h b/plugins/usbdmx/LibUsbUtils.h deleted file mode 100644 index 5a0dfda226..0000000000 --- a/plugins/usbdmx/LibUsbUtils.h +++ /dev/null @@ -1,38 +0,0 @@ -/* - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * 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 Library 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - * - * LibUsbUtils.h - * libusb Util functions. - * Copyright (C) 2014 Peter Newman - */ - -#ifndef PLUGINS_USBDMX_LIBUSBUTILS_H_ -#define PLUGINS_USBDMX_LIBUSBUTILS_H_ - -#include - -#include - -namespace ola { -namespace plugin { -namespace usbdmx { - -bool GetDescriptorString(libusb_device_handle *usb_handle, - uint8_t desc_index, - std::string *data); -} // namespace usbdmx -} // namespace plugin -} // namespace ola -#endif // PLUGINS_USBDMX_LIBUSBUTILS_H_ diff --git a/plugins/usbdmx/Makefile.mk b/plugins/usbdmx/Makefile.mk index 3d76269963..2928365f39 100644 --- a/plugins/usbdmx/Makefile.mk +++ b/plugins/usbdmx/Makefile.mk @@ -22,8 +22,6 @@ plugins_usbdmx_libolausbdmx_la_SOURCES = \ plugins/usbdmx/LibUsbAdaptor.h \ plugins/usbdmx/LibUsbThread.cpp \ plugins/usbdmx/LibUsbThread.h \ - plugins/usbdmx/LibUsbUtils.cpp \ - plugins/usbdmx/LibUsbUtils.h \ plugins/usbdmx/PluginImplInterface.h \ plugins/usbdmx/SunliteFirmware.h \ plugins/usbdmx/SunliteFirmwareLoader.cpp \ From 72e33e8bb799d93137ca5e5358048d928ad74bf4 Mon Sep 17 00:00:00 2001 From: Simon Newton Date: Sun, 16 Nov 2014 15:16:15 -0800 Subject: [PATCH 19/34] Fix a crashing bug, add better logging for libusb. --- plugins/usbdmx/AsyncPluginImpl.cpp | 2 +- plugins/usbdmx/LibUsbAdaptor.cpp | 17 +++++++++++------ plugins/usbdmx/SyncPluginImpl.cpp | 1 - 3 files changed, 12 insertions(+), 8 deletions(-) diff --git a/plugins/usbdmx/AsyncPluginImpl.cpp b/plugins/usbdmx/AsyncPluginImpl.cpp index f036188dde..03e4c6f3fe 100644 --- a/plugins/usbdmx/AsyncPluginImpl.cpp +++ b/plugins/usbdmx/AsyncPluginImpl.cpp @@ -309,7 +309,7 @@ bool AsyncPluginImpl::ScanUSBDevices() { std::set current_device_ids; libusb_device **device_list; - size_t device_count = libusb_get_device_list(NULL, &device_list); + size_t device_count = libusb_get_device_list(m_context, &device_list); OLA_INFO << "Got " << device_count << " devices"; for (unsigned int i = 0; i < device_count; i++) { diff --git a/plugins/usbdmx/LibUsbAdaptor.cpp b/plugins/usbdmx/LibUsbAdaptor.cpp index ffdab3448a..6ca02df16d 100644 --- a/plugins/usbdmx/LibUsbAdaptor.cpp +++ b/plugins/usbdmx/LibUsbAdaptor.cpp @@ -49,7 +49,8 @@ bool GetStringDescriptorAscii(libusb_device_handle *usb_handle, buffer_size); if (r <= 0) { - OLA_INFO << "libusb_get_string_descriptor_ascii returned " << r; + OLA_INFO << "libusb_get_string_descriptor_ascii failed: " + << libusb_error_name(r); return false; } data->assign(reinterpret_cast(buffer)); @@ -61,8 +62,10 @@ bool GetStringDescriptorAscii(libusb_device_handle *usb_handle, */ bool Open(libusb_device *usb_device, libusb_device_handle **usb_handle) { - if (libusb_open(usb_device, usb_handle)) { - OLA_WARN << "Failed to open libusb device: " << usb_device; + int r = libusb_open(usb_device, usb_handle); + if (r) { + OLA_WARN << "Failed to open libusb device: " << usb_device << ": " + << libusb_error_name(r);; return false; } return true; @@ -83,9 +86,11 @@ bool OpenHandleAndClaimInterface( return false; } - if (libusb_claim_interface(*usb_handle, 0)) { + int r = libusb_claim_interface(*usb_handle, 0); + if (r) { OLA_WARN << "Failed to claim interface " << interface - << " for libusb device: " << usb_device; + << " on device: " << usb_device << ": " + << libusb_error_name(r); Close(*usb_handle); return false; } @@ -94,7 +99,7 @@ bool OpenHandleAndClaimInterface( } // namespace // LibUsbAdaptor -// ----------------------------------------------------------------------------- +// ---------------------------------------------------------------------------- bool LibUsbAdaptor::GetDeviceInfo( struct libusb_device *usb_device, diff --git a/plugins/usbdmx/SyncPluginImpl.cpp b/plugins/usbdmx/SyncPluginImpl.cpp index f92f472860..f185435e04 100644 --- a/plugins/usbdmx/SyncPluginImpl.cpp +++ b/plugins/usbdmx/SyncPluginImpl.cpp @@ -160,7 +160,6 @@ bool SyncPluginImpl::CheckDevice(libusb_device *usb_device) { } WidgetFactories::iterator iter = m_widget_factories.begin(); - OLA_INFO << "Checking " << m_widget_factories.size() << " factories"; for (; iter != m_widget_factories.end(); ++iter) { if ((*iter)->DeviceAdded(this, usb_device, device_descriptor)) { m_registered_devices.insert(bus_dev_id); From 90671f64bc80d6ef870ed9237c7b84a52b3f0044 Mon Sep 17 00:00:00 2001 From: Simon Newton Date: Sun, 16 Nov 2014 16:20:55 -0800 Subject: [PATCH 20/34] Add the README file explaining how the libusb plugin is laid out. --- plugins/usbdmx/README.md | 97 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 97 insertions(+) create mode 100644 plugins/usbdmx/README.md diff --git a/plugins/usbdmx/README.md b/plugins/usbdmx/README.md new file mode 100644 index 0000000000..da4875a2c2 --- /dev/null +++ b/plugins/usbdmx/README.md @@ -0,0 +1,97 @@ +USBDMX Plugin +=============================================================================== + +This plugin uses [libusb](http://libusb.info/) to communicate with USB devices. + +libusb supports synchronous and asynchronous transfers. The initial version of +this plugin used the synchronous interface, and spawned a thread for every USB +device present. + +The new version of the plugin uses the asynchronous mode of operation and a +single thread for the libusb completion handling. + +You can opt-in to the new asynchronous mode by passing the --use\_async\_libusb +flag to olad. Assuming we don't find any problems, at some point this will +become the default and the synchronous implementation will be removed. + +The rest of this file explains how the plugin is constructed and is aimed at +developers wishing to add support for a new USB Device. It assumes the reader +has an understanding of libusb. + +Terminology +------------------------------------------------------------------------------- + +*USB Device*, is the physical USB device attached to the host. + +*DMX512 Interface*, the physical socket on the USB Device the user plugins the +DMX512 cable into. Currently all the USB Devices supported by the plugin +contain a single DMX512 Interface. + +*libusb\_device*, is the libusb structure that represents a USB device. + +*Widget*, is OLA's internal object representation of a USB Device. Widgets use +the corresponding libusb\_device to communicate with the USB Device. + +*Port*, the software representation of a DMX512 Interface. In OLA, users +associate a Port with a Universe. + +*Device*, the software representation of a USB Device. Contains one or more +Ports. + +Code Concepts & Structure +------------------------------------------------------------------------------- + +USB Devices are represented as Widgets, this allows us to de-couple the Widget +code from OLA's Port representation (remember, prefer composition over +inheritance). Since all the USB devices we support so +far have a single DMX512 interface, each specific Widget (e.g. AnymaWidget) +derives from the Widget class. This isn't strictly necessary, it just means we +can avoid code duplication by using the GenericDevice and GenericPort classes. +If in the future, multi-interface USB devices are supported, they shouldn't +inherit from the Widget class. + +GenericPort wraps a Widget into a Port object, so it can show up in olad. +GenericDevice creates a Device with a single GenericPort. + +For each type of USB Device, we create a common base class, e.g. AnymaWidget. +This enables the WidgetObserver class (see below) to know what +name it should give the resultant Device. + +Then for each type of USB Device, we create a synchronous and asynchronous +version of the Widget. So you end up with something like: + +* Widget + * AnymaWidget + * SynchronousAnymaWidget + * AsynchronousAnymaWidget + * SunliteWidget + * SynchronousSunliteWidget + * AsynchronousSunliteWidget + +Each type of USB Device has an associated factory. When a new libusb\_device is +discovered, each factory has the opportunity to claim the new device. Typically +factories will check the vendor and product ID and, if they recognize the +device, create a Widget to represent it. + +If a factory creates a new Widget, it needs to notify the WidgetObserver. The +WidgetObserver can then go ahead and use the newly created Widget to setup an +OLA Device & Port. + + +Adding Support for a new USB Device +------------------------------------------------------------------------------- + +Adding support for a new USB device should be reasonably straightforward. This +guide assumes the new USB Device has a single DMX512 Interface. + +1. Create the FooWidget.{h,cpp} files. + - Create the FooWidget base class, and a synchronous and asynchronous + implementation of the Foo Widget. +2. Create the FooWidgetFactory.{h,cpp} files. + - Write the DeviceAdded() method, to detect the new USB Device and create + either a synchronous or asynchronous widget, depending on the + --use\_async\_libusb flag. +3. Extend the WidgetObserver with new NewWidget() and WidgetRemoved() removed + methods for the new FooWidget. +4. Implement the new NewWidget() and WidgetRemoved() methods in both the + SyncPluginImpl and AsyncPluginImpl. From 695ebacb8357a63902a37684c3bd22c7899753fd Mon Sep 17 00:00:00 2001 From: Simon Newton Date: Sun, 16 Nov 2014 17:00:42 -0800 Subject: [PATCH 21/34] Minor code formatting tweaks. --- plugins/usbdmx/AnymaWidget.cpp | 12 +++++++++--- plugins/usbdmx/AnymaWidget.h | 10 ---------- plugins/usbdmx/AnymaWidgetFactory.cpp | 12 +++++++----- plugins/usbdmx/AnymaWidgetFactory.h | 4 +++- plugins/usbdmx/EuroliteProWidget.cpp | 12 +++++++++--- plugins/usbdmx/EuroliteProWidget.h | 10 ---------- plugins/usbdmx/EuroliteProWidgetFactory.cpp | 12 +++++------- plugins/usbdmx/EuroliteProWidgetFactory.h | 3 +++ plugins/usbdmx/SunliteWidget.cpp | 8 ++++++++ plugins/usbdmx/SunliteWidgetFactory.cpp | 1 - plugins/usbdmx/VellemanWidget.cpp | 7 +++++++ plugins/usbdmx/VellemanWidgetFactory.cpp | 1 - 12 files changed, 51 insertions(+), 41 deletions(-) diff --git a/plugins/usbdmx/AnymaWidget.cpp b/plugins/usbdmx/AnymaWidget.cpp index eb0906c8e5..fbc65e80a2 100644 --- a/plugins/usbdmx/AnymaWidget.cpp +++ b/plugins/usbdmx/AnymaWidget.cpp @@ -32,9 +32,6 @@ namespace ola { namespace plugin { namespace usbdmx { -const char AnymaWidget::EXPECTED_MANUFACTURER[] = "www.anyma.ch"; -const char AnymaWidget::EXPECTED_PRODUCT[] = "uDMX"; - using std::string; namespace { @@ -53,6 +50,9 @@ void AsyncCallback(struct libusb_transfer *transfer) { } // namespace +// AnymaThreadedSender +// ----------------------------------------------------------------------------- + /* * Sends messages to a Anyma device in a separate thread. */ @@ -89,6 +89,9 @@ bool AnymaThreadedSender::TransmitBuffer(libusb_device_handle *handle, } +// SynchronousAnymaWidget +// ----------------------------------------------------------------------------- + SynchronousAnymaWidget::SynchronousAnymaWidget(LibUsbAdaptor *adaptor, libusb_device *usb_device, const string &serial) @@ -118,6 +121,9 @@ bool SynchronousAnymaWidget::SendDMX(const DmxBuffer &buffer) { return m_sender.get() ? m_sender->SendDMX(buffer) : false; } +// AsynchronousAnymaWidget +// ----------------------------------------------------------------------------- + AsynchronousAnymaWidget::AsynchronousAnymaWidget( LibUsbAdaptor *adaptor, libusb_device *usb_device, diff --git a/plugins/usbdmx/AnymaWidget.h b/plugins/usbdmx/AnymaWidget.h index 2d54ca0ec8..3456e65fd6 100644 --- a/plugins/usbdmx/AnymaWidget.h +++ b/plugins/usbdmx/AnymaWidget.h @@ -59,16 +59,6 @@ class AnymaWidget: public BaseWidget { return m_serial; } - /** - * @brief The expected manufacturer string for an Anyma widget. - */ - static const char EXPECTED_MANUFACTURER[]; - - /** - * @brief The expected product string for an Anyma widget. - */ - static const char EXPECTED_PRODUCT[]; - private: std::string m_serial; }; diff --git a/plugins/usbdmx/AnymaWidgetFactory.cpp b/plugins/usbdmx/AnymaWidgetFactory.cpp index b0ed5e0b32..40c23a0abd 100644 --- a/plugins/usbdmx/AnymaWidgetFactory.cpp +++ b/plugins/usbdmx/AnymaWidgetFactory.cpp @@ -32,8 +32,11 @@ namespace ola { namespace plugin { namespace usbdmx { -const uint16_t AnymaWidgetFactory::VENDOR_ID = 0x16C0; +const char AnymaWidgetFactory::EXPECTED_MANUFACTURER[] = "www.anyma.ch"; +const char AnymaWidgetFactory::EXPECTED_PRODUCT[] = "uDMX"; const uint16_t AnymaWidgetFactory::PRODUCT_ID = 0x05DC; +const uint16_t AnymaWidgetFactory::VENDOR_ID = 0x16C0; + bool AnymaWidgetFactory::DeviceAdded( WidgetObserver *observer, @@ -50,12 +53,11 @@ bool AnymaWidgetFactory::DeviceAdded( return false; } - if (!m_adaptor->CheckManufacturer( - AnymaWidget::EXPECTED_MANUFACTURER, info.manufacturer)) { + if (!m_adaptor->CheckManufacturer(EXPECTED_MANUFACTURER, info.manufacturer)) { return false; } - if (!m_adaptor->CheckProduct(AnymaWidget::EXPECTED_PRODUCT, info.product)) { + if (!m_adaptor->CheckProduct(EXPECTED_PRODUCT, info.product)) { return false; } @@ -75,7 +77,7 @@ bool AnymaWidgetFactory::DeviceAdded( } } - AnymaWidget *widget; + AnymaWidget *widget = NULL; if (FLAGS_use_async_libusb) { widget = new AsynchronousAnymaWidget(m_adaptor, usb_device, info.serial); } else { diff --git a/plugins/usbdmx/AnymaWidgetFactory.h b/plugins/usbdmx/AnymaWidgetFactory.h index f88097d540..55baa7cec4 100644 --- a/plugins/usbdmx/AnymaWidgetFactory.h +++ b/plugins/usbdmx/AnymaWidgetFactory.h @@ -47,8 +47,10 @@ class AnymaWidgetFactory : public BaseWidgetFactory { bool m_missing_serial_number; class LibUsbAdaptor *m_adaptor; - static const uint16_t VENDOR_ID; + static const char EXPECTED_MANUFACTURER[]; + static const char EXPECTED_PRODUCT[]; static const uint16_t PRODUCT_ID; + static const uint16_t VENDOR_ID; DISALLOW_COPY_AND_ASSIGN(AnymaWidgetFactory); }; diff --git a/plugins/usbdmx/EuroliteProWidget.cpp b/plugins/usbdmx/EuroliteProWidget.cpp index f07ca44cf6..9d69d1b87c 100644 --- a/plugins/usbdmx/EuroliteProWidget.cpp +++ b/plugins/usbdmx/EuroliteProWidget.cpp @@ -32,9 +32,6 @@ namespace ola { namespace plugin { namespace usbdmx { -const char EuroliteProWidget::EXPECTED_MANUFACTURER[] = "Eurolite"; -const char EuroliteProWidget::EXPECTED_PRODUCT[] = "Eurolite DMX512 Pro"; - using std::string; namespace { @@ -114,6 +111,9 @@ bool LocateInterface(libusb_device *usb_device, } } // namespace +// EuroliteProThreadedSender +// ----------------------------------------------------------------------------- + /* * Sends messages to a EurolitePro device in a separate thread. */ @@ -153,6 +153,9 @@ bool EuroliteProThreadedSender::TransmitBuffer(libusb_device_handle *handle, return r == 0; } +// SynchronousEuroliteProWidget +// ----------------------------------------------------------------------------- + SynchronousEuroliteProWidget::SynchronousEuroliteProWidget( LibUsbAdaptor *adaptor, libusb_device *usb_device, @@ -188,6 +191,9 @@ bool SynchronousEuroliteProWidget::SendDMX(const DmxBuffer &buffer) { return m_sender.get() ? m_sender->SendDMX(buffer) : false; } +// AsynchronousEuroliteProWidget +// ----------------------------------------------------------------------------- + AsynchronousEuroliteProWidget::AsynchronousEuroliteProWidget( class LibUsbAdaptor *adaptor, libusb_device *usb_device, diff --git a/plugins/usbdmx/EuroliteProWidget.h b/plugins/usbdmx/EuroliteProWidget.h index 3030ec79e2..3c52d43183 100644 --- a/plugins/usbdmx/EuroliteProWidget.h +++ b/plugins/usbdmx/EuroliteProWidget.h @@ -58,16 +58,6 @@ class EuroliteProWidget : public BaseWidget { return m_serial; } - /** - * @brief The expected manufacturer string for a EurolitePro widget. - */ - static const char EXPECTED_MANUFACTURER[]; - - /** - * @brief The expected product string for a EurolitePro widget. - */ - static const char EXPECTED_PRODUCT[]; - /** * @brief The size of a EurolitePro frame. * diff --git a/plugins/usbdmx/EuroliteProWidgetFactory.cpp b/plugins/usbdmx/EuroliteProWidgetFactory.cpp index 3e30994f73..46333a8b8d 100644 --- a/plugins/usbdmx/EuroliteProWidgetFactory.cpp +++ b/plugins/usbdmx/EuroliteProWidgetFactory.cpp @@ -30,9 +30,12 @@ namespace ola { namespace plugin { namespace usbdmx { +const char EuroliteProWidgetFactory::EXPECTED_MANUFACTURER[] = "Eurolite"; +const char EuroliteProWidgetFactory::EXPECTED_PRODUCT[] = "Eurolite DMX512 Pro"; const uint16_t EuroliteProWidgetFactory::PRODUCT_ID = 0xfa63; const uint16_t EuroliteProWidgetFactory::VENDOR_ID = 0x04d; + bool EuroliteProWidgetFactory::DeviceAdded( WidgetObserver *observer, libusb_device *usb_device, @@ -48,13 +51,11 @@ bool EuroliteProWidgetFactory::DeviceAdded( return false; } - if (!m_adaptor->CheckManufacturer( - EuroliteProWidget::EXPECTED_MANUFACTURER, info.manufacturer)) { + if (!m_adaptor->CheckManufacturer(EXPECTED_MANUFACTURER, info.manufacturer)) { return false; } - if (!m_adaptor->CheckProduct( - EuroliteProWidget::EXPECTED_PRODUCT, info.product)) { + if (!m_adaptor->CheckProduct(EXPECTED_PRODUCT, info.product)) { return false; } @@ -67,9 +68,6 @@ bool EuroliteProWidgetFactory::DeviceAdded( int bus_number = libusb_get_bus_number(usb_device); int device_address = libusb_get_device_address(usb_device); - OLA_INFO << "Bus_number: " << bus_number << ", Device_address: " << - device_address; - std::ostringstream serial_str; serial_str << bus_number << "-" << device_address; diff --git a/plugins/usbdmx/EuroliteProWidgetFactory.h b/plugins/usbdmx/EuroliteProWidgetFactory.h index d99b3c3a74..aacb38010c 100644 --- a/plugins/usbdmx/EuroliteProWidgetFactory.h +++ b/plugins/usbdmx/EuroliteProWidgetFactory.h @@ -48,6 +48,9 @@ class EuroliteProWidgetFactory static const uint16_t PRODUCT_ID; static const uint16_t VENDOR_ID; + static const char EXPECTED_MANUFACTURER[]; + static const char EXPECTED_PRODUCT[]; + DISALLOW_COPY_AND_ASSIGN(EuroliteProWidgetFactory); }; diff --git a/plugins/usbdmx/SunliteWidget.cpp b/plugins/usbdmx/SunliteWidget.cpp index 467ff00eba..fbb94c9fef 100644 --- a/plugins/usbdmx/SunliteWidget.cpp +++ b/plugins/usbdmx/SunliteWidget.cpp @@ -94,6 +94,9 @@ void UpdatePacket(const DmxBuffer &buffer, } // namespace +// SunliteThreadedSender +// ----------------------------------------------------------------------------- + /* * Sends messages to a Sunlite device in a separate thread. */ @@ -134,6 +137,8 @@ bool SunliteThreadedSender::TransmitBuffer(libusb_device_handle *handle, return r == 0; } +// SynchronousSunliteWidget +// ----------------------------------------------------------------------------- SynchronousSunliteWidget::SynchronousSunliteWidget(LibUsbAdaptor *adaptor, libusb_device *usb_device) @@ -163,6 +168,9 @@ bool SynchronousSunliteWidget::SendDMX(const DmxBuffer &buffer) { return m_sender.get() ? m_sender->SendDMX(buffer) : false; } +// AsynchronousSunliteWidget +// ----------------------------------------------------------------------------- + AsynchronousSunliteWidget::AsynchronousSunliteWidget( LibUsbAdaptor *adaptor, libusb_device *usb_device) diff --git a/plugins/usbdmx/SunliteWidgetFactory.cpp b/plugins/usbdmx/SunliteWidgetFactory.cpp index 257f274deb..01b9cd04f8 100644 --- a/plugins/usbdmx/SunliteWidgetFactory.cpp +++ b/plugins/usbdmx/SunliteWidgetFactory.cpp @@ -49,7 +49,6 @@ bool SunliteWidgetFactory::DeviceAdded( descriptor.idProduct == FULL_PRODUCT_ID && !HasDevice(usb_device)) { OLA_INFO << "Found a new Sunlite device"; - SunliteWidget *widget = NULL; if (FLAGS_use_async_libusb) { widget = new AsynchronousSunliteWidget(m_adaptor, usb_device); diff --git a/plugins/usbdmx/VellemanWidget.cpp b/plugins/usbdmx/VellemanWidget.cpp index c76f420c7b..758c0ff7f0 100644 --- a/plugins/usbdmx/VellemanWidget.cpp +++ b/plugins/usbdmx/VellemanWidget.cpp @@ -56,6 +56,9 @@ void AsyncCallback(struct libusb_transfer *transfer) { } // namespace +// VellemanThreadedSender +// ----------------------------------------------------------------------------- + /* * Sends messages to a Velleman device in a separate thread. */ @@ -178,6 +181,8 @@ bool VellemanThreadedSender::SendDataChunk(libusb_device_handle *handle, return ret == 0; } +// SynchronousVellemanWidget +// ----------------------------------------------------------------------------- SynchronousVellemanWidget::SynchronousVellemanWidget( LibUsbAdaptor *adaptor, @@ -254,6 +259,8 @@ bool SynchronousVellemanWidget::SendDMX(const DmxBuffer &buffer) { return m_sender.get() ? m_sender->SendDMX(buffer) : false; } +// AsynchronousVellemanWidget +// ----------------------------------------------------------------------------- AsynchronousVellemanWidget::AsynchronousVellemanWidget( LibUsbAdaptor *adaptor, diff --git a/plugins/usbdmx/VellemanWidgetFactory.cpp b/plugins/usbdmx/VellemanWidgetFactory.cpp index 3cedb4f158..f8eb2308d6 100644 --- a/plugins/usbdmx/VellemanWidgetFactory.cpp +++ b/plugins/usbdmx/VellemanWidgetFactory.cpp @@ -46,7 +46,6 @@ bool VellemanWidgetFactory::DeviceAdded( VellemanWidget *widget = NULL; if (FLAGS_use_async_libusb) { widget = new AsynchronousVellemanWidget(m_adaptor, usb_device); - } else { widget = new SynchronousVellemanWidget(m_adaptor, usb_device); } From f64dd17a6afe7df411f9153ac4c51290f3661fb5 Mon Sep 17 00:00:00 2001 From: Simon Newton Date: Sun, 16 Nov 2014 17:26:11 -0800 Subject: [PATCH 22/34] Fix the flag name. --- plugins/usbdmx/README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/usbdmx/README.md b/plugins/usbdmx/README.md index da4875a2c2..38533a5a91 100644 --- a/plugins/usbdmx/README.md +++ b/plugins/usbdmx/README.md @@ -10,7 +10,7 @@ device present. The new version of the plugin uses the asynchronous mode of operation and a single thread for the libusb completion handling. -You can opt-in to the new asynchronous mode by passing the --use\_async\_libusb +You can opt-in to the new asynchronous mode by passing the --use-async-libusb flag to olad. Assuming we don't find any problems, at some point this will become the default and the synchronous implementation will be removed. @@ -90,7 +90,7 @@ guide assumes the new USB Device has a single DMX512 Interface. 2. Create the FooWidgetFactory.{h,cpp} files. - Write the DeviceAdded() method, to detect the new USB Device and create either a synchronous or asynchronous widget, depending on the - --use\_async\_libusb flag. + --use-async-libusb flag. 3. Extend the WidgetObserver with new NewWidget() and WidgetRemoved() removed methods for the new FooWidget. 4. Implement the new NewWidget() and WidgetRemoved() methods in both the From dc71ae2e101efd65b74aa7a1f0fbdb28fc2e0e90 Mon Sep 17 00:00:00 2001 From: Simon Newton Date: Sun, 16 Nov 2014 18:55:58 -0800 Subject: [PATCH 23/34] Move the Async USB TX code into a common class --- plugins/usbdmx/AnymaWidget.cpp | 164 +++++++++------------------ plugins/usbdmx/AnymaWidget.h | 23 +--- plugins/usbdmx/AsyncUsbSender.cpp | 149 ++++++++++++++++++++++++ plugins/usbdmx/AsyncUsbSender.h | 92 +++++++++++++++ plugins/usbdmx/EuroliteProWidget.cpp | 139 ++++++++--------------- plugins/usbdmx/EuroliteProWidget.h | 30 +---- plugins/usbdmx/Makefile.mk | 2 + plugins/usbdmx/SunliteWidget.cpp | 132 +++++++-------------- plugins/usbdmx/SunliteWidget.h | 28 +---- 9 files changed, 387 insertions(+), 372 deletions(-) create mode 100644 plugins/usbdmx/AsyncUsbSender.cpp create mode 100644 plugins/usbdmx/AsyncUsbSender.h diff --git a/plugins/usbdmx/AnymaWidget.cpp b/plugins/usbdmx/AnymaWidget.cpp index fbc65e80a2..5cd684ce65 100644 --- a/plugins/usbdmx/AnymaWidget.cpp +++ b/plugins/usbdmx/AnymaWidget.cpp @@ -25,6 +25,7 @@ #include "ola/Logging.h" #include "ola/Constants.h" +#include "plugins/usbdmx/AsyncUsbSender.h" #include "plugins/usbdmx/LibUsbAdaptor.h" #include "plugins/usbdmx/ThreadedUsbSender.h" @@ -39,15 +40,6 @@ namespace { static const unsigned int URB_TIMEOUT_MS = 500; static const unsigned int UDMX_SET_CHANNEL_RANGE = 0x0002; -/* - * Called by the AsynchronousSunliteWidget when the transfer completes. - */ -void AsyncCallback(struct libusb_transfer *transfer) { - AsynchronousAnymaWidget *widget = - reinterpret_cast(transfer->user_data); - widget->TransferComplete(transfer); -} - } // namespace // AnymaThreadedSender @@ -121,123 +113,69 @@ bool SynchronousAnymaWidget::SendDMX(const DmxBuffer &buffer) { return m_sender.get() ? m_sender->SendDMX(buffer) : false; } -// AsynchronousAnymaWidget +// AnymaAsyncUsbSender // ----------------------------------------------------------------------------- - -AsynchronousAnymaWidget::AsynchronousAnymaWidget( - LibUsbAdaptor *adaptor, - libusb_device *usb_device, - const string &serial) - : AnymaWidget(adaptor, serial), - m_usb_device(usb_device), - m_usb_handle(NULL), - m_control_setup_buffer(NULL), - m_transfer_state(IDLE) { - m_control_setup_buffer = - new uint8_t[LIBUSB_CONTROL_SETUP_SIZE + DMX_UNIVERSE_SIZE]; - - m_transfer = libusb_alloc_transfer(0); - libusb_ref_device(usb_device); -} - -AsynchronousAnymaWidget::~AsynchronousAnymaWidget() { - bool canceled = false; - while (1) { - ola::thread::MutexLocker locker(&m_mutex); - /* - OLA_INFO << "AsynchronousAnymaWidget shutdown, state is " - << m_transfer_state; - */ - if (m_transfer_state == IDLE || m_transfer_state == DISCONNECTED) { - break; - } - if (!canceled) { - libusb_cancel_transfer(m_transfer); - canceled = true; - OLA_INFO << "Canceling transfer"; - } +class AnymaAsyncUsbSender : public AsyncUsbSender { + public: + AnymaAsyncUsbSender(LibUsbAdaptor *adaptor, + libusb_device *usb_device) + : AsyncUsbSender(adaptor, usb_device) { + m_control_setup_buffer = + new uint8_t[LIBUSB_CONTROL_SETUP_SIZE + DMX_UNIVERSE_SIZE]; } - libusb_free_transfer(m_transfer); - delete[] m_control_setup_buffer; - m_adaptor->CloseHandle(m_usb_handle); - libusb_unref_device(m_usb_device); -} - -bool AsynchronousAnymaWidget::Init() { - bool ok = m_adaptor->OpenDeviceAndClaimInterface( - m_usb_device, 0, &m_usb_handle); - if (!ok) { - return false; + ~AnymaAsyncUsbSender() { + CancelTransfer(); + delete[] m_control_setup_buffer; } - return true; -} -bool AsynchronousAnymaWidget::SendDMX(const DmxBuffer &buffer) { - if (!m_usb_handle) { - OLA_WARN << "AsynchronousAnymaWidget hasn't been initialized"; - return false; - } - ola::thread::MutexLocker locker(&m_mutex); - if (m_transfer_state != IDLE) { - return true; + libusb_device_handle* SetupHandle() { + libusb_device_handle *usb_handle; + bool ok = m_adaptor->OpenDeviceAndClaimInterface( + m_usb_device, 0, &usb_handle); + return ok ? usb_handle : NULL; } - libusb_fill_control_setup( - m_control_setup_buffer, - LIBUSB_REQUEST_TYPE_VENDOR | LIBUSB_RECIPIENT_DEVICE | - LIBUSB_ENDPOINT_OUT, // bmRequestType - UDMX_SET_CHANNEL_RANGE, // bRequest - buffer.Size(), // wValue - 0, // wIndex - buffer.Size()); // wLength - - unsigned int length = DMX_UNIVERSE_SIZE; - buffer.Get(m_control_setup_buffer + LIBUSB_CONTROL_SETUP_SIZE, &length); - - libusb_fill_control_transfer( - m_transfer, - m_usb_handle, - m_control_setup_buffer, - &AsyncCallback, - this, - URB_TIMEOUT_MS); - - int ret = libusb_submit_transfer(m_transfer); - if (ret) { - OLA_WARN << "libusb_submit_transfer returned " << libusb_error_name(ret); - if (ret == LIBUSB_ERROR_NO_DEVICE) { - OLA_INFO << "State now DISCONNECTED"; - m_transfer_state = DISCONNECTED; - } - return false; + void PerformTransfer(const DmxBuffer &buffer) { + libusb_fill_control_setup( + m_control_setup_buffer, + LIBUSB_REQUEST_TYPE_VENDOR | LIBUSB_RECIPIENT_DEVICE | + LIBUSB_ENDPOINT_OUT, // bmRequestType + UDMX_SET_CHANNEL_RANGE, // bRequest + buffer.Size(), // wValue + 0, // wIndex + buffer.Size()); // wLength + + unsigned int length = DMX_UNIVERSE_SIZE; + buffer.Get(m_control_setup_buffer + LIBUSB_CONTROL_SETUP_SIZE, &length); + + FillControlTransfer(m_control_setup_buffer, URB_TIMEOUT_MS); + SubmitTransfer(); } - OLA_INFO << "submit ok, state now IN_PROGRESS"; - m_transfer_state = IN_PROGRESS; - return true; -} -void AsynchronousAnymaWidget::TransferComplete( - struct libusb_transfer *transfer) { - if (transfer != m_transfer) { - OLA_WARN << "Mismatched libusb transfer: " << transfer << " != " - << m_transfer; - return; - } + private: + uint8_t *m_control_setup_buffer; + + DISALLOW_COPY_AND_ASSIGN(AnymaAsyncUsbSender); +}; +// AsynchronousAnymaWidget +// ----------------------------------------------------------------------------- - OLA_WARN << "Transfer returned " << transfer->status; - if (transfer->status != LIBUSB_TRANSFER_COMPLETED) { - OLA_WARN << "Transfer returned " << transfer->status; - } +AsynchronousAnymaWidget::AsynchronousAnymaWidget( + LibUsbAdaptor *adaptor, + libusb_device *usb_device, + const string &serial) + : AnymaWidget(adaptor, serial) { + m_sender.reset(new AnymaAsyncUsbSender(m_adaptor, usb_device)); +} - OLA_INFO << transfer->length << " " << (transfer->actual_length + - LIBUSB_CONTROL_SETUP_SIZE); +bool AsynchronousAnymaWidget::Init() { + return m_sender->Init(); +} - ola::thread::MutexLocker locker(&m_mutex); - m_transfer_state = transfer->status == LIBUSB_TRANSFER_NO_DEVICE ? - DISCONNECTED : IDLE; - OLA_INFO << "State now " << m_transfer_state; +bool AsynchronousAnymaWidget::SendDMX(const DmxBuffer &buffer) { + return m_sender->SendDMX(buffer); } } // namespace usbdmx } // namespace plugin diff --git a/plugins/usbdmx/AnymaWidget.h b/plugins/usbdmx/AnymaWidget.h index 3456e65fd6..3d1a4c5a86 100644 --- a/plugins/usbdmx/AnymaWidget.h +++ b/plugins/usbdmx/AnymaWidget.h @@ -105,34 +105,13 @@ class AsynchronousAnymaWidget : public AnymaWidget { AsynchronousAnymaWidget(LibUsbAdaptor *adaptor, libusb_device *usb_device, const std::string &serial); - ~AsynchronousAnymaWidget(); bool Init(); bool SendDMX(const DmxBuffer &buffer); - /** - * @brief Called from the libusb callback when the asynchronous transfer - * completes. - * @param transfer the completed transfer. - */ - void TransferComplete(struct libusb_transfer *transfer); - private: - enum TransferState { - IDLE, - IN_PROGRESS, - DISCONNECTED, - }; - - libusb_device* const m_usb_device; - libusb_device_handle *m_usb_handle; - uint8_t *m_control_setup_buffer; - - TransferState m_transfer_state; - ola::thread::Mutex m_mutex; - - struct libusb_transfer *m_transfer; + std::auto_ptr m_sender; DISALLOW_COPY_AND_ASSIGN(AsynchronousAnymaWidget); }; diff --git a/plugins/usbdmx/AsyncUsbSender.cpp b/plugins/usbdmx/AsyncUsbSender.cpp new file mode 100644 index 0000000000..c31a26bcf0 --- /dev/null +++ b/plugins/usbdmx/AsyncUsbSender.cpp @@ -0,0 +1,149 @@ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * 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 Library 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * AsyncUsbSender.cpp + * + * Copyright (C) 2014 Simon Newton + */ + +#include "plugins/usbdmx/AsyncUsbSender.h" + +#include "ola/Logging.h" +#include "plugins/usbdmx/LibUsbAdaptor.h" + +namespace ola { +namespace plugin { +namespace usbdmx { + +namespace { + +/* + * Called by libusb when the transfer completes. + */ +void AsyncCallback(struct libusb_transfer *transfer) { + AsyncUsbSender *widget = reinterpret_cast( + transfer->user_data); + widget->TransferComplete(transfer); +} + +} // namespace + +AsyncUsbSender::AsyncUsbSender(LibUsbAdaptor *adaptor, + libusb_device *usb_device) + : m_adaptor(adaptor), + m_usb_device(usb_device), + m_usb_handle(NULL), + m_transfer_state(IDLE) { + m_transfer = libusb_alloc_transfer(0); + libusb_ref_device(usb_device); +} + +AsyncUsbSender::~AsyncUsbSender() { + CancelTransfer(); + m_adaptor->CloseHandle(m_usb_handle); + libusb_unref_device(m_usb_device); +} + +bool AsyncUsbSender::Init() { + m_usb_handle = SetupHandle(); + return m_usb_handle ? true : false; +} + +bool AsyncUsbSender::SendDMX(const DmxBuffer &buffer) { + OLA_INFO << "Call to AsyncUsbSender::SendDMX"; + + if (!m_usb_handle) { + OLA_WARN << "AsynchronousAnymaWidget hasn't been initialized"; + return false; + } + ola::thread::MutexLocker locker(&m_mutex); + if (m_transfer_state != IDLE) { + return true; + } + + PerformTransfer(buffer); + return true; +} + +void AsyncUsbSender::CancelTransfer() { + if (!m_transfer) { + return; + } + + bool canceled = false; + while (1) { + ola::thread::MutexLocker locker(&m_mutex); + if (m_transfer_state == IDLE || m_transfer_state == DISCONNECTED) { + break; + } + if (!canceled) { + libusb_cancel_transfer(m_transfer); + canceled = true; + } + } + + libusb_free_transfer(m_transfer); + m_transfer = NULL; +} + +void AsyncUsbSender::FillControlTransfer(unsigned char *buffer, + unsigned int timeout) { + libusb_fill_control_transfer( + m_transfer, m_usb_handle, buffer, + &AsyncCallback, this, timeout); +} + +void AsyncUsbSender::FillBulkTransfer(unsigned char endpoint, + unsigned char *buffer, + int length, + unsigned int timeout) { + libusb_fill_bulk_transfer( + m_transfer, m_usb_handle, endpoint, + buffer, length, &AsyncCallback, + this, timeout); +} + +int AsyncUsbSender::SubmitTransfer() { + int ret = libusb_submit_transfer(m_transfer); + if (ret) { + OLA_WARN << "libusb_submit_transfer returned " << libusb_error_name(ret); + if (ret == LIBUSB_ERROR_NO_DEVICE) { + m_transfer_state = DISCONNECTED; + } + return false; + } + m_transfer_state = IN_PROGRESS; + return ret; +} + +void AsyncUsbSender::TransferComplete( + struct libusb_transfer *transfer) { + if (transfer != m_transfer) { + OLA_WARN << "Mismatched libusb transfer: " << transfer << " != " + << m_transfer; + return; + } + + if (transfer->status != LIBUSB_TRANSFER_COMPLETED) { + OLA_WARN << "Transfer returned " << transfer->status; + } + + ola::thread::MutexLocker locker(&m_mutex); + m_transfer_state = transfer->status == LIBUSB_TRANSFER_NO_DEVICE ? + DISCONNECTED : IDLE; +} +} // namespace usbdmx +} // namespace plugin +} // namespace ola diff --git a/plugins/usbdmx/AsyncUsbSender.h b/plugins/usbdmx/AsyncUsbSender.h new file mode 100644 index 0000000000..17ca412619 --- /dev/null +++ b/plugins/usbdmx/AsyncUsbSender.h @@ -0,0 +1,92 @@ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * 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 Library 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * AsyncUsbSender.h + * + * Copyright (C) 2014 Simon Newton + */ + +#ifndef PLUGINS_USBDMX_ASYNCUSBSENDER_H_ +#define PLUGINS_USBDMX_ASYNCUSBSENDER_H_ + +#include + +#include "ola/DmxBuffer.h" +#include "ola/base/Macro.h" +#include "ola/thread/Mutex.h" + +namespace ola { +namespace plugin { +namespace usbdmx { + +/** + * @brief + */ +class AsyncUsbSender { + public: + /** + * @brief Create a new AsynchronousAnymaWidget. + * @param usb_device the libusb_device to use for the widget. + */ + AsyncUsbSender(class LibUsbAdaptor* const adaptor, + libusb_device *usb_device); + + virtual ~AsyncUsbSender(); + + bool Init(); + + bool SendDMX(const DmxBuffer &buffer); + + /** + * @brief Called from the libusb callback when the asynchronous transfer + * completes. + * @param transfer the completed transfer. + */ + void TransferComplete(struct libusb_transfer *transfer); + + protected: + class LibUsbAdaptor* const m_adaptor; + libusb_device* const m_usb_device; + + virtual libusb_device_handle* SetupHandle() = 0; + + virtual void PerformTransfer(const DmxBuffer &buffer) = 0; + + void CancelTransfer(); + void FillControlTransfer(unsigned char *buffer, unsigned int timeout); + void FillBulkTransfer(unsigned char endpoint, unsigned char *buffer, + int length, unsigned int timeout); + + int SubmitTransfer(); + + private: + enum TransferState { + IDLE, + IN_PROGRESS, + DISCONNECTED, + }; + + libusb_device_handle *m_usb_handle; + struct libusb_transfer *m_transfer; + + TransferState m_transfer_state; // GUARDED_BY(m_mutex); + ola::thread::Mutex m_mutex; + + DISALLOW_COPY_AND_ASSIGN(AsyncUsbSender); +}; +} // namespace usbdmx +} // namespace plugin +} // namespace ola +#endif // PLUGINS_USBDMX_ASYNCUSBSENDER_H_ diff --git a/plugins/usbdmx/EuroliteProWidget.cpp b/plugins/usbdmx/EuroliteProWidget.cpp index 9d69d1b87c..84b52fd930 100644 --- a/plugins/usbdmx/EuroliteProWidget.cpp +++ b/plugins/usbdmx/EuroliteProWidget.cpp @@ -25,6 +25,7 @@ #include "ola/Constants.h" #include "ola/Logging.h" +#include "plugins/usbdmx/AsyncUsbSender.h" #include "plugins/usbdmx/LibUsbAdaptor.h" #include "plugins/usbdmx/ThreadedUsbSender.h" @@ -40,22 +41,14 @@ namespace { static const unsigned int URB_TIMEOUT_MS = 500; static const uint8_t DMX_LABEL = 6; static const unsigned char ENDPOINT = 0x02; - -/* - * Called by the AsynchronousEuroliteProWidget when the transfer completes. - */ -void AsyncCallback(struct libusb_transfer *transfer) { - AsynchronousEuroliteProWidget *widget = - reinterpret_cast(transfer->user_data); - widget->TransferComplete(transfer); -} +enum { EUROLITE_PRO_FRAME_SIZE = 518 }; /* * Create a Eurolite Pro message to match the supplied DmxBuffer. */ void CreateFrame( const DmxBuffer &buffer, - uint8_t frame[EuroliteProWidget::EUROLITE_PRO_FRAME_SIZE]) { + uint8_t frame[EUROLITE_PRO_FRAME_SIZE]) { unsigned int frame_size = buffer.Size(); // header @@ -68,7 +61,7 @@ void CreateFrame( memset(frame + 5 + frame_size, 0, DMX_UNIVERSE_SIZE - frame_size); // End message delimiter - frame[EuroliteProWidget::EUROLITE_PRO_FRAME_SIZE - 1] = 0xE7; + frame[EUROLITE_PRO_FRAME_SIZE - 1] = 0xE7; } /** @@ -135,7 +128,7 @@ EuroliteProThreadedSender::EuroliteProThreadedSender( bool EuroliteProThreadedSender::TransmitBuffer(libusb_device_handle *handle, const DmxBuffer &buffer) { - uint8_t frame[EuroliteProWidget::EUROLITE_PRO_FRAME_SIZE]; + uint8_t frame[EUROLITE_PRO_FRAME_SIZE]; CreateFrame(buffer, frame); int transferred; @@ -143,10 +136,10 @@ bool EuroliteProThreadedSender::TransmitBuffer(libusb_device_handle *handle, handle, ENDPOINT, frame, - EuroliteProWidget::EUROLITE_PRO_FRAME_SIZE, + EUROLITE_PRO_FRAME_SIZE, &transferred, URB_TIMEOUT_MS); - if (transferred != EuroliteProWidget::EUROLITE_PRO_FRAME_SIZE) { + if (transferred != EUROLITE_PRO_FRAME_SIZE) { // not sure if this is fatal or not OLA_WARN << "EurolitePro driver failed to transfer all data"; } @@ -191,99 +184,61 @@ bool SynchronousEuroliteProWidget::SendDMX(const DmxBuffer &buffer) { return m_sender.get() ? m_sender->SendDMX(buffer) : false; } -// AsynchronousEuroliteProWidget +// EuroliteProAsyncUsbSender // ----------------------------------------------------------------------------- - -AsynchronousEuroliteProWidget::AsynchronousEuroliteProWidget( - class LibUsbAdaptor *adaptor, - libusb_device *usb_device, - const string &serial) - : EuroliteProWidget(adaptor, serial), - m_usb_device(usb_device), - m_usb_handle(NULL), - m_transfer_state(IDLE) { - m_transfer = libusb_alloc_transfer(0); - libusb_ref_device(usb_device); -} - -AsynchronousEuroliteProWidget::~AsynchronousEuroliteProWidget() { - bool canceled = false; - OLA_INFO << "AsynchronousEuroliteProWidget shutdown"; - while (1) { - ola::thread::MutexLocker locker(&m_mutex); - if (m_transfer_state == IDLE) { - break; - } - if (!canceled) { - libusb_cancel_transfer(m_transfer); - canceled = true; - } +class EuroliteProAsyncUsbSender : public AsyncUsbSender { + public: + EuroliteProAsyncUsbSender(LibUsbAdaptor *adaptor, + libusb_device *usb_device) + : AsyncUsbSender(adaptor, usb_device) { } - libusb_free_transfer(m_transfer); - libusb_close(m_usb_handle); - libusb_unref_device(m_usb_device); -} - -bool AsynchronousEuroliteProWidget::Init() { - int interface_number; - if (!LocateInterface(m_usb_device, &interface_number)) { - return false; + ~EuroliteProAsyncUsbSender() { + CancelTransfer(); } - bool ok = m_adaptor->OpenDeviceAndClaimInterface( - m_usb_device, 0, &m_usb_handle); - if (!ok) { - return false; - } - return true; -} + libusb_device_handle* SetupHandle() { + int interface_number; + if (!LocateInterface(m_usb_device, &interface_number)) { + return NULL; + } -bool AsynchronousEuroliteProWidget::SendDMX(const DmxBuffer &buffer) { - OLA_INFO << "Call to AsynchronousEuroliteProWidget::SendDMX"; - if (!m_usb_handle) { - OLA_WARN << "AsynchronousEuroliteProWidget hasn't been initialized"; - return false; + libusb_device_handle *usb_handle; + bool ok = m_adaptor->OpenDeviceAndClaimInterface( + m_usb_device, 0, &usb_handle); + return ok ? usb_handle : NULL; } - ola::thread::MutexLocker locker(&m_mutex); - if (m_transfer_state != IDLE) { - return true; + void PerformTransfer(const DmxBuffer &buffer) { + CreateFrame(buffer, m_tx_frame); + FillBulkTransfer(ENDPOINT, m_tx_frame, EUROLITE_PRO_FRAME_SIZE, + URB_TIMEOUT_MS); + SubmitTransfer(); } - CreateFrame(buffer, m_tx_frame); + private: + uint8_t m_tx_frame[EUROLITE_PRO_FRAME_SIZE]; - libusb_fill_bulk_transfer( - m_transfer, - m_usb_handle, - ENDPOINT, - m_tx_frame, - EUROLITE_PRO_FRAME_SIZE, - &AsyncCallback, - this, - URB_TIMEOUT_MS); + DISALLOW_COPY_AND_ASSIGN(EuroliteProAsyncUsbSender); +}; - int ret = libusb_submit_transfer(m_transfer); - if (ret) { - OLA_WARN << "libusb_submit_transfer returned " << libusb_error_name(ret); - return false; - } - OLA_INFO << "submit ok"; - m_transfer_state = IN_PROGRESS; - return true; +// AsynchronousEuroliteProWidget +// ----------------------------------------------------------------------------- + +AsynchronousEuroliteProWidget::AsynchronousEuroliteProWidget( + LibUsbAdaptor *adaptor, + libusb_device *usb_device, + const string &serial) + : EuroliteProWidget(adaptor, serial) { + m_sender.reset(new EuroliteProAsyncUsbSender(m_adaptor, usb_device)); } -void AsynchronousEuroliteProWidget::TransferComplete( - struct libusb_transfer *transfer) { - if (transfer != m_transfer) { - OLA_WARN << "Mismatched libusb transfer: " << transfer << " != " - << m_transfer; - return; - } +bool AsynchronousEuroliteProWidget::Init() { + return m_sender->Init(); +} - OLA_INFO << "async transfer complete"; - ola::thread::MutexLocker locker(&m_mutex); - m_transfer_state = IDLE; +bool AsynchronousEuroliteProWidget::SendDMX(const DmxBuffer &buffer) { + return m_sender->SendDMX(buffer); } } // namespace usbdmx } // namespace plugin diff --git a/plugins/usbdmx/EuroliteProWidget.h b/plugins/usbdmx/EuroliteProWidget.h index 3c52d43183..a6d2041aab 100644 --- a/plugins/usbdmx/EuroliteProWidget.h +++ b/plugins/usbdmx/EuroliteProWidget.h @@ -58,13 +58,6 @@ class EuroliteProWidget : public BaseWidget { return m_serial; } - /** - * @brief The size of a EurolitePro frame. - * - * This consists of 513 bytes of DMX data + header + code + size(2) + footer - */ - enum { EUROLITE_PRO_FRAME_SIZE = 518 }; - private: std::string m_serial; }; @@ -112,34 +105,13 @@ class AsynchronousEuroliteProWidget: public EuroliteProWidget { AsynchronousEuroliteProWidget(class LibUsbAdaptor *adaptor, libusb_device *usb_device, const std::string &serial); - ~AsynchronousEuroliteProWidget(); bool Init(); bool SendDMX(const DmxBuffer &buffer); - /** - * @brief Called from the libusb callback when the asynchronous transfer - * completes. - * @param transfer the completed transfer. - */ - void TransferComplete(struct libusb_transfer *transfer); - private: - enum TransferState { - IDLE, - IN_PROGRESS, - }; - - libusb_device* const m_usb_device; - libusb_device_handle *m_usb_handle; - - TransferState m_transfer_state; - ola::thread::Mutex m_mutex; - - struct libusb_transfer *m_transfer; - - uint8_t m_tx_frame[EUROLITE_PRO_FRAME_SIZE]; + std::auto_ptr m_sender; DISALLOW_COPY_AND_ASSIGN(AsynchronousEuroliteProWidget); }; diff --git a/plugins/usbdmx/Makefile.mk b/plugins/usbdmx/Makefile.mk index 2928365f39..598e0637d2 100644 --- a/plugins/usbdmx/Makefile.mk +++ b/plugins/usbdmx/Makefile.mk @@ -9,6 +9,8 @@ plugins_usbdmx_libolausbdmx_la_SOURCES = \ plugins/usbdmx/AnymaWidgetFactory.h \ plugins/usbdmx/AsyncPluginImpl.cpp \ plugins/usbdmx/AsyncPluginImpl.h \ + plugins/usbdmx/AsyncUsbSender.cpp \ + plugins/usbdmx/AsyncUsbSender.h \ plugins/usbdmx/EuroliteProWidget.cpp \ plugins/usbdmx/EuroliteProWidget.h \ plugins/usbdmx/EuroliteProWidgetFactory.cpp \ diff --git a/plugins/usbdmx/SunliteWidget.cpp b/plugins/usbdmx/SunliteWidget.cpp index fbb94c9fef..a11b90bf12 100644 --- a/plugins/usbdmx/SunliteWidget.cpp +++ b/plugins/usbdmx/SunliteWidget.cpp @@ -24,6 +24,7 @@ #include "ola/Constants.h" #include "ola/Logging.h" +#include "plugins/usbdmx/AsyncUsbSender.h" #include "plugins/usbdmx/LibUsbAdaptor.h" #include "plugins/usbdmx/ThreadedUsbSender.h" @@ -38,21 +39,13 @@ static const unsigned int CHANNELS_PER_CHUNK = 20; static const unsigned int CHUNK_SIZE = 32; static const uint8_t ENDPOINT = 1; static const unsigned int TIMEOUT = 50; // 50ms is ok - -/* - * Called by the AsynchronousSunliteWidget when the transfer completes. - */ -void AsyncCallback(struct libusb_transfer *transfer) { - AsynchronousSunliteWidget *widget = - reinterpret_cast(transfer->user_data); - widget->TransferComplete(transfer); -} +enum {SUNLITE_PACKET_SIZE = 0x340}; /* * Initialize a USBDMX2 packet */ -void InitPacket(uint8_t packet[SunliteWidget::SUNLITE_PACKET_SIZE]) { - memset(packet, 0, SunliteWidget::SUNLITE_PACKET_SIZE); +void InitPacket(uint8_t packet[SUNLITE_PACKET_SIZE]) { + memset(packet, 0, SUNLITE_PACKET_SIZE); // The packet is divided into 26 chunks of 32 bytes each. Each chunk contains // the data for 20 channels (except the last one which has 12 channels of @@ -85,7 +78,7 @@ void InitPacket(uint8_t packet[SunliteWidget::SUNLITE_PACKET_SIZE]) { * Update a USBDMX2 packet to match the supplied DmxBuffer. */ void UpdatePacket(const DmxBuffer &buffer, - uint8_t packet[SunliteWidget::SUNLITE_PACKET_SIZE]) { + uint8_t packet[SUNLITE_PACKET_SIZE]) { for (unsigned int i = 0; i < buffer.Size(); i++) { packet[(i / CHANNELS_PER_CHUNK) * CHUNK_SIZE + ((i / 4) % 5) * 6 + 3 + (i % 4)] = buffer.Get(i); @@ -106,7 +99,7 @@ class SunliteThreadedSender: public ThreadedUsbSender { libusb_device_handle *handle); private: - uint8_t m_packet[SunliteWidget::SUNLITE_PACKET_SIZE]; + uint8_t m_packet[SUNLITE_PACKET_SIZE]; bool TransmitBuffer(libusb_device_handle *handle, const DmxBuffer &buffer); @@ -127,10 +120,10 @@ bool SunliteThreadedSender::TransmitBuffer(libusb_device_handle *handle, handle, ENDPOINT, (unsigned char*) m_packet, - SunliteWidget::SUNLITE_PACKET_SIZE, + SUNLITE_PACKET_SIZE, &transferred, TIMEOUT); - if (transferred != SunliteWidget::SUNLITE_PACKET_SIZE) { + if (transferred != SUNLITE_PACKET_SIZE) { // not sure if this is fatal or not OLA_WARN << "Sunlite driver failed to transfer all data"; } @@ -168,93 +161,54 @@ bool SynchronousSunliteWidget::SendDMX(const DmxBuffer &buffer) { return m_sender.get() ? m_sender->SendDMX(buffer) : false; } -// AsynchronousSunliteWidget +// SunliteAsyncUsbSender // ----------------------------------------------------------------------------- - -AsynchronousSunliteWidget::AsynchronousSunliteWidget( - LibUsbAdaptor *adaptor, - libusb_device *usb_device) - : SunliteWidget(adaptor), - m_usb_device(usb_device), - m_usb_handle(NULL), - m_transfer_state(IDLE) { - InitPacket(m_packet); - m_transfer = libusb_alloc_transfer(0); - libusb_ref_device(usb_device); -} - -AsynchronousSunliteWidget::~AsynchronousSunliteWidget() { - bool canceled = false; - OLA_INFO << "AsynchronousSunliteWidget shutdown"; - while (1) { - ola::thread::MutexLocker locker(&m_mutex); - if (m_transfer_state == IDLE) { - break; - } - if (!canceled) { - libusb_cancel_transfer(m_transfer); - canceled = true; - } +class SunliteAsyncUsbSender : public AsyncUsbSender { + public: + SunliteAsyncUsbSender(LibUsbAdaptor *adaptor, + libusb_device *usb_device) + : AsyncUsbSender(adaptor, usb_device) { } - libusb_free_transfer(m_transfer); - libusb_close(m_usb_handle); - libusb_unref_device(m_usb_device); -} - -bool AsynchronousSunliteWidget::Init() { - bool ok = m_adaptor->OpenDeviceAndClaimInterface( - m_usb_device, 0, &m_usb_handle); - if (!ok) { - return false; + ~SunliteAsyncUsbSender() { + CancelTransfer(); } - return true; -} -bool AsynchronousSunliteWidget::SendDMX(const DmxBuffer &buffer) { - OLA_INFO << "Call to AsynchronousSunliteWidget::SendDMX"; - if (!m_usb_handle) { - OLA_WARN << "AsynchronousSunliteWidget hasn't been initialized"; - return false; + libusb_device_handle* SetupHandle() { + libusb_device_handle *usb_handle; + bool ok = m_adaptor->OpenDeviceAndClaimInterface( + m_usb_device, 0, &usb_handle); + return ok ? usb_handle : NULL; } - ola::thread::MutexLocker locker(&m_mutex); - if (m_transfer_state != IDLE) { - return true; + void PerformTransfer(const DmxBuffer &buffer) { + UpdatePacket(buffer, m_packet); + FillBulkTransfer(ENDPOINT, m_packet, SUNLITE_PACKET_SIZE, TIMEOUT); + SubmitTransfer(); } - UpdatePacket(buffer, m_packet); - libusb_fill_bulk_transfer( - m_transfer, - m_usb_handle, - ENDPOINT, - (unsigned char*) m_packet, - SUNLITE_PACKET_SIZE, - &AsyncCallback, - this, - TIMEOUT); + private: + uint8_t m_packet[SUNLITE_PACKET_SIZE]; - int ret = libusb_submit_transfer(m_transfer); - if (ret) { - OLA_WARN << "libusb_submit_transfer returned " << libusb_error_name(ret); - return false; - } - OLA_INFO << "submit ok"; - m_transfer_state = IN_PROGRESS; - return true; + DISALLOW_COPY_AND_ASSIGN(SunliteAsyncUsbSender); +}; + +// AsynchronousSunliteWidget +// ----------------------------------------------------------------------------- + +AsynchronousSunliteWidget::AsynchronousSunliteWidget( + LibUsbAdaptor *adaptor, + libusb_device *usb_device) + : SunliteWidget(adaptor) { + m_sender.reset(new SunliteAsyncUsbSender(m_adaptor, usb_device)); } -void AsynchronousSunliteWidget::TransferComplete( - struct libusb_transfer *transfer) { - if (transfer != m_transfer) { - OLA_WARN << "Mismatched libusb transfer: " << transfer << " != " - << m_transfer; - return; - } +bool AsynchronousSunliteWidget::Init() { + return m_sender->Init(); +} - OLA_INFO << "async transfer complete"; - ola::thread::MutexLocker locker(&m_mutex); - m_transfer_state = IDLE; +bool AsynchronousSunliteWidget::SendDMX(const DmxBuffer &buffer) { + return m_sender->SendDMX(buffer); } } // namespace usbdmx } // namespace plugin diff --git a/plugins/usbdmx/SunliteWidget.h b/plugins/usbdmx/SunliteWidget.h index ddfd2bedca..f82568fd11 100644 --- a/plugins/usbdmx/SunliteWidget.h +++ b/plugins/usbdmx/SunliteWidget.h @@ -42,11 +42,6 @@ class SunliteWidget : public BaseWidget { explicit SunliteWidget(LibUsbAdaptor *adaptor) : BaseWidget(adaptor) { } - - /** - * @brief The size of a Sunlite frame. - */ - enum {SUNLITE_PACKET_SIZE = 0x340}; }; @@ -88,34 +83,13 @@ class AsynchronousSunliteWidget: public SunliteWidget { */ AsynchronousSunliteWidget(LibUsbAdaptor *adaptor, libusb_device *usb_device); - ~AsynchronousSunliteWidget(); bool Init(); bool SendDMX(const DmxBuffer &buffer); - /** - * @brief Called from the libusb callback when the asynchronous transfer - * completes. - * @param transfer the completed transfer. - */ - void TransferComplete(struct libusb_transfer *transfer); - private: - enum TransferState { - IDLE, - IN_PROGRESS, - }; - - libusb_device* const m_usb_device; - libusb_device_handle *m_usb_handle; - - TransferState m_transfer_state; - ola::thread::Mutex m_mutex; - - struct libusb_transfer *m_transfer; - - uint8_t m_packet[SUNLITE_PACKET_SIZE]; + std::auto_ptr m_sender; DISALLOW_COPY_AND_ASSIGN(AsynchronousSunliteWidget); }; From aaff7cd6f75d3891aca6e589fb98f50254fa2506 Mon Sep 17 00:00:00 2001 From: Simon Newton Date: Sun, 16 Nov 2014 18:57:13 -0800 Subject: [PATCH 24/34] Add the AsyncUsbSender. --- plugins/usbdmx/AsyncUsbSender.cpp | 2 +- plugins/usbdmx/AsyncUsbSender.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/usbdmx/AsyncUsbSender.cpp b/plugins/usbdmx/AsyncUsbSender.cpp index c31a26bcf0..f8ad13e830 100644 --- a/plugins/usbdmx/AsyncUsbSender.cpp +++ b/plugins/usbdmx/AsyncUsbSender.cpp @@ -14,7 +14,7 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * * AsyncUsbSender.cpp - * + * A Asynchronous DMX USB sender. * Copyright (C) 2014 Simon Newton */ diff --git a/plugins/usbdmx/AsyncUsbSender.h b/plugins/usbdmx/AsyncUsbSender.h index 17ca412619..83e735eb2a 100644 --- a/plugins/usbdmx/AsyncUsbSender.h +++ b/plugins/usbdmx/AsyncUsbSender.h @@ -14,7 +14,7 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * * AsyncUsbSender.h - * + * A Asynchronous DMX USB sender. * Copyright (C) 2014 Simon Newton */ From 66180223967d6e40c6f233968f967f6acfd7c2dd Mon Sep 17 00:00:00 2001 From: Simon Newton Date: Mon, 17 Nov 2014 22:22:59 -0800 Subject: [PATCH 25/34] Clean up some more documentation. --- plugins/usbdmx/AnymaWidget.cpp | 4 +- plugins/usbdmx/AsyncPluginImpl.cpp | 1 - plugins/usbdmx/AsyncUsbSender.cpp | 5 +-- plugins/usbdmx/AsyncUsbSender.h | 58 +++++++++++++++++++++++++++- plugins/usbdmx/EuroliteProWidget.cpp | 4 +- plugins/usbdmx/LibUsbThread.cpp | 13 +------ plugins/usbdmx/LibUsbThread.h | 5 --- plugins/usbdmx/SunliteWidget.cpp | 4 +- plugins/usbdmx/WidgetFactory.h | 2 - 9 files changed, 65 insertions(+), 31 deletions(-) diff --git a/plugins/usbdmx/AnymaWidget.cpp b/plugins/usbdmx/AnymaWidget.cpp index 5cd684ce65..8e31ac46b8 100644 --- a/plugins/usbdmx/AnymaWidget.cpp +++ b/plugins/usbdmx/AnymaWidget.cpp @@ -136,7 +136,7 @@ class AnymaAsyncUsbSender : public AsyncUsbSender { return ok ? usb_handle : NULL; } - void PerformTransfer(const DmxBuffer &buffer) { + bool PerformTransfer(const DmxBuffer &buffer) { libusb_fill_control_setup( m_control_setup_buffer, LIBUSB_REQUEST_TYPE_VENDOR | LIBUSB_RECIPIENT_DEVICE | @@ -150,7 +150,7 @@ class AnymaAsyncUsbSender : public AsyncUsbSender { buffer.Get(m_control_setup_buffer + LIBUSB_CONTROL_SETUP_SIZE, &length); FillControlTransfer(m_control_setup_buffer, URB_TIMEOUT_MS); - SubmitTransfer(); + return SubmitTransfer() == 0; } private: diff --git a/plugins/usbdmx/AsyncPluginImpl.cpp b/plugins/usbdmx/AsyncPluginImpl.cpp index 03e4c6f3fe..c6c3313cec 100644 --- a/plugins/usbdmx/AsyncPluginImpl.cpp +++ b/plugins/usbdmx/AsyncPluginImpl.cpp @@ -235,7 +235,6 @@ bool AsyncPluginImpl::USBDeviceAdded(libusb_device *usb_device) { libusb_get_device_descriptor(usb_device, &descriptor); WidgetFactories::iterator iter = m_widget_factories.begin(); - OLA_INFO << "Checking " << m_widget_factories.size() << " factories"; for (; iter != m_widget_factories.end(); ++iter) { if ((*iter)->DeviceAdded(&m_widget_observer, usb_device, descriptor)) { STLReplacePtr(&m_device_factory_map, usb_device, *iter); diff --git a/plugins/usbdmx/AsyncUsbSender.cpp b/plugins/usbdmx/AsyncUsbSender.cpp index f8ad13e830..5e734f6d64 100644 --- a/plugins/usbdmx/AsyncUsbSender.cpp +++ b/plugins/usbdmx/AsyncUsbSender.cpp @@ -37,7 +37,6 @@ void AsyncCallback(struct libusb_transfer *transfer) { transfer->user_data); widget->TransferComplete(transfer); } - } // namespace AsyncUsbSender::AsyncUsbSender(LibUsbAdaptor *adaptor, @@ -62,8 +61,6 @@ bool AsyncUsbSender::Init() { } bool AsyncUsbSender::SendDMX(const DmxBuffer &buffer) { - OLA_INFO << "Call to AsyncUsbSender::SendDMX"; - if (!m_usb_handle) { OLA_WARN << "AsynchronousAnymaWidget hasn't been initialized"; return false; @@ -142,7 +139,7 @@ void AsyncUsbSender::TransferComplete( ola::thread::MutexLocker locker(&m_mutex); m_transfer_state = transfer->status == LIBUSB_TRANSFER_NO_DEVICE ? - DISCONNECTED : IDLE; + DISCONNECTED : IDLE; } } // namespace usbdmx } // namespace plugin diff --git a/plugins/usbdmx/AsyncUsbSender.h b/plugins/usbdmx/AsyncUsbSender.h index 83e735eb2a..8d6c3f3d7d 100644 --- a/plugins/usbdmx/AsyncUsbSender.h +++ b/plugins/usbdmx/AsyncUsbSender.h @@ -32,21 +32,37 @@ namespace plugin { namespace usbdmx { /** - * @brief + * @brief A base class that send DMX data asynchronously. + * + * This encapsulates much of the asynchronous libusb logic. Subclasses should + * implement the SetupHandle() and PerformTransfer() methods. */ class AsyncUsbSender { public: /** * @brief Create a new AsynchronousAnymaWidget. + * @param adaptor the LibUsbAdaptor to use. * @param usb_device the libusb_device to use for the widget. */ AsyncUsbSender(class LibUsbAdaptor* const adaptor, libusb_device *usb_device); + /** + * @brief Destructor + */ virtual ~AsyncUsbSender(); + /** + * @brief Initialize the sender. + * @returns true if SetupHandle() returned a valid handle, false otherwise. + */ bool Init(); + /** + * @brief Send on frame of DMX data. + * @param buffer the DMX data to send. + * @returns the value of PerformTransfer(). + */ bool SendDMX(const DmxBuffer &buffer); /** @@ -57,18 +73,56 @@ class AsyncUsbSender { void TransferComplete(struct libusb_transfer *transfer); protected: + /** + * @brief The LibUsbAdaptor passed in the constructor. + */ class LibUsbAdaptor* const m_adaptor; + + /** + * @brief The libusb_device passed in the constructor. + */ libusb_device* const m_usb_device; + /** + * @brief Open the device handle. + * @returns A valid libusb_device_handle or NULL if the device could not be + * opened. + */ virtual libusb_device_handle* SetupHandle() = 0; - virtual void PerformTransfer(const DmxBuffer &buffer) = 0; + /** + * @brief Perform the DMX transfer. + * @param buffer the DMX buffer to send. + * @returns true if the transfer was scheduled, false otherwise. + * + * This method is implemented by the subclass. The subclass should call + * FillControlTransfer() / FillBulkTransfer() as appropriate and then call + * SubmitTransfer(). + */ + virtual bool PerformTransfer(const DmxBuffer &buffer) = 0; + /** + * @brief Cancel any pending transfers. + */ void CancelTransfer(); + + /** + * @brief Fill a control transfer. + * @param buffer passed to libusb_fill_control_transfer. + * @param timeout passed to libusb_fill_control_transfer. + */ void FillControlTransfer(unsigned char *buffer, unsigned int timeout); + + /** + * @brief Fill a bulk transfer. + */ void FillBulkTransfer(unsigned char endpoint, unsigned char *buffer, int length, unsigned int timeout); + /** + * @brief Submit the transfer for tx. + * @returns the result of libusb_submit_transfer(). + */ int SubmitTransfer(); private: diff --git a/plugins/usbdmx/EuroliteProWidget.cpp b/plugins/usbdmx/EuroliteProWidget.cpp index 84b52fd930..b2db6c9e7c 100644 --- a/plugins/usbdmx/EuroliteProWidget.cpp +++ b/plugins/usbdmx/EuroliteProWidget.cpp @@ -209,11 +209,11 @@ class EuroliteProAsyncUsbSender : public AsyncUsbSender { return ok ? usb_handle : NULL; } - void PerformTransfer(const DmxBuffer &buffer) { + bool PerformTransfer(const DmxBuffer &buffer) { CreateFrame(buffer, m_tx_frame); FillBulkTransfer(ENDPOINT, m_tx_frame, EUROLITE_PRO_FRAME_SIZE, URB_TIMEOUT_MS); - SubmitTransfer(); + return SubmitTransfer() == 0; } private: diff --git a/plugins/usbdmx/LibUsbThread.cpp b/plugins/usbdmx/LibUsbThread.cpp index c384f5cb1c..a56c154272 100644 --- a/plugins/usbdmx/LibUsbThread.cpp +++ b/plugins/usbdmx/LibUsbThread.cpp @@ -47,10 +47,12 @@ void *LibUsbThread::Run() { } void LibUsbThread::LaunchThread() { + OLA_INFO << "-- Starting libusb thread"; Start(); } void LibUsbThread::JoinThread() { + OLA_INFO << "-- Stopping libusb thread"; Join(); m_term = false; } @@ -66,10 +68,6 @@ LibUsbHotplugThread::LibUsbHotplugThread(libusb_context *context, m_hotplug_handle(0), m_callback_fn(callback_fn), m_user_data(user_data) { - OLA_INFO << "-- Starting libusb thread"; -} - -LibUsbHotplugThread::~LibUsbHotplugThread() { } bool LibUsbHotplugThread::Init() { @@ -85,13 +83,11 @@ bool LibUsbHotplugThread::Init() { OLA_WARN << "Error creating a hotplug callback"; return false; } - OLA_INFO << "libusb_hotplug_register_callback passed"; LaunchThread(); return true; } void LibUsbHotplugThread::Shutdown() { - OLA_INFO << "-- Stopping libusb thread"; SetTerminate(); libusb_hotplug_deregister_callback(Context(), m_hotplug_handle); JoinThread(); @@ -109,24 +105,19 @@ void LibUsbHotplugThread::CloseHandle(libusb_device_handle *handle) { void LibUsbSimpleThread::OpenHandle() { m_device_count++; if (m_device_count == 1) { - OLA_INFO << "-- Starting libusb thread"; LaunchThread(); } } void LibUsbSimpleThread::CloseHandle(libusb_device_handle *handle) { - OLA_INFO << "LibUsbSimpleThread::CloseHandle, count is " << m_device_count; if (m_device_count == 1) { SetTerminate(); } libusb_close(handle); if (m_device_count == 1) { - OLA_INFO << "-- Stopping libusb thread"; JoinThread(); } m_device_count--; - OLA_INFO << "exit LibUsbSimpleThread::CloseHandle, count is " - << m_device_count; } } // namespace usbdmx } // namespace plugin diff --git a/plugins/usbdmx/LibUsbThread.h b/plugins/usbdmx/LibUsbThread.h index 85b8d97238..e764c592a7 100644 --- a/plugins/usbdmx/LibUsbThread.h +++ b/plugins/usbdmx/LibUsbThread.h @@ -149,11 +149,6 @@ class LibUsbHotplugThread : public LibUsbThread { libusb_hotplug_callback_fn callback_fn, void *user_data); - /** - * @brief Destructor. - */ - ~LibUsbHotplugThread(); - bool Init(); void Shutdown(); diff --git a/plugins/usbdmx/SunliteWidget.cpp b/plugins/usbdmx/SunliteWidget.cpp index a11b90bf12..a6b1a8ccfe 100644 --- a/plugins/usbdmx/SunliteWidget.cpp +++ b/plugins/usbdmx/SunliteWidget.cpp @@ -181,10 +181,10 @@ class SunliteAsyncUsbSender : public AsyncUsbSender { return ok ? usb_handle : NULL; } - void PerformTransfer(const DmxBuffer &buffer) { + bool PerformTransfer(const DmxBuffer &buffer) { UpdatePacket(buffer, m_packet); FillBulkTransfer(ENDPOINT, m_packet, SUNLITE_PACKET_SIZE, TIMEOUT); - SubmitTransfer(); + return SubmitTransfer() == 0; } private: diff --git a/plugins/usbdmx/WidgetFactory.h b/plugins/usbdmx/WidgetFactory.h index 1e091724fd..61f9a91559 100644 --- a/plugins/usbdmx/WidgetFactory.h +++ b/plugins/usbdmx/WidgetFactory.h @@ -205,13 +205,11 @@ bool BaseWidgetFactory::AddWidget(WidgetObserver *observer, libusb_device *usb_device, WidgetType *widget) { if (!widget->Init()) { - OLA_INFO << "widget init failed"; delete widget; return false; } if (!observer->NewWidget(widget)) { - OLA_INFO << "observer rejected widget"; delete widget; return false; } From b54bcb1ebdc9e7faa10764ba59c7e9e39c2a9da1 Mon Sep 17 00:00:00 2001 From: Simon Newton Date: Tue, 18 Nov 2014 23:05:57 -0800 Subject: [PATCH 26/34] Add the async velleman plugin. --- plugins/usbdmx/AsyncUsbSender.cpp | 11 + plugins/usbdmx/AsyncUsbSender.h | 9 + plugins/usbdmx/VellemanWidget.cpp | 469 +++++++++++++++++++----------- plugins/usbdmx/VellemanWidget.h | 22 +- 4 files changed, 315 insertions(+), 196 deletions(-) diff --git a/plugins/usbdmx/AsyncUsbSender.cpp b/plugins/usbdmx/AsyncUsbSender.cpp index 5e734f6d64..a97fd83ac7 100644 --- a/plugins/usbdmx/AsyncUsbSender.cpp +++ b/plugins/usbdmx/AsyncUsbSender.cpp @@ -112,6 +112,16 @@ void AsyncUsbSender::FillBulkTransfer(unsigned char endpoint, this, timeout); } +void AsyncUsbSender::FillInterruptTransfer(unsigned char endpoint, + unsigned char *buffer, + int length, + unsigned int timeout) { + libusb_fill_interrupt_transfer( + m_transfer, m_usb_handle, endpoint, + buffer, length, &AsyncCallback, + this, timeout); +} + int AsyncUsbSender::SubmitTransfer() { int ret = libusb_submit_transfer(m_transfer); if (ret) { @@ -140,6 +150,7 @@ void AsyncUsbSender::TransferComplete( ola::thread::MutexLocker locker(&m_mutex); m_transfer_state = transfer->status == LIBUSB_TRANSFER_NO_DEVICE ? DISCONNECTED : IDLE; + PostTransferHook(); } } // namespace usbdmx } // namespace plugin diff --git a/plugins/usbdmx/AsyncUsbSender.h b/plugins/usbdmx/AsyncUsbSender.h index 8d6c3f3d7d..8231163cf7 100644 --- a/plugins/usbdmx/AsyncUsbSender.h +++ b/plugins/usbdmx/AsyncUsbSender.h @@ -101,6 +101,9 @@ class AsyncUsbSender { */ virtual bool PerformTransfer(const DmxBuffer &buffer) = 0; + + virtual void PostTransferHook() {} + /** * @brief Cancel any pending transfers. */ @@ -119,6 +122,12 @@ class AsyncUsbSender { void FillBulkTransfer(unsigned char endpoint, unsigned char *buffer, int length, unsigned int timeout); + /** + * @brief Fill a interrupt transfer. + */ + void FillInterruptTransfer(unsigned char endpoint, unsigned char *buffer, + int length, unsigned int timeout); + /** * @brief Submit the transfer for tx. * @returns the result of libusb_submit_transfer(). diff --git a/plugins/usbdmx/VellemanWidget.cpp b/plugins/usbdmx/VellemanWidget.cpp index 758c0ff7f0..bceb8c8c16 100644 --- a/plugins/usbdmx/VellemanWidget.cpp +++ b/plugins/usbdmx/VellemanWidget.cpp @@ -27,6 +27,8 @@ #include "ola/Logging.h" #include "ola/Constants.h" +#include "ola/StringUtils.h" +#include "plugins/usbdmx/AsyncUsbSender.h" #include "plugins/usbdmx/LibUsbAdaptor.h" #include "plugins/usbdmx/ThreadedUsbSender.h" @@ -43,16 +45,123 @@ static const unsigned char ENDPOINT = 0x01; static const unsigned int URB_TIMEOUT_MS = 25; static const int CONFIGURATION = 1; static const int INTERFACE = 0; +static const unsigned int DEFAULT_CHUNK_SIZE = 8; static const unsigned int UPGRADED_CHUNK_SIZE = 64; +static const unsigned int HEADER_SIZE = 2; + +// Message types +// Length: 8 or 64 for the extended version. +// Data: [2] [slot N] [slot N +1] [slot N + 2] ... [slot N + 6] +static const uint8_t INTERMEDIATE_FRAME_MSG = 2; + +// Length: 8 or 64 for the extended version. +// Data: [3] [slot N] [undef] [undef] [undef] ... +static const uint8_t SINGLE_SLOT_MSG = 3; + +// This must be used to indicate a new DMX512 frame. +// Length: 8 or 64 for the extended version. +// Data: [4] [number of leading 0s] [slot N] [slot N + 1] [slot N + 2] ... +static const uint8_t BREAK_MSG = 4; + +// Length: 8 or 64 for the extended version. +// [5] [number of leading 0s] [slot N] [slot N + 1] [slot N + 2] ... +static const uint8_t INTERMEDIATE_COMPRESSED_FRAME_MSG = 5; + +// Length: 64, only defined for the extended version. +// Data: [4] [data size] [slot 0] [slot 1] [slot 2] ... +static const uint8_t VARIABLE_FRAME_CONTINUATION_MSG = 6; + +// Length: 64, only defined for the extended version. +// Data: [4] [data size] [slot 0] [slot 1] [slot 2] ... +static const uint8_t FULL_FRAME_MSG = 7; /* - * Called by the AsynchronousSunliteWidget when the transfer completes. -void AsyncCallback(struct libusb_transfer *transfer) { - AsynchronousVellemanWidget *widget = - reinterpret_cast(transfer->user_data); - widget->TransferComplete(transfer); + * @brief Attempt to open a handle to a Velleman widget. + * @param adaptor the LibUsbAdaptor to use. + * @param usb_device the libusb_device to use. + * @param[out] The chunk size of the device, this determines if the enhanced + * firmware is present. + * @returns A libusb_device_handle of NULL if it failed. + */ +libusb_device_handle *OpenVellemenWidget(LibUsbAdaptor *adaptor, + libusb_device *usb_device, + unsigned int *chunk_size) { + libusb_config_descriptor *config; + if (libusb_get_active_config_descriptor(usb_device, &config)) { + OLA_WARN << "Could not get active config descriptor"; + return NULL; + } + + // determine the max packet size, see + // http://opendmx.net/index.php/Velleman_K8062_Upgrade + // The standard size is 8. + *chunk_size = DEFAULT_CHUNK_SIZE; + if (config && + config->interface && + config->interface->altsetting && + config->interface->altsetting->endpoint) { + uint16_t max_packet_size = + config->interface->altsetting->endpoint->wMaxPacketSize; + OLA_DEBUG << "Velleman K8062 max packet size is " << max_packet_size; + if (max_packet_size == UPGRADED_CHUNK_SIZE) + // this means the upgrade is present + *chunk_size = max_packet_size; + } + libusb_free_config_descriptor(config); + + libusb_device_handle *usb_handle; + bool ok = adaptor->OpenDevice(usb_device, &usb_handle); + if (!ok) { + return NULL; + } + + if (libusb_kernel_driver_active(usb_handle, 0)) { + if (libusb_detach_kernel_driver(usb_handle, 0)) { + OLA_WARN << "Failed to detach kernel driver"; + adaptor->CloseHandle(usb_handle); + return NULL; + } + } + + // this device only has one configuration + int ret_code = libusb_set_configuration(usb_handle, CONFIGURATION); + if (ret_code) { + OLA_WARN << "Velleman set config failed, with libusb error code " + << ret_code; + adaptor->CloseHandle(usb_handle); + return NULL; + } + + if (libusb_claim_interface(usb_handle, INTERFACE)) { + OLA_WARN << "Failed to claim Velleman usb device"; + adaptor->CloseHandle(usb_handle); + return NULL; + } + return usb_handle; } + +/** + * @brief Count the number of leading 0s in a block of data. + * ma */ +unsigned int CountLeadingZeros(const uint8_t *data, unsigned int data_length, + unsigned int chunk_size) { + unsigned int leading_zero_count = 0; + + // This could be up to 254 for the standard interface but then the shutdown + // process gets wacky. Limit it to 100 for the standard and 255 for the + // extended. + unsigned int max_leading_zeros = chunk_size == UPGRADED_CHUNK_SIZE ? + 254 : 100; + unsigned int rest_of_chunk = chunk_size - 2; + + while (leading_zero_count < max_leading_zeros && + leading_zero_count + rest_of_chunk < data_length && + data[leading_zero_count] == 0) { + leading_zero_count++; + } + return leading_zero_count; +} } // namespace @@ -76,23 +185,18 @@ class VellemanThreadedSender: public ThreadedUsbSender { bool TransmitBuffer(libusb_device_handle *handle, const DmxBuffer &buffer); - bool SendDataChunk(libusb_device_handle *handle, uint8_t *usb_data, + bool SendDataChunk(libusb_device_handle *handle, + uint8_t *usb_data, unsigned int chunk_size); }; bool VellemanThreadedSender::TransmitBuffer(libusb_device_handle *handle, const DmxBuffer &buffer) { unsigned char usb_data[m_chunk_size]; - unsigned int size = buffer.Size(); + const unsigned int size = buffer.Size(); const uint8_t *data = buffer.GetRaw(); unsigned int i = 0; - unsigned int n; - // this could be up to 254 for the standard interface but then the shutdown - // process gets wacky. Limit it to 100 for the standard and 255 for the - // extended. - unsigned int max_compressed_channels = m_chunk_size == UPGRADED_CHUNK_SIZE ? - 254 : 100; unsigned int compressed_channel_count = m_chunk_size - 2; unsigned int channel_count = m_chunk_size - 1; @@ -100,40 +204,35 @@ bool VellemanThreadedSender::TransmitBuffer(libusb_device_handle *handle, if (m_chunk_size == UPGRADED_CHUNK_SIZE && size <= m_chunk_size - 2) { // if the upgrade is present and we can fit the data in a single packet - // use the 7 message type - usb_data[0] = 7; + // use FULL_FRAME_MSG. + usb_data[0] = FULL_FRAME_MSG; usb_data[1] = size; // number of channels in packet - memcpy(usb_data + 2, data, std::min(size, m_chunk_size - 2)); + memcpy(usb_data + HEADER_SIZE, data, std::min(size, m_chunk_size - 2)); } else { - // otherwise use 4 to signal the start of frame - for (n = 0; - n < max_compressed_channels && n < size - compressed_channel_count - && !data[n]; - n++) { - } - usb_data[0] = 4; - usb_data[1] = n + 1; // include start code - memcpy(usb_data + 2, data + n, compressed_channel_count); - i += n + compressed_channel_count; + unsigned int leading_zero_count = CountLeadingZeros( + data, size, m_chunk_size); + usb_data[0] = BREAK_MSG; + usb_data[1] = leading_zero_count + 1; // include start code + memcpy(usb_data + HEADER_SIZE, data + leading_zero_count, + compressed_channel_count); + i += leading_zero_count + compressed_channel_count; } if (!SendDataChunk(handle, usb_data, m_chunk_size)) return false; while (i < size - channel_count) { - for (n = 0; - n < max_compressed_channels && n + i < size - compressed_channel_count - && !data[i + n]; - n++) { - } - if (n) { + unsigned int leading_zero_count = CountLeadingZeros( + data + i, size - i, m_chunk_size); + if (leading_zero_count) { // we have leading zeros - usb_data[0] = 5; - usb_data[1] = n; - memcpy(usb_data + 2, data + i + n, compressed_channel_count); - i += n + compressed_channel_count; + usb_data[0] = INTERMEDIATE_COMPRESSED_FRAME_MSG; + usb_data[1] = leading_zero_count; + memcpy(usb_data + HEADER_SIZE, data + i + leading_zero_count, + compressed_channel_count); + i += leading_zero_count + compressed_channel_count; } else { - usb_data[0] = 2; + usb_data[0] = INTERMEDIATE_FRAME_MSG; memcpy(usb_data + 1, data + i, channel_count); i += channel_count; } @@ -145,16 +244,16 @@ bool VellemanThreadedSender::TransmitBuffer(libusb_device_handle *handle, if (m_chunk_size == UPGRADED_CHUNK_SIZE) { // if running in extended mode we can use the 6 message type to send // everything at once. - usb_data[0] = 6; + usb_data[0] = VARIABLE_FRAME_CONTINUATION_MSG; usb_data[1] = size - i; - memcpy(usb_data + 2, data + i, size - i); + memcpy(usb_data + HEADER_SIZE, data + i, size - i); if (!SendDataChunk(handle, usb_data, m_chunk_size)) return false; } else { // else we use the 3 message type to send one at a time for (; i != size; i++) { - usb_data[0] = 3; + usb_data[0] = SINGLE_SLOT_MSG; usb_data[1] = data[i]; if (!SendDataChunk(handle, usb_data, m_chunk_size)) return false; @@ -192,57 +291,11 @@ SynchronousVellemanWidget::SynchronousVellemanWidget( } bool SynchronousVellemanWidget::Init() { - libusb_device_handle *usb_handle; - - bool ok = m_adaptor->OpenDevice(m_usb_device, &usb_handle); - if (!ok) { - return false; - } - - if (libusb_kernel_driver_active(usb_handle, 0)) { - if (libusb_detach_kernel_driver(usb_handle, 0)) { - OLA_WARN << "Failed to detach kernel driver"; - libusb_close(usb_handle); - return false; - } - } - - // this device only has one configuration - int ret_code = libusb_set_configuration(usb_handle, CONFIGURATION); - if (ret_code) { - OLA_WARN << "Velleman set config failed, with libusb error code " << - ret_code; - libusb_close(usb_handle); - return false; - } - - libusb_config_descriptor *config; - if (libusb_get_active_config_descriptor(m_usb_device, &config)) { - OLA_WARN << "Could not get active config descriptor"; - libusb_close(usb_handle); - return false; - } - - // determine the max packet size, see - // http://opendmx.net/index.php/Velleman_K8062_Upgrade - // The standard size is 8. - unsigned int chunk_size = 8; - if (config && - config->interface && - config->interface->altsetting && - config->interface->altsetting->endpoint) { - uint16_t max_packet_size = - config->interface->altsetting->endpoint->wMaxPacketSize; - OLA_DEBUG << "Velleman K8062 max packet size is " << max_packet_size; - if (max_packet_size == UPGRADED_CHUNK_SIZE) - // this means the upgrade is present - chunk_size = max_packet_size; - } - libusb_free_config_descriptor(config); + unsigned int chunk_size = DEFAULT_CHUNK_SIZE; + libusb_device_handle *usb_handle = OpenVellemenWidget( + m_adaptor, m_usb_device, &chunk_size); - if (libusb_claim_interface(usb_handle, INTERFACE)) { - OLA_WARN << "Failed to claim Velleman usb device"; - libusb_close(usb_handle); + if (!usb_handle) { return false; } @@ -259,111 +312,177 @@ bool SynchronousVellemanWidget::SendDMX(const DmxBuffer &buffer) { return m_sender.get() ? m_sender->SendDMX(buffer) : false; } -// AsynchronousVellemanWidget +// VellemanAsyncUsbSender // ----------------------------------------------------------------------------- +class VellemanAsyncUsbSender : public AsyncUsbSender { + public: + VellemanAsyncUsbSender(LibUsbAdaptor *adaptor, + libusb_device *usb_device) + : AsyncUsbSender(adaptor, usb_device), + m_chunk_size(DEFAULT_CHUNK_SIZE), + m_buffer_offset(0) { + } -AsynchronousVellemanWidget::AsynchronousVellemanWidget( - LibUsbAdaptor *adaptor, - libusb_device *usb_device) - : VellemanWidget(adaptor), - m_usb_device(usb_device), - m_usb_handle(NULL), - m_control_setup_buffer(NULL), - m_transfer_state(IDLE) { - m_control_setup_buffer = - new uint8_t[LIBUSB_CONTROL_SETUP_SIZE + DMX_UNIVERSE_SIZE]; - - m_transfer = libusb_alloc_transfer(0); - libusb_ref_device(usb_device); -} + ~VellemanAsyncUsbSender() { + CancelTransfer(); + } -AsynchronousVellemanWidget::~AsynchronousVellemanWidget() { - bool canceled = false; - OLA_INFO << "AsynchronousVellemanWidget shutdown"; - while (1) { - ola::thread::MutexLocker locker(&m_mutex); - if (m_transfer_state == IDLE) { - break; - } - if (!canceled) { - libusb_cancel_transfer(m_transfer); - canceled = true; + libusb_device_handle* SetupHandle() { + return OpenVellemenWidget(m_adaptor, m_usb_device, &m_chunk_size); + } + + bool PerformTransfer(const DmxBuffer &buffer); + + void PostTransferHook() { + if (m_buffer_offset < m_tx_buffer.Size()) { + ContinueTransfer(); + } else if (m_buffer_offset >= m_tx_buffer.Size()) { + m_buffer_offset = 0; + m_tx_buffer.Reset(); } } - libusb_free_transfer(m_transfer); - delete[] m_control_setup_buffer; - libusb_close(m_usb_handle); - libusb_unref_device(m_usb_device); -} + private: + // These are set once we known the type of device we're talking to. + unsigned int m_chunk_size; -bool AsynchronousVellemanWidget::Init() { - bool ok = m_adaptor->OpenDeviceAndClaimInterface( - m_usb_device, 0, &m_usb_handle); - if (!ok) { - return false; + DmxBuffer m_tx_buffer; + // This tracks where were are in m_tx_buffer. A value of 0 means we're at the + // state of a DMX frame. + unsigned int m_buffer_offset; + + bool ContinueTransfer(); + + bool SendInitialChunk(const DmxBuffer &buffer); + bool SendIntermediateChunk(); + bool SendSingleSlotChunk(); + + bool SendChunk(uint8_t *usb_data, unsigned int data_length) { + FormatData(&std::cout, usb_data, data_length); + FillInterruptTransfer(ENDPOINT, usb_data, data_length, URB_TIMEOUT_MS); + return SubmitTransfer() == 0; } - return true; -} -bool AsynchronousVellemanWidget::SendDMX(const DmxBuffer &buffer) { - OLA_INFO << "Call to AsynchronousVellemanWidget::SendDMX"; + DISALLOW_COPY_AND_ASSIGN(VellemanAsyncUsbSender); +}; - if (!m_usb_handle) { - OLA_WARN << "AsynchronousVellemanWidget hasn't been initialized"; - return false; +bool VellemanAsyncUsbSender::PerformTransfer(const DmxBuffer &buffer) { + if (m_buffer_offset == 0) { + return SendInitialChunk(buffer); } + // Otherwise we're part way through a transfer. + return ContinueTransfer(); +} - (void) buffer; - return false; - /* - ola::thread::MutexLocker locker(&m_mutex); - if (m_transfer_state != IDLE) { - return true; +bool VellemanAsyncUsbSender::SendInitialChunk(const DmxBuffer &buffer) { + unsigned char usb_data[m_chunk_size]; + unsigned int length = m_chunk_size - HEADER_SIZE; + + if (m_chunk_size == UPGRADED_CHUNK_SIZE && + buffer.Size() <= m_chunk_size - HEADER_SIZE) { + // If the upgrade is present and we can fit the data in a single chunk + // use the FULL_FRAME_MSG message type. + usb_data[0] = FULL_FRAME_MSG; + usb_data[1] = buffer.Size(); // number of slots in the frame. + buffer.Get(usb_data + HEADER_SIZE, &length); + memset(usb_data + HEADER_SIZE + length, 0, + m_chunk_size - length - HEADER_SIZE); + } else { + // Otherwise use BREAK_MSG to signal the start of frame. + unsigned int leading_zero_count = CountLeadingZeros( + buffer.GetRaw(), buffer.Size(), m_chunk_size); + usb_data[0] = BREAK_MSG; + usb_data[1] = leading_zero_count + 1; // include start code + buffer.GetRange(leading_zero_count, usb_data + HEADER_SIZE, &length); + memset(usb_data + HEADER_SIZE + length, 0, + m_chunk_size - length - HEADER_SIZE); + + unsigned int slots_sent = leading_zero_count + length; + if (slots_sent < buffer.Size()) { + // There are more frames to send. + m_tx_buffer.Set(buffer); + m_buffer_offset = slots_sent; + } } + return SendChunk(usb_data, m_chunk_size) == 0; +} - libusb_fill_control_setup( - m_control_setup_buffer, - LIBUSB_REQUEST_TYPE_VENDOR | LIBUSB_RECIPIENT_DEVICE | - LIBUSB_ENDPOINT_OUT, // bmRequestType - UDMX_SET_CHANNEL_RANGE, // bRequest - buffer.Size(), // wValue - 0, // wIndex - buffer.Size()); // wLength - - unsigned int length = DMX_UNIVERSE_SIZE; - buffer.Get(m_control_setup_buffer + LIBUSB_CONTROL_SETUP_SIZE, &length); - - libusb_fill_control_transfer( - m_transfer, - m_usb_handle, - m_control_setup_buffer, - &AsyncCallback, - this, - URB_TIMEOUT_MS); +bool VellemanAsyncUsbSender::SendIntermediateChunk() { + unsigned char usb_data[m_chunk_size]; - int ret = libusb_submit_transfer(m_transfer); - if (ret) { - OLA_WARN << "libusb_submit_transfer returned " << libusb_error_name(ret); - return false; + // Intermediate frame. + unsigned int zeros = CountLeadingZeros( + m_tx_buffer.GetRaw() + m_buffer_offset, + m_tx_buffer.Size() - m_buffer_offset, + m_chunk_size); + + unsigned int length = m_chunk_size - 1; + if (zeros) { + // we have leading zeros + usb_data[0] = INTERMEDIATE_COMPRESSED_FRAME_MSG; + usb_data[1] = zeros; + length--; + m_tx_buffer.GetRange(m_buffer_offset + zeros, usb_data + HEADER_SIZE, + &length); + m_buffer_offset += zeros + length; + } else { + usb_data[0] = INTERMEDIATE_FRAME_MSG; + m_tx_buffer.GetRange(m_buffer_offset, usb_data + 1, &length); + memset(usb_data + 1 + length, 0, m_chunk_size - length - 1); + m_buffer_offset += length; } - OLA_INFO << "submit ok"; - m_transfer_state = IN_PROGRESS; - return true; - */ + return SendChunk(usb_data, m_chunk_size) == 0; } -void AsynchronousVellemanWidget::TransferComplete( - struct libusb_transfer *transfer) { - if (transfer != m_transfer) { - OLA_WARN << "Mismatched libusb transfer: " << transfer << " != " - << m_transfer; - return; +bool VellemanAsyncUsbSender::SendSingleSlotChunk() { + unsigned char usb_data[m_chunk_size]; + memset(usb_data, 0, m_chunk_size); + + usb_data[0] = SINGLE_SLOT_MSG; + usb_data[1] = m_tx_buffer.Get(m_buffer_offset); + m_buffer_offset++; + return SendChunk(usb_data, m_chunk_size) == 0; +} + +bool VellemanAsyncUsbSender::ContinueTransfer() { + if (m_buffer_offset + m_chunk_size + 1 < m_tx_buffer.Size()) { + return SendIntermediateChunk(); + } + + if (m_chunk_size == UPGRADED_CHUNK_SIZE) { + // If running in extended mode we can use the 6 message type to send + // everything at once. + unsigned char usb_data[m_chunk_size]; + unsigned int length = m_chunk_size - HEADER_SIZE; + + usb_data[0] = VARIABLE_FRAME_CONTINUATION_MSG; + usb_data[1] = m_tx_buffer.Size() - m_buffer_offset; + m_tx_buffer.GetRange(m_buffer_offset, usb_data + HEADER_SIZE, &length); + memset(usb_data + HEADER_SIZE + length, 0, + m_chunk_size - length - HEADER_SIZE); + return SendChunk(usb_data, m_chunk_size) == 0; + } else { + // The trailing slots are sendt individually. + return SendSingleSlotChunk(); } +} + +// AsynchronousVellemanWidget +// ----------------------------------------------------------------------------- - OLA_INFO << "async transfer complete"; - ola::thread::MutexLocker locker(&m_mutex); - m_transfer_state = IDLE; +AsynchronousVellemanWidget::AsynchronousVellemanWidget( + LibUsbAdaptor *adaptor, + libusb_device *usb_device) + : VellemanWidget(adaptor) { + m_sender.reset(new VellemanAsyncUsbSender(m_adaptor, usb_device)); +} + +bool AsynchronousVellemanWidget::Init() { + return m_sender->Init(); +} + +bool AsynchronousVellemanWidget::SendDMX(const DmxBuffer &buffer) { + return m_sender->SendDMX(buffer); } } // namespace usbdmx } // namespace plugin diff --git a/plugins/usbdmx/VellemanWidget.h b/plugins/usbdmx/VellemanWidget.h index c1a75a1b99..e29827f8ad 100644 --- a/plugins/usbdmx/VellemanWidget.h +++ b/plugins/usbdmx/VellemanWidget.h @@ -82,33 +82,13 @@ class AsynchronousVellemanWidget : public VellemanWidget { */ AsynchronousVellemanWidget(LibUsbAdaptor *adaptor, libusb_device *usb_device); - ~AsynchronousVellemanWidget(); bool Init(); bool SendDMX(const DmxBuffer &buffer); - /** - * @brief Called from the libusb callback when the asynchronous transfer - * completes. - * @param transfer the completed transfer. - */ - void TransferComplete(struct libusb_transfer *transfer); - private: - enum TransferState { - IDLE, - IN_PROGRESS, - }; - - libusb_device* const m_usb_device; - libusb_device_handle *m_usb_handle; - uint8_t *m_control_setup_buffer; - - TransferState m_transfer_state; - ola::thread::Mutex m_mutex; - - struct libusb_transfer *m_transfer; + std::auto_ptr m_sender; DISALLOW_COPY_AND_ASSIGN(AsynchronousVellemanWidget); }; From 5107c0e794c08b684c50ad98441b258bf858f823 Mon Sep 17 00:00:00 2001 From: Simon Newton Date: Fri, 21 Nov 2014 18:50:28 -0800 Subject: [PATCH 27/34] Move the libusb calls behind an adaptor class. --- plugins/usbdmx/AnymaWidget.cpp | 25 +- plugins/usbdmx/AsyncUsbSender.cpp | 34 +-- plugins/usbdmx/EuroliteProWidget.cpp | 42 +-- plugins/usbdmx/LibUsbAdaptor.cpp | 208 ++++++++++++-- plugins/usbdmx/LibUsbAdaptor.h | 407 ++++++++++++++++++++++++++- plugins/usbdmx/SunliteWidget.cpp | 19 +- plugins/usbdmx/VellemanWidget.cpp | 41 ++- 7 files changed, 660 insertions(+), 116 deletions(-) diff --git a/plugins/usbdmx/AnymaWidget.cpp b/plugins/usbdmx/AnymaWidget.cpp index 8e31ac46b8..5e1115ea94 100644 --- a/plugins/usbdmx/AnymaWidget.cpp +++ b/plugins/usbdmx/AnymaWidget.cpp @@ -50,23 +50,23 @@ static const unsigned int UDMX_SET_CHANNEL_RANGE = 0x0002; */ class AnymaThreadedSender: public ThreadedUsbSender { public: - AnymaThreadedSender(libusb_device *usb_device, - libusb_device_handle *handle); + AnymaThreadedSender(LibUsbAdaptor *adaptor, + libusb_device *usb_device, + libusb_device_handle *handle) + : ThreadedUsbSender(usb_device, handle), + m_adaptor(adaptor) { + } private: + LibUsbAdaptor* const m_adaptor; + bool TransmitBuffer(libusb_device_handle *handle, const DmxBuffer &buffer); }; -AnymaThreadedSender::AnymaThreadedSender( - libusb_device *usb_device, - libusb_device_handle *usb_handle) - : ThreadedUsbSender(usb_device, usb_handle) { -} - bool AnymaThreadedSender::TransmitBuffer(libusb_device_handle *handle, const DmxBuffer &buffer) { - int r = libusb_control_transfer( + int r = m_adaptor->ControlTransfer( handle, LIBUSB_REQUEST_TYPE_VENDOR | LIBUSB_RECIPIENT_DEVICE | LIBUSB_ENDPOINT_OUT, // bmRequestType @@ -101,7 +101,7 @@ bool SynchronousAnymaWidget::Init() { } std::auto_ptr sender( - new AnymaThreadedSender(m_usb_device, usb_handle)); + new AnymaThreadedSender(m_adaptor, m_usb_device, usb_handle)); if (!sender->Start()) { return false; } @@ -117,8 +117,7 @@ bool SynchronousAnymaWidget::SendDMX(const DmxBuffer &buffer) { // ----------------------------------------------------------------------------- class AnymaAsyncUsbSender : public AsyncUsbSender { public: - AnymaAsyncUsbSender(LibUsbAdaptor *adaptor, - libusb_device *usb_device) + AnymaAsyncUsbSender(LibUsbAdaptor *adaptor, libusb_device *usb_device) : AsyncUsbSender(adaptor, usb_device) { m_control_setup_buffer = new uint8_t[LIBUSB_CONTROL_SETUP_SIZE + DMX_UNIVERSE_SIZE]; @@ -137,7 +136,7 @@ class AnymaAsyncUsbSender : public AsyncUsbSender { } bool PerformTransfer(const DmxBuffer &buffer) { - libusb_fill_control_setup( + m_adaptor->FillControlSetup( m_control_setup_buffer, LIBUSB_REQUEST_TYPE_VENDOR | LIBUSB_RECIPIENT_DEVICE | LIBUSB_ENDPOINT_OUT, // bmRequestType diff --git a/plugins/usbdmx/AsyncUsbSender.cpp b/plugins/usbdmx/AsyncUsbSender.cpp index a97fd83ac7..ffac1f37ac 100644 --- a/plugins/usbdmx/AsyncUsbSender.cpp +++ b/plugins/usbdmx/AsyncUsbSender.cpp @@ -45,14 +45,14 @@ AsyncUsbSender::AsyncUsbSender(LibUsbAdaptor *adaptor, m_usb_device(usb_device), m_usb_handle(NULL), m_transfer_state(IDLE) { - m_transfer = libusb_alloc_transfer(0); - libusb_ref_device(usb_device); + m_transfer = m_adaptor->AllocTransfer(0); + m_adaptor->RefDevice(usb_device); } AsyncUsbSender::~AsyncUsbSender() { CancelTransfer(); - m_adaptor->CloseHandle(m_usb_handle); - libusb_unref_device(m_usb_device); + m_adaptor->Close(m_usb_handle); + m_adaptor->UnrefDevice(m_usb_device); } bool AsyncUsbSender::Init() { @@ -86,44 +86,39 @@ void AsyncUsbSender::CancelTransfer() { break; } if (!canceled) { - libusb_cancel_transfer(m_transfer); + m_adaptor->CancelTransfer(m_transfer); canceled = true; } } - libusb_free_transfer(m_transfer); + m_adaptor->FreeTransfer(m_transfer); m_transfer = NULL; } void AsyncUsbSender::FillControlTransfer(unsigned char *buffer, unsigned int timeout) { - libusb_fill_control_transfer( - m_transfer, m_usb_handle, buffer, - &AsyncCallback, this, timeout); + m_adaptor->FillControlTransfer(m_transfer, m_usb_handle, buffer, + &AsyncCallback, this, timeout); } void AsyncUsbSender::FillBulkTransfer(unsigned char endpoint, unsigned char *buffer, int length, unsigned int timeout) { - libusb_fill_bulk_transfer( - m_transfer, m_usb_handle, endpoint, - buffer, length, &AsyncCallback, - this, timeout); + m_adaptor->FillBulkTransfer(m_transfer, m_usb_handle, endpoint, buffer, + length, &AsyncCallback, this, timeout); } void AsyncUsbSender::FillInterruptTransfer(unsigned char endpoint, unsigned char *buffer, int length, unsigned int timeout) { - libusb_fill_interrupt_transfer( - m_transfer, m_usb_handle, endpoint, - buffer, length, &AsyncCallback, - this, timeout); + m_adaptor->FillInterruptTransfer(m_transfer, m_usb_handle, endpoint, buffer, + length, &AsyncCallback, this, timeout); } int AsyncUsbSender::SubmitTransfer() { - int ret = libusb_submit_transfer(m_transfer); + int ret = m_adaptor->SubmitTransfer(m_transfer); if (ret) { OLA_WARN << "libusb_submit_transfer returned " << libusb_error_name(ret); if (ret == LIBUSB_ERROR_NO_DEVICE) { @@ -135,8 +130,7 @@ int AsyncUsbSender::SubmitTransfer() { return ret; } -void AsyncUsbSender::TransferComplete( - struct libusb_transfer *transfer) { +void AsyncUsbSender::TransferComplete(struct libusb_transfer *transfer) { if (transfer != m_transfer) { OLA_WARN << "Mismatched libusb transfer: " << transfer << " != " << m_transfer; diff --git a/plugins/usbdmx/EuroliteProWidget.cpp b/plugins/usbdmx/EuroliteProWidget.cpp index b2db6c9e7c..572b8de30a 100644 --- a/plugins/usbdmx/EuroliteProWidget.cpp +++ b/plugins/usbdmx/EuroliteProWidget.cpp @@ -68,16 +68,17 @@ void CreateFrame( * Find the interface with the endpoint we're after. Usually this is interface * 1 but we check them all just in case. */ -bool LocateInterface(libusb_device *usb_device, +bool LocateInterface(LibUsbAdaptor *adaptor, + libusb_device *usb_device, int *interface_number) { struct libusb_config_descriptor *device_config; - if (libusb_get_config_descriptor(usb_device, 0, &device_config) != 0) { + if (adaptor->GetConfigDescriptor(usb_device, 0, &device_config) != 0) { OLA_WARN << "Failed to get device config descriptor"; return false; } - OLA_DEBUG << static_cast(device_config->bNumInterfaces) << - " interfaces found"; + OLA_DEBUG << static_cast(device_config->bNumInterfaces) + << " interfaces found"; for (unsigned int i = 0; i < device_config->bNumInterfaces; i++) { const struct libusb_interface *interface = &device_config->interface[i]; for (int j = 0; j < interface->num_altsetting; j++) { @@ -87,19 +88,19 @@ bool LocateInterface(libusb_device *usb_device, const struct libusb_endpoint_descriptor *endpoint = &iface_descriptor->endpoint[k]; OLA_DEBUG << "Interface " << i << ", altsetting " << j << ", endpoint " - << static_cast(k) << ", endpoint address 0x" << std::hex << - static_cast(endpoint->bEndpointAddress); + << static_cast(k) << ", endpoint address 0x" << std::hex + << static_cast(endpoint->bEndpointAddress); if (endpoint->bEndpointAddress == ENDPOINT) { OLA_INFO << "Using interface " << i; *interface_number = i; - libusb_free_config_descriptor(device_config); + adaptor->FreeConfigDescriptor(device_config); return true; } } } } OLA_WARN << "Failed to locate endpoint for EurolitePro device."; - libusb_free_config_descriptor(device_config); + adaptor->FreeConfigDescriptor(device_config); return false; } } // namespace @@ -112,18 +113,23 @@ bool LocateInterface(libusb_device *usb_device, */ class EuroliteProThreadedSender: public ThreadedUsbSender { public: - EuroliteProThreadedSender(libusb_device *usb_device, + EuroliteProThreadedSender(LibUsbAdaptor *adaptor, + libusb_device *usb_device, libusb_device_handle *handle); private: + LibUsbAdaptor* const m_adaptor; + bool TransmitBuffer(libusb_device_handle *handle, const DmxBuffer &buffer); }; EuroliteProThreadedSender::EuroliteProThreadedSender( + LibUsbAdaptor *adaptor, libusb_device *usb_device, libusb_device_handle *usb_handle) - : ThreadedUsbSender(usb_device, usb_handle) { + : ThreadedUsbSender(usb_device, usb_handle), + m_adaptor(adaptor) { } bool EuroliteProThreadedSender::TransmitBuffer(libusb_device_handle *handle, @@ -132,13 +138,9 @@ bool EuroliteProThreadedSender::TransmitBuffer(libusb_device_handle *handle, CreateFrame(buffer, frame); int transferred; - int r = libusb_bulk_transfer( - handle, - ENDPOINT, - frame, - EUROLITE_PRO_FRAME_SIZE, - &transferred, - URB_TIMEOUT_MS); + int r = m_adaptor->BulkTransfer(handle, ENDPOINT, frame, + EUROLITE_PRO_FRAME_SIZE, &transferred, + URB_TIMEOUT_MS); if (transferred != EUROLITE_PRO_FRAME_SIZE) { // not sure if this is fatal or not OLA_WARN << "EurolitePro driver failed to transfer all data"; @@ -161,7 +163,7 @@ bool SynchronousEuroliteProWidget::Init() { libusb_device_handle *usb_handle; int interface_number; - if (!LocateInterface(m_usb_device, &interface_number)) { + if (!LocateInterface(m_adaptor, m_usb_device, &interface_number)) { return false; } @@ -172,7 +174,7 @@ bool SynchronousEuroliteProWidget::Init() { } std::auto_ptr sender( - new EuroliteProThreadedSender(m_usb_device, usb_handle)); + new EuroliteProThreadedSender(m_adaptor, m_usb_device, usb_handle)); if (!sender->Start()) { return false; } @@ -199,7 +201,7 @@ class EuroliteProAsyncUsbSender : public AsyncUsbSender { libusb_device_handle* SetupHandle() { int interface_number; - if (!LocateInterface(m_usb_device, &interface_number)) { + if (!LocateInterface(m_adaptor, m_usb_device, &interface_number)) { return NULL; } diff --git a/plugins/usbdmx/LibUsbAdaptor.cpp b/plugins/usbdmx/LibUsbAdaptor.cpp index 6ca02df16d..591dd57c85 100644 --- a/plugins/usbdmx/LibUsbAdaptor.cpp +++ b/plugins/usbdmx/LibUsbAdaptor.cpp @@ -71,17 +71,9 @@ bool Open(libusb_device *usb_device, return true; } -/** - * @brief A wrapper around libusb_close. - */ -void Close(libusb_device_handle *usb_handle) { - libusb_close(usb_handle); -} - -bool OpenHandleAndClaimInterface( - libusb_device *usb_device, - int interface, - libusb_device_handle **usb_handle) { +bool OpenHandleAndClaimInterface(libusb_device *usb_device, + int interface, + libusb_device_handle **usb_handle) { if (!Open(usb_device, usb_handle)) { return false; } @@ -91,7 +83,7 @@ bool OpenHandleAndClaimInterface( OLA_WARN << "Failed to claim interface " << interface << " on device: " << usb_device << ": " << libusb_error_name(r); - Close(*usb_handle); + libusb_close(*usb_handle); return false; } return true; @@ -128,7 +120,7 @@ bool LibUsbAdaptor::GetDeviceInfo( << "have one"; } - Close(usb_handle); + libusb_close(usb_handle); return true; } @@ -149,12 +141,148 @@ bool LibUsbAdaptor::CheckProduct(const string &expected, const string &actual) { return true; } +// BaseLibUsbAdaptor +// ---------------------------------------------------------------------------- +libusb_device* BaseLibUsbAdaptor::RefDevice(libusb_device *dev) { + return libusb_ref_device(dev); +} + +void BaseLibUsbAdaptor::UnrefDevice(libusb_device *dev) { + libusb_unref_device(dev); +} + +int BaseLibUsbAdaptor::SetConfiguration(libusb_device_handle *dev, + int configuration) { + return libusb_set_configuration(dev, configuration); +} + +int BaseLibUsbAdaptor::ClaimInterface(libusb_device_handle *dev, + int interface_number) { + return libusb_claim_interface(dev, interface_number); +} + +int BaseLibUsbAdaptor::DetachKernelDriver(libusb_device_handle *dev, + int interface_number) { + return libusb_detach_kernel_driver(dev, interface_number); +} + +int BaseLibUsbAdaptor::GetActiveConfigDescriptor( + libusb_device *dev, + struct libusb_config_descriptor **config) { + return libusb_get_active_config_descriptor(dev, config); +} + +int BaseLibUsbAdaptor::GetConfigDescriptor( + libusb_device *dev, + uint8_t config_index, + struct libusb_config_descriptor **config) { + return libusb_get_config_descriptor(dev, config_index, config); +} + +void BaseLibUsbAdaptor::FreeConfigDescriptor( + struct libusb_config_descriptor *config) { + libusb_free_config_descriptor(config); +} + +struct libusb_transfer* BaseLibUsbAdaptor::AllocTransfer(int iso_packets) { + return libusb_alloc_transfer(iso_packets); +} + +void BaseLibUsbAdaptor::FreeTransfer(struct libusb_transfer *transfer) { + return libusb_free_transfer(transfer); +} + +int BaseLibUsbAdaptor::SubmitTransfer(struct libusb_transfer *transfer) { + return libusb_submit_transfer(transfer); +} + +int BaseLibUsbAdaptor::CancelTransfer(struct libusb_transfer *transfer) { + return libusb_cancel_transfer(transfer); +} + +void BaseLibUsbAdaptor::FillControlSetup(unsigned char *buffer, + uint8_t bmRequestType, + uint8_t bRequest, + uint16_t wValue, + uint16_t wIndex, + uint16_t wLength) { + return libusb_fill_control_setup(buffer, bmRequestType, bRequest, wValue, + wIndex, wLength); +} + +void BaseLibUsbAdaptor::FillControlTransfer( + struct libusb_transfer *transfer, + libusb_device_handle *dev_handle, + unsigned char *buffer, + libusb_transfer_cb_fn callback, + void *user_data, + unsigned int timeout) { + return libusb_fill_control_transfer(transfer, dev_handle, buffer, callback, + user_data, timeout); +} + +void BaseLibUsbAdaptor::FillBulkTransfer(struct libusb_transfer *transfer, + libusb_device_handle *dev_handle, + unsigned char endpoint, + unsigned char *buffer, + int length, + libusb_transfer_cb_fn callback, + void *user_data, + unsigned int timeout) { + libusb_fill_bulk_transfer(transfer, dev_handle, endpoint, buffer, + length, callback, user_data, timeout); +} + +void BaseLibUsbAdaptor::FillInterruptTransfer(struct libusb_transfer *transfer, + libusb_device_handle *dev_handle, + unsigned char endpoint, + unsigned char *buffer, + int length, + libusb_transfer_cb_fn callback, + void *user_data, + unsigned int timeout) { + libusb_fill_interrupt_transfer(transfer, dev_handle, endpoint, buffer, + length, callback, user_data, timeout); +} + +int BaseLibUsbAdaptor::ControlTransfer( + libusb_device_handle *dev_handle, + uint8_t bmRequestType, + uint8_t bRequest, + uint16_t wValue, + uint16_t wIndex, + unsigned char *data, + uint16_t wLength, + unsigned int timeout) { + return libusb_control_transfer(dev_handle, bmRequestType, bRequest, wValue, + wIndex, data, wLength, timeout); +} + +int BaseLibUsbAdaptor::BulkTransfer(struct libusb_device_handle *dev_handle, + unsigned char endpoint, + unsigned char *data, + int length, + int *transferred, + unsigned int timeout) { + return libusb_bulk_transfer(dev_handle, endpoint, data, length, transferred, + timeout); +} + +int BaseLibUsbAdaptor::InterruptTransfer(libusb_device_handle *dev_handle, + unsigned char endpoint, + unsigned char *data, + int length, + int *actual_length, + unsigned int timeout) { + return libusb_interrupt_transfer(dev_handle, endpoint, data, length, + actual_length, timeout); +} + + // SyncronousLibUsbAdaptor // ----------------------------------------------------------------------------- - -bool SyncronousLibUsbAdaptor::OpenDevice( - libusb_device *usb_device, - libusb_device_handle **usb_handle) { +bool SyncronousLibUsbAdaptor::OpenDevice(libusb_device *usb_device, + libusb_device_handle **usb_handle) { return Open(usb_device, usb_handle); } @@ -165,17 +293,12 @@ bool SyncronousLibUsbAdaptor::OpenDeviceAndClaimInterface( return OpenHandleAndClaimInterface(usb_device, interface, usb_handle); } -void SyncronousLibUsbAdaptor::CloseHandle(libusb_device_handle *usb_handle) { - Close(usb_handle); +void SyncronousLibUsbAdaptor::Close(libusb_device_handle *usb_handle) { + libusb_close(usb_handle); } // AsyncronousLibUsbAdaptor // ----------------------------------------------------------------------------- - -AsyncronousLibUsbAdaptor::AsyncronousLibUsbAdaptor(LibUsbThread *thread) - : m_thread(thread) { -} - bool AsyncronousLibUsbAdaptor::OpenDevice(libusb_device *usb_device, libusb_device_handle **usb_handle) { bool ok = Open(usb_device, usb_handle); @@ -196,9 +319,44 @@ bool AsyncronousLibUsbAdaptor::OpenDeviceAndClaimInterface( return ok; } -void AsyncronousLibUsbAdaptor::CloseHandle(libusb_device_handle *handle) { +void AsyncronousLibUsbAdaptor::Close(libusb_device_handle *handle) { m_thread->CloseHandle(handle); } + +int AsyncronousLibUsbAdaptor::ControlTransfer( + OLA_UNUSED libusb_device_handle *dev_handle, + OLA_UNUSED uint8_t bmRequestType, + OLA_UNUSED uint8_t bRequest, + OLA_UNUSED uint16_t wValue, + OLA_UNUSED uint16_t wIndex, + OLA_UNUSED unsigned char *data, + OLA_UNUSED uint16_t wLength, + OLA_UNUSED unsigned int timeout) { + OLA_WARN << "libusb_control_transfer in an AsyncronousLibUsbAdaptor"; + return LIBUSB_ERROR_NOT_SUPPORTED; +} + +int AsyncronousLibUsbAdaptor::BulkTransfer( + OLA_UNUSED struct libusb_device_handle *dev_handle, + OLA_UNUSED unsigned char endpoint, + OLA_UNUSED unsigned char *data, + OLA_UNUSED int length, + OLA_UNUSED int *transferred, + OLA_UNUSED unsigned int timeout) { + OLA_WARN << "libusb_bulk_transfer in an AsyncronousLibUsbAdaptor"; + return LIBUSB_ERROR_NOT_SUPPORTED; +} + +int AsyncronousLibUsbAdaptor::InterruptTransfer( + OLA_UNUSED libusb_device_handle *dev_handle, + OLA_UNUSED unsigned char endpoint, + OLA_UNUSED unsigned char *data, + OLA_UNUSED int length, + OLA_UNUSED int *actual_length, + OLA_UNUSED unsigned int timeout) { + OLA_WARN << "libusb_interrupt_transfer in an AsyncronousLibUsbAdaptor"; + return LIBUSB_ERROR_NOT_SUPPORTED; +} } // namespace usbdmx } // namespace plugin } // namespace ola diff --git a/plugins/usbdmx/LibUsbAdaptor.h b/plugins/usbdmx/LibUsbAdaptor.h index 8a18eca6a3..1708781893 100644 --- a/plugins/usbdmx/LibUsbAdaptor.h +++ b/plugins/usbdmx/LibUsbAdaptor.h @@ -29,7 +29,6 @@ namespace ola { namespace plugin { namespace usbdmx { - /** * @brief Wraps calls to libusb so we can test the code. */ @@ -43,6 +42,21 @@ class LibUsbAdaptor { virtual ~LibUsbAdaptor() {} + // Device handling and enumeration + + /** + * @brief Wraps libusb_ref_device. + * @param dev the device to reference + * @returns the same device + */ + virtual libusb_device* RefDevice(libusb_device *dev) = 0; + + /** + * @brief Wraps libusb_unref_device. + * @param dev the device to unreference. + */ + virtual void UnrefDevice(libusb_device *dev) = 0; + /** * @brief Open a libusb device. * @param usb_device The usb device to open. @@ -69,7 +83,269 @@ class LibUsbAdaptor { * @brief Close a libusb handle. * @param usb_handle the handle to close. */ - virtual void CloseHandle(libusb_device_handle *usb_handle) = 0; + virtual void Close(libusb_device_handle *usb_handle) = 0; + + /** + * @brief Wraps libusb_set_configuration. + * @param dev a device handle + * @param configuration the bConfigurationValue of the configuration you + * wish to activate, or -1 if you wish to put the device in unconfigured state + * @returns 0 on success + * @returns LIBUSB_ERROR_NOT_FOUND if the requested configuration does not + * exist. + * @returns LIBUSB_ERROR_BUSY if interfaces are currently claimed + * @returns LIBUSB_ERROR_NO_DEVICE if the device has been disconnected + * @returns another LIBUSB_ERROR code on other failure + */ + virtual int SetConfiguration(libusb_device_handle *dev, + int configuration) = 0; + + /** + * @brief Wraps libusb_claim_interface. + * @param dev a device handle + * @param interface_number the bInterfaceNumber of the interface you + * wish to claim + * @returns 0 on success + * @returns LIBUSB_ERROR_NOT_FOUND if the requested interface does not exist + * @returns LIBUSB_ERROR_BUSY if another program or driver has claimed the + * interface + * @returns LIBUSB_ERROR_NO_DEVICE if the device has been disconnected + * @returns a LIBUSB_ERROR code on other failure + */ + virtual int ClaimInterface(libusb_device_handle *dev, + int interface_number) = 0; + + /** + * @brief Detatch a kernel driver. + * @param dev a device handle + * @param interface_number the interface to detach the driver from + * @returns 0 on success + * @returns LIBUSB_ERROR_NOT_FOUND if no kernel driver was active + * @returns LIBUSB_ERROR_INVALID_PARAM if the interface does not exist + * @returns LIBUSB_ERROR_NO_DEVICE if the device has been disconnected + * @returns LIBUSB_ERROR_NOT_SUPPORTED on platforms where the functionality + * is not available + * @returns another LIBUSB_ERROR code on other failure + * + */ + virtual int DetachKernelDriver(libusb_device_handle *dev, + int interface_number) = 0; + + // USB descriptors + + /** + * @brief Wraps libusb_get_active_config_descriptor. + * @param dev a device + * @param config output location for the USB configuration descriptor. Only + * valid if 0 was returned. Must be freed with libusb_free_config_descriptor() + * after use. + * @returns 0 on success + * @returns LIBUSB_ERROR_NOT_FOUND if the device is in unconfigured state + * @returns another LIBUSB_ERROR code on error + */ + virtual int GetActiveConfigDescriptor( + libusb_device *dev, + struct libusb_config_descriptor **config) = 0; + + /** + * @brief Wraps libusb_get_config_descriptor. + * @param dev a device + * @param config_index the index of the configuration you wish to retrieve + * @param config output location for the USB configuration descriptor. Only + * valid if 0 was returned. Must be freed with libusb_free_config_descriptor() + * after use. + * @returns 0 on success + * @returns LIBUSB_ERROR_NOT_FOUND if the configuration does not exist + * @returns another LIBUSB_ERROR code on error + */ + virtual int GetConfigDescriptor(libusb_device *dev, + uint8_t config_index, + struct libusb_config_descriptor **config) = 0; + + /** + * @brief Wraps busb_free_config_descriptor. + * @param config the configuration descriptor to free + */ + virtual void FreeConfigDescriptor( + struct libusb_config_descriptor *config) = 0; + + // Asynchronous device I/O + + /** + * @brief Wraps libusb_alloc_transfer + * @param iso_packets number of isochronous packet descriptors to allocate + * @returns a newly allocated transfer, or NULL on error + */ + virtual struct libusb_transfer* AllocTransfer(int iso_packets) = 0; + + /** + * @brief Wraps libusb_free_transfer. + * @param transfer the transfer to free + */ + virtual void FreeTransfer(struct libusb_transfer *transfer) = 0; + + /** + * @brief Wraps libusb_submit_transfer. + * @param transfer the transfer to submit + * @returns 0 on success + * @returns LIBUSB_ERROR_NO_DEVICE if the device has been disconnected + * @returns LIBUSB_ERROR_BUSY if the transfer has already been submitted. + * @returns LIBUSB_ERROR_NOT_SUPPORTED if the transfer flags are not supported + * by the operating system. + * @returns another LIBUSB_ERROR code on other failure + */ + virtual int SubmitTransfer(struct libusb_transfer *transfer) = 0; + + /** + * @brief Wraps libusb_cancel_transfer + * @param transfer the transfer to cancel + * @returns 0 on success + * @returns LIBUSB_ERROR_NOT_FOUND if the transfer is already complete or + * cancelled. + * @returns a LIBUSB_ERROR code on failure + */ + virtual int CancelTransfer(struct libusb_transfer *transfer) = 0; + + /** + * @brief Wraps libusb_fill_control_setup + * @param buffer buffer to output the setup packet into + * This pointer must be aligned to at least 2 bytes boundary. + * @param bmRequestType the request type field for the setup packet + * @param bRequest the request field for the setup packet + * @param wValue the value field for the setup packet + * @param wIndex the index field for the setup packet + * @param wLength the length field for the setup packet. The data buffer + * should be at least this size. + */ + virtual void FillControlSetup(unsigned char *buffer, + uint8_t bmRequestType, + uint8_t bRequest, + uint16_t wValue, + uint16_t wIndex, + uint16_t wLength) = 0; + + /** + * @brief Wraps libusb_fill_control_transfer + * @param transfer the transfer to populate + * @param dev_handle handle of the device that will handle the transfer + * @param buffer data buffer. If provided, this function will interpret the + * first 8 bytes as a setup packet and infer the transfer length from that. + * This pointer must be aligned to at least 2 bytes boundary. + * @param callback callback function to be invoked on transfer completion + * @param user_data user data to pass to callback function + * @param timeout timeout for the transfer in milliseconds + */ + virtual void FillControlTransfer(struct libusb_transfer *transfer, + libusb_device_handle *dev_handle, + unsigned char *buffer, + libusb_transfer_cb_fn callback, + void *user_data, + unsigned int timeout) = 0; + + /** + * @brief Wraps libusb_fill_bulk_transfer. + * @param transfer the transfer to populate + * @param dev_handle handle of the device that will handle the transfer + * @param endpoint address of the endpoint where this transfer will be sent + * @param buffer data buffer. If provided, this function will interpret the + * first 8 bytes as a setup packet and infer the transfer length from that. + * This pointer must be aligned to at least 2 bytes boundary. + * @param length length of data buffer + * @param callback callback function to be invoked on transfer completion + * @param user_data user data to pass to callback function + * @param timeout timeout for the transfer in milliseconds + */ + virtual void FillBulkTransfer(struct libusb_transfer *transfer, + libusb_device_handle *dev_handle, + unsigned char endpoint, + unsigned char *buffer, + int length, + libusb_transfer_cb_fn callback, + void *user_data, + unsigned int timeout) = 0; + + /** + * @brief Wraps libusb_fill_interrupt_transfer. + * @param transfer the transfer to populate + * @param dev_handle handle of the device that will handle the transfer + * @param endpoint address of the endpoint where this transfer will be sent + * @param buffer data buffer + * @param length length of data buffer + * @param callback callback function to be invoked on transfer completion + * @param user_data user data to pass to callback function + * @param timeout timeout for the transfer in milliseconds + */ + virtual void FillInterruptTransfer(struct libusb_transfer *transfer, + libusb_device_handle *dev_handle, + unsigned char endpoint, + unsigned char *buffer, + int length, + libusb_transfer_cb_fn callback, + void *user_data, + unsigned int timeout) = 0; + + // Synchronous device I/O + + /** + * @brief Wraps libusb_control_transfer(). + * @param dev_handle a handle for the device to communicate with + * @param bmRequestType the request type field for the setup packet + * @param bRequest the request field for the setup packet + * @param wValue the value field for the setup packet + * @param wIndex the index field for the setup packet + * @param data a suitably-sized data buffer for either input or output + * (depending on direction bits within bmRequestType) + * @param wLength the length field for the setup packet. The data buffer + * should be at least this size. + * @param timeout timeout (in millseconds) that this function should wait + * before giving up due to no response being received. For an unlimited + * timeout, use value 0. + * @returns on success, the number of bytes actually transferred + * + */ + virtual int ControlTransfer(libusb_device_handle *dev_handle, + uint8_t bmRequestType, + uint8_t bRequest, + uint16_t wValue, + uint16_t wIndex, + unsigned char *data, + uint16_t wLength, + unsigned int timeout) = 0; + + /** + * @brief Wraps libusb_bulk_transfer. + * @returns 0 on success (and populates transferred) + * @returns LIBUSB_ERROR_TIMEOUT if the transfer timed out (and populates + * transferred) + * @returns LIBUSB_ERROR_PIPE if the endpoint halted + * @returns LIBUSB_ERROR_OVERFLOW if the device offered more data, see + * @returns LIBUSB_ERROR_NO_DEVICE if the device has been disconnected + * @returns another LIBUSB_ERROR code on other failures + */ + virtual int BulkTransfer(struct libusb_device_handle *dev_handle, + unsigned char endpoint, + unsigned char *data, + int length, + int *transferred, + unsigned int timeout) = 0; + + /** + * @brief Wraps libusb_interrupt_transfer + * @returns 0 on success (and populates transferred) + * @returns LIBUSB_ERROR_TIMEOUT if the transfer timed out + * @returns LIBUSB_ERROR_PIPE if the endpoint halted + * @returns LIBUSB_ERROR_OVERFLOW if the device offered more data, see + * @returns LIBUSB_ERROR_NO_DEVICE if the device has been disconnected + * @returns another LIBUSB_ERROR code on other error + */ + virtual int InterruptTransfer(libusb_device_handle *dev_handle, + unsigned char endpoint, + unsigned char *data, + int length, + int *actual_length, + unsigned int timeout) = 0; + + // Static helper methods. /** * @brief Fetch the manufacturer, product and serial strings from a device. @@ -102,6 +378,98 @@ class LibUsbAdaptor { const std::string &actual); }; +/** + * @brief The base LibUsbAdaptor that passes most called through to libusb. + */ +class BaseLibUsbAdaptor : public LibUsbAdaptor { + public: + // Device handling and enumeration + libusb_device* RefDevice(libusb_device *dev); + + void UnrefDevice(libusb_device *dev); + + int SetConfiguration(libusb_device_handle *dev, int configuration); + + int ClaimInterface(libusb_device_handle *dev, int interface_number); + + int DetachKernelDriver(libusb_device_handle *dev, int interface_number); + + // USB descriptors + int GetActiveConfigDescriptor( + libusb_device *dev, + struct libusb_config_descriptor **config); + + int GetConfigDescriptor(libusb_device *dev, + uint8_t config_index, + struct libusb_config_descriptor **config); + + void FreeConfigDescriptor(struct libusb_config_descriptor *config); + + // Asynchronous device I/O + struct libusb_transfer* AllocTransfer(int iso_packets); + + void FreeTransfer(struct libusb_transfer *transfer); + + int SubmitTransfer(struct libusb_transfer *transfer); + + int CancelTransfer(struct libusb_transfer *transfer); + + void FillControlSetup(unsigned char *buffer, + uint8_t bmRequestType, + uint8_t bRequest, + uint16_t wValue, + uint16_t wIndex, + uint16_t wLength); + + void FillControlTransfer(struct libusb_transfer *transfer, + libusb_device_handle *dev_handle, + unsigned char *buffer, + libusb_transfer_cb_fn callback, + void *user_data, + unsigned int timeout); + + void FillBulkTransfer(struct libusb_transfer *transfer, + libusb_device_handle *dev_handle, + unsigned char endpoint, + unsigned char *buffer, + int length, + libusb_transfer_cb_fn callback, + void *user_data, + unsigned int timeout); + + void FillInterruptTransfer(struct libusb_transfer *transfer, + libusb_device_handle *dev_handle, + unsigned char endpoint, + unsigned char *buffer, + int length, + libusb_transfer_cb_fn callback, + void *user_data, + unsigned int timeout); + + // Synchronous device I/O + int ControlTransfer(libusb_device_handle *dev_handle, + uint8_t bmRequestType, + uint8_t bRequest, + uint16_t wValue, + uint16_t wIndex, + unsigned char *data, + uint16_t wLength, + unsigned int timeout); + + int BulkTransfer(struct libusb_device_handle *dev_handle, + unsigned char endpoint, + unsigned char *data, + int length, + int *transferred, + unsigned int timeout); + + int InterruptTransfer(libusb_device_handle *dev_handle, + unsigned char endpoint, + unsigned char *data, + int length, + int *actual_length, + unsigned int timeout); +}; /** * @brief A LibUsbAdaptor for use with Syncronous widgets. @@ -109,7 +477,7 @@ class LibUsbAdaptor { * When using syncronous mode, we don't have the requirement of interacting * with a LibUsbThread. */ -class SyncronousLibUsbAdaptor : public LibUsbAdaptor { +class SyncronousLibUsbAdaptor : public BaseLibUsbAdaptor { public: SyncronousLibUsbAdaptor() {} @@ -120,7 +488,7 @@ class SyncronousLibUsbAdaptor : public LibUsbAdaptor { int interface, libusb_device_handle **usb_handle); - void CloseHandle(libusb_device_handle *usb_handle); + void Close(libusb_device_handle *usb_handle); private: DISALLOW_COPY_AND_ASSIGN(SyncronousLibUsbAdaptor); @@ -132,9 +500,11 @@ class SyncronousLibUsbAdaptor : public LibUsbAdaptor { * Asyncronous mode requires notifying the LibUsbThread when handles are opened * and closed. */ -class AsyncronousLibUsbAdaptor : public LibUsbAdaptor { +class AsyncronousLibUsbAdaptor : public BaseLibUsbAdaptor { public: - explicit AsyncronousLibUsbAdaptor(class LibUsbThread *thread); + explicit AsyncronousLibUsbAdaptor(class LibUsbThread *thread) + : m_thread(thread) { + } bool OpenDevice(libusb_device *usb_device, libusb_device_handle **usb_handle); @@ -143,7 +513,30 @@ class AsyncronousLibUsbAdaptor : public LibUsbAdaptor { int interface, libusb_device_handle **usb_handle); - void CloseHandle(libusb_device_handle *usb_handle); + void Close(libusb_device_handle *usb_handle); + + int ControlTransfer(libusb_device_handle *dev_handle, + uint8_t bmRequestType, + uint8_t bRequest, + uint16_t wValue, + uint16_t wIndex, + unsigned char *data, + uint16_t wLength, + unsigned int timeout); + + int BulkTransfer(struct libusb_device_handle *dev_handle, + unsigned char endpoint, + unsigned char *data, + int length, + int *transferred, + unsigned int timeout); + + int InterruptTransfer(libusb_device_handle *dev_handle, + unsigned char endpoint, + unsigned char *data, + int length, + int *actual_length, + unsigned int timeout); private: class LibUsbThread *m_thread; diff --git a/plugins/usbdmx/SunliteWidget.cpp b/plugins/usbdmx/SunliteWidget.cpp index a6b1a8ccfe..5ad3955915 100644 --- a/plugins/usbdmx/SunliteWidget.cpp +++ b/plugins/usbdmx/SunliteWidget.cpp @@ -95,10 +95,12 @@ void UpdatePacket(const DmxBuffer &buffer, */ class SunliteThreadedSender: public ThreadedUsbSender { public: - SunliteThreadedSender(libusb_device *usb_device, + SunliteThreadedSender(LibUsbAdaptor *adaptor, + libusb_device *usb_device, libusb_device_handle *handle); private: + LibUsbAdaptor* const m_adaptor; uint8_t m_packet[SUNLITE_PACKET_SIZE]; bool TransmitBuffer(libusb_device_handle *handle, @@ -106,9 +108,11 @@ class SunliteThreadedSender: public ThreadedUsbSender { }; SunliteThreadedSender::SunliteThreadedSender( + LibUsbAdaptor *adaptor, libusb_device *usb_device, libusb_device_handle *usb_handle) - : ThreadedUsbSender(usb_device, usb_handle) { + : ThreadedUsbSender(usb_device, usb_handle), + m_adaptor(adaptor) { InitPacket(m_packet); } @@ -116,13 +120,8 @@ bool SunliteThreadedSender::TransmitBuffer(libusb_device_handle *handle, const DmxBuffer &buffer) { UpdatePacket(buffer, m_packet); int transferred; - int r = libusb_bulk_transfer( - handle, - ENDPOINT, - (unsigned char*) m_packet, - SUNLITE_PACKET_SIZE, - &transferred, - TIMEOUT); + int r = m_adaptor->BulkTransfer(handle, ENDPOINT, (unsigned char*) m_packet, + SUNLITE_PACKET_SIZE, &transferred, TIMEOUT); if (transferred != SUNLITE_PACKET_SIZE) { // not sure if this is fatal or not OLA_WARN << "Sunlite driver failed to transfer all data"; @@ -149,7 +148,7 @@ bool SynchronousSunliteWidget::Init() { } std::auto_ptr sender( - new SunliteThreadedSender(m_usb_device, usb_handle)); + new SunliteThreadedSender(m_adaptor, m_usb_device, usb_handle)); if (!sender->Start()) { return false; } diff --git a/plugins/usbdmx/VellemanWidget.cpp b/plugins/usbdmx/VellemanWidget.cpp index bceb8c8c16..bd711905e9 100644 --- a/plugins/usbdmx/VellemanWidget.cpp +++ b/plugins/usbdmx/VellemanWidget.cpp @@ -87,7 +87,7 @@ libusb_device_handle *OpenVellemenWidget(LibUsbAdaptor *adaptor, libusb_device *usb_device, unsigned int *chunk_size) { libusb_config_descriptor *config; - if (libusb_get_active_config_descriptor(usb_device, &config)) { + if (adaptor->GetActiveConfigDescriptor(usb_device, &config)) { OLA_WARN << "Could not get active config descriptor"; return NULL; } @@ -107,7 +107,7 @@ libusb_device_handle *OpenVellemenWidget(LibUsbAdaptor *adaptor, // this means the upgrade is present *chunk_size = max_packet_size; } - libusb_free_config_descriptor(config); + adaptor->FreeConfigDescriptor(config); libusb_device_handle *usb_handle; bool ok = adaptor->OpenDevice(usb_device, &usb_handle); @@ -115,26 +115,25 @@ libusb_device_handle *OpenVellemenWidget(LibUsbAdaptor *adaptor, return NULL; } - if (libusb_kernel_driver_active(usb_handle, 0)) { - if (libusb_detach_kernel_driver(usb_handle, 0)) { - OLA_WARN << "Failed to detach kernel driver"; - adaptor->CloseHandle(usb_handle); - return NULL; - } + int ret_code = adaptor->DetachKernelDriver(usb_handle, INTERFACE); + if (ret_code != 0 && ret_code != LIBUSB_ERROR_NOT_FOUND) { + OLA_WARN << "Failed to detach kernel driver"; + adaptor->Close(usb_handle); + return NULL; } // this device only has one configuration - int ret_code = libusb_set_configuration(usb_handle, CONFIGURATION); + ret_code = adaptor->SetConfiguration(usb_handle, CONFIGURATION); if (ret_code) { OLA_WARN << "Velleman set config failed, with libusb error code " << ret_code; - adaptor->CloseHandle(usb_handle); + adaptor->Close(usb_handle); return NULL; } - if (libusb_claim_interface(usb_handle, INTERFACE)) { + if (adaptor->ClaimInterface(usb_handle, INTERFACE)) { OLA_WARN << "Failed to claim Velleman usb device"; - adaptor->CloseHandle(usb_handle); + adaptor->Close(usb_handle); return NULL; } return usb_handle; @@ -173,14 +172,17 @@ unsigned int CountLeadingZeros(const uint8_t *data, unsigned int data_length, */ class VellemanThreadedSender: public ThreadedUsbSender { public: - VellemanThreadedSender(libusb_device *usb_device, + VellemanThreadedSender(LibUsbAdaptor *adaptor, + libusb_device *usb_device, libusb_device_handle *handle, unsigned int chunk_size) : ThreadedUsbSender(usb_device, handle), + m_adaptor(adaptor), m_chunk_size(chunk_size) { } private: + LibUsbAdaptor* const m_adaptor; const unsigned int m_chunk_size; bool TransmitBuffer(libusb_device_handle *handle, @@ -266,13 +268,9 @@ bool VellemanThreadedSender::SendDataChunk(libusb_device_handle *handle, uint8_t *usb_data, unsigned int chunk_size) { int transferred; - int ret = libusb_interrupt_transfer( - handle, - ENDPOINT, - reinterpret_cast(usb_data), - chunk_size, - &transferred, - URB_TIMEOUT_MS); + int ret = m_adaptor->InterruptTransfer(handle, + ENDPOINT, reinterpret_cast(usb_data), + chunk_size, &transferred, URB_TIMEOUT_MS); if (ret) { OLA_INFO << "USB return code was " << ret << ", transferred " << transferred; @@ -300,7 +298,8 @@ bool SynchronousVellemanWidget::Init() { } std::auto_ptr sender( - new VellemanThreadedSender(m_usb_device, usb_handle, chunk_size)); + new VellemanThreadedSender(m_adaptor, m_usb_device, usb_handle, + chunk_size)); if (!sender->Start()) { return false; } From a74fb1f935dc96fb2cd50c21a2671bff1e5a0f81 Mon Sep 17 00:00:00 2001 From: Simon Newton Date: Sat, 22 Nov 2014 06:54:17 -0800 Subject: [PATCH 28/34] Minor cleanup to the libusb plugin --- plugins/usbdmx/AsyncPluginImpl.cpp | 16 ++-- plugins/usbdmx/FadecandyWidgetFactory.cpp | 91 ++++++++++++++++++++++ plugins/usbdmx/FadecandyWidgetFactory.h | 60 ++++++++++++++ plugins/usbdmx/Makefile.mk | 4 + plugins/usbdmx/SyncPluginImpl.cpp | 8 +- plugins/usbdmx/SyncronizedWidgetObserver.h | 8 ++ plugins/usbdmx/UsbDmxPlugin.cpp | 6 +- plugins/usbdmx/WidgetFactory.h | 17 ++++ 8 files changed, 193 insertions(+), 17 deletions(-) create mode 100644 plugins/usbdmx/FadecandyWidgetFactory.cpp create mode 100644 plugins/usbdmx/FadecandyWidgetFactory.h diff --git a/plugins/usbdmx/AsyncPluginImpl.cpp b/plugins/usbdmx/AsyncPluginImpl.cpp index c6c3313cec..adf1ed58cf 100644 --- a/plugins/usbdmx/AsyncPluginImpl.cpp +++ b/plugins/usbdmx/AsyncPluginImpl.cpp @@ -169,45 +169,45 @@ void AsyncPluginImpl::HotPlugEvent(struct libusb_device *usb_device, } #endif -bool AsyncPluginImpl::NewWidget(class AnymaWidget *widget) { +bool AsyncPluginImpl::NewWidget(AnymaWidget *widget) { return StartAndRegisterDevice( widget, new GenericDevice(m_plugin, widget, "Anyma USB Device", "anyma-" + widget->SerialNumber())); } -bool AsyncPluginImpl::NewWidget(class EuroliteProWidget *widget) { +bool AsyncPluginImpl::NewWidget(EuroliteProWidget *widget) { return StartAndRegisterDevice( widget, new GenericDevice(m_plugin, widget, "EurolitePro USB Device", "eurolite-" + widget->SerialNumber())); } -bool AsyncPluginImpl::NewWidget(class SunliteWidget *widget) { +bool AsyncPluginImpl::NewWidget(SunliteWidget *widget) { return StartAndRegisterDevice( widget, new GenericDevice(m_plugin, widget, "Sunlite USBDMX2 Device", "usbdmx2")); } -bool AsyncPluginImpl::NewWidget(class VellemanWidget *widget) { +bool AsyncPluginImpl::NewWidget(VellemanWidget *widget) { return StartAndRegisterDevice( widget, new GenericDevice(m_plugin, widget, "Velleman USB Device", "velleman")); } -void AsyncPluginImpl::WidgetRemoved(class AnymaWidget *widget) { +void AsyncPluginImpl::WidgetRemoved(AnymaWidget *widget) { RemoveWidget(widget); } -void AsyncPluginImpl::WidgetRemoved(class EuroliteProWidget *widget) { +void AsyncPluginImpl::WidgetRemoved(EuroliteProWidget *widget) { RemoveWidget(widget); } -void AsyncPluginImpl::WidgetRemoved(class SunliteWidget *widget) { +void AsyncPluginImpl::WidgetRemoved(SunliteWidget *widget) { RemoveWidget(widget); } -void AsyncPluginImpl::WidgetRemoved(class VellemanWidget *widget) { +void AsyncPluginImpl::WidgetRemoved(VellemanWidget *widget) { RemoveWidget(widget); } diff --git a/plugins/usbdmx/FadecandyWidgetFactory.cpp b/plugins/usbdmx/FadecandyWidgetFactory.cpp new file mode 100644 index 0000000000..7865aebc41 --- /dev/null +++ b/plugins/usbdmx/FadecandyWidgetFactory.cpp @@ -0,0 +1,91 @@ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * 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 Library 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * FadecandyWidgetFactory.cpp + * The WidgetFactory for Fadecandy widgets. + * Copyright (C) 2014 Simon Newton + */ + +#include "plugins/usbdmx/FadecandyWidgetFactory.h" + +#include "ola/Logging.h" +#include "ola/base/Flags.h" +#include "plugins/usbdmx/FadecandyWidget.h" +#include "plugins/usbdmx/LibUsbAdaptor.h" + +DECLARE_bool(use_async_libusb); + + +namespace ola { +namespace plugin { +namespace usbdmx { + +const char FadecandyWidgetFactory::EXPECTED_MANUFACTURER[] = "scanlime"; +const char FadecandyWidgetFactory::EXPECTED_PRODUCT[] = "Fadecandy"; +const uint16_t FadecandyWidgetFactory::PRODUCT_ID = 0x1D50; +const uint16_t FadecandyWidgetFactory::VENDOR_ID = 0x607A; + + +bool FadecandyWidgetFactory::DeviceAdded( + WidgetObserver *observer, + libusb_device *usb_device, + const struct libusb_device_descriptor &descriptor) { + if (descriptor.idVendor != VENDOR_ID || descriptor.idProduct != PRODUCT_ID || + HasDevice(usb_device)) { + return false; + } + + OLA_INFO << "Found a new Fadecandy device"; + LibUsbAdaptor::DeviceInformation info; + if (!m_adaptor->GetDeviceInfo(usb_device, descriptor, &info)) { + return false; + } + + if (!m_adaptor->CheckManufacturer(EXPECTED_MANUFACTURER, info.manufacturer)) { + return false; + } + + if (!m_adaptor->CheckProduct(EXPECTED_PRODUCT, info.product)) { + return false; + } + + // Fadecandy devices may be missing serial numbers. Since there isn't another + // good way to uniquely identify a USB device, we only support one of these + // types of devices per host. + if (info.serial.empty()) { + if (m_missing_serial_number) { + OLA_WARN << "Failed to read serial number or serial number empty. " + << "We can only support one device without a serial number."; + return false; + } else { + OLA_WARN << "Failed to read serial number from " << info.manufacturer + << " : " << info.product + << " the device probably doesn't have one"; + m_missing_serial_number = true; + } + } + + FadecandyWidget *widget = NULL; + if (FLAGS_use_async_libusb) { + widget = new AsynchronousFadecandyWidget(m_adaptor, usb_device, + info.serial); + } else { + widget = new SynchronousFadecandyWidget(m_adaptor, usb_device, info.serial); + } + return AddWidget(observer, usb_device, widget); +} +} // namespace usbdmx +} // namespace plugin +} // namespace ola diff --git a/plugins/usbdmx/FadecandyWidgetFactory.h b/plugins/usbdmx/FadecandyWidgetFactory.h new file mode 100644 index 0000000000..a440ff63ed --- /dev/null +++ b/plugins/usbdmx/FadecandyWidgetFactory.h @@ -0,0 +1,60 @@ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * 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 Library 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * FadecandyWidgetFactory.h + * The WidgetFactory for Fadecandy / FadeCandy widgets. + * Copyright (C) 2014 Simon Newton + */ + +#ifndef PLUGINS_USBDMX_FADECANDYWIDGETFACTORY_H_ +#define PLUGINS_USBDMX_FADECANDYWIDGETFACTORY_H_ + +#include "ola/base/Macro.h" +#include "plugins/usbdmx/WidgetFactory.h" + +namespace ola { +namespace plugin { +namespace usbdmx { + +/** + * @brief Creates Fadecandy widgets. + */ +class FadecandyWidgetFactory : public BaseWidgetFactory { + public: + explicit FadecandyWidgetFactory(class LibUsbAdaptor *adaptor) + : m_missing_serial_number(false), + m_adaptor(adaptor) { + } + + bool DeviceAdded( + WidgetObserver *observer, + libusb_device *usb_device, + const struct libusb_device_descriptor &descriptor); + + private: + bool m_missing_serial_number; + class LibUsbAdaptor *m_adaptor; + + static const char EXPECTED_MANUFACTURER[]; + static const char EXPECTED_PRODUCT[]; + static const uint16_t PRODUCT_ID; + static const uint16_t VENDOR_ID; + + DISALLOW_COPY_AND_ASSIGN(FadecandyWidgetFactory); +}; +} // namespace usbdmx +} // namespace plugin +} // namespace ola +#endif // PLUGINS_USBDMX_FADECANDYWIDGETFACTORY_H_ diff --git a/plugins/usbdmx/Makefile.mk b/plugins/usbdmx/Makefile.mk index 598e0637d2..068bad5dc9 100644 --- a/plugins/usbdmx/Makefile.mk +++ b/plugins/usbdmx/Makefile.mk @@ -15,6 +15,10 @@ plugins_usbdmx_libolausbdmx_la_SOURCES = \ plugins/usbdmx/EuroliteProWidget.h \ plugins/usbdmx/EuroliteProWidgetFactory.cpp \ plugins/usbdmx/EuroliteProWidgetFactory.h \ + plugins/usbdmx/FadecandyWidget.cpp \ + plugins/usbdmx/FadecandyWidget.h \ + plugins/usbdmx/FadecandyWidgetFactory.cpp \ + plugins/usbdmx/FadecandyWidgetFactory.h \ plugins/usbdmx/FirmwareLoader.h \ plugins/usbdmx/GenericDevice.cpp \ plugins/usbdmx/GenericDevice.h \ diff --git a/plugins/usbdmx/SyncPluginImpl.cpp b/plugins/usbdmx/SyncPluginImpl.cpp index f185435e04..0539e39200 100644 --- a/plugins/usbdmx/SyncPluginImpl.cpp +++ b/plugins/usbdmx/SyncPluginImpl.cpp @@ -105,27 +105,27 @@ bool SyncPluginImpl::Stop() { return true; } -bool SyncPluginImpl::NewWidget(class AnymaWidget *widget) { +bool SyncPluginImpl::NewWidget(AnymaWidget *widget) { return StartAndRegisterDevice( widget, new GenericDevice(m_plugin, widget, "Anyma USB Device", "anyma-" + widget->SerialNumber())); } -bool SyncPluginImpl::NewWidget(class EuroliteProWidget *widget) { +bool SyncPluginImpl::NewWidget(EuroliteProWidget *widget) { return StartAndRegisterDevice( widget, new GenericDevice(m_plugin, widget, "EurolitePro USB Device", "eurolite-" + widget->SerialNumber())); } -bool SyncPluginImpl::NewWidget(class SunliteWidget *widget) { +bool SyncPluginImpl::NewWidget(SunliteWidget *widget) { return StartAndRegisterDevice( widget, new GenericDevice(m_plugin, widget, "Sunlite USBDMX2 Device", "usbdmx2")); } -bool SyncPluginImpl::NewWidget(class VellemanWidget *widget) { +bool SyncPluginImpl::NewWidget(VellemanWidget *widget) { return StartAndRegisterDevice( widget, new GenericDevice(m_plugin, widget, "Velleman USB Device", "velleman")); diff --git a/plugins/usbdmx/SyncronizedWidgetObserver.h b/plugins/usbdmx/SyncronizedWidgetObserver.h index e14e81f790..fb8b0c0631 100644 --- a/plugins/usbdmx/SyncronizedWidgetObserver.h +++ b/plugins/usbdmx/SyncronizedWidgetObserver.h @@ -55,6 +55,10 @@ class SyncronizedWidgetObserver : public WidgetObserver { return DispatchNewWidget(widget); } + bool NewWidget(class FadecandyWidget *widget) { + return DispatchNewWidget(widget); + } + bool NewWidget(class SunliteWidget *widget) { return DispatchNewWidget(widget); } @@ -71,6 +75,10 @@ class SyncronizedWidgetObserver : public WidgetObserver { DispatchWidgetRemoved(widget); } + void WidgetRemoved(class FadecandyWidget *widget) { + DispatchWidgetRemoved(widget); + } + void WidgetRemoved(class SunliteWidget *widget) { DispatchWidgetRemoved(widget); } diff --git a/plugins/usbdmx/UsbDmxPlugin.cpp b/plugins/usbdmx/UsbDmxPlugin.cpp index 12fdaf0839..a47f441472 100644 --- a/plugins/usbdmx/UsbDmxPlugin.cpp +++ b/plugins/usbdmx/UsbDmxPlugin.cpp @@ -20,10 +20,6 @@ #include "plugins/usbdmx/UsbDmxPlugin.h" -#include -#include -#include - #include #include "ola/Logging.h" @@ -96,7 +92,7 @@ string UsbDmxPlugin::Description() const { "----------------------------\n" "\n" "This plugin supports various USB DMX devices including the \n" -"Anyma uDMX, Sunlite USBDMX2 & Velleman K8062.\n" +"Anyma uDMX, Eurolite, Sunlite USBDMX2 & Velleman K8062.\n" "\n" "--- Config file : ola-usbdmx.conf ---\n" "\n" diff --git a/plugins/usbdmx/WidgetFactory.h b/plugins/usbdmx/WidgetFactory.h index 61f9a91559..5cd80a681d 100644 --- a/plugins/usbdmx/WidgetFactory.h +++ b/plugins/usbdmx/WidgetFactory.h @@ -66,6 +66,15 @@ class WidgetObserver { */ virtual bool NewWidget(class EuroliteProWidget *widget) = 0; + /** + * @brief Called when a new FadecandyWidget is added. + * @param widget the new Widget, ownership is not transferred but the object + * may be used until the corresponding WidgetRemoved() call is made. + * @returns true if the widget has been claimed, false if the widget was + * ignored. + */ + virtual bool NewWidget(class FadecandyWidget *widget) = 0; + /** * @brief Called when a new SunliteWidget is added. * @param widget the new Widget, ownership is not transferred but the object @@ -100,6 +109,14 @@ class WidgetObserver { */ virtual void WidgetRemoved(class EuroliteProWidget *widget) = 0; + /** + * @brief Called when a FadecandyWidget is removed. + * @param widget the Widget that has been removed. + * + * It is an error to use the widget once this call completes. + */ + virtual void WidgetRemoved(class FadecandyWidget *widget) = 0; + /** * @brief Called when a SunliteWidget is removed. * @param widget the Widget that has been removed. From 37dabcf8f7b88ede61d7aff765b1e55069f1254c Mon Sep 17 00:00:00 2001 From: Simon Newton Date: Sat, 22 Nov 2014 09:08:55 -0800 Subject: [PATCH 29/34] Add initial support for the fadecandy widget. Peter to test. --- common/utils/DmxBuffer.cpp | 5 +++++ plugins/usbdmx/AsyncPluginImpl.cpp | 15 ++++++++++++++- plugins/usbdmx/AsyncPluginImpl.h | 2 ++ plugins/usbdmx/SyncPluginImpl.cpp | 9 +++++++++ plugins/usbdmx/SyncPluginImpl.h | 2 ++ plugins/usbdmx/UsbDmxPlugin.cpp | 2 +- 6 files changed, 33 insertions(+), 2 deletions(-) diff --git a/common/utils/DmxBuffer.cpp b/common/utils/DmxBuffer.cpp index 9ff3c2c205..a664e2f8c2 100644 --- a/common/utils/DmxBuffer.cpp +++ b/common/utils/DmxBuffer.cpp @@ -261,6 +261,11 @@ void DmxBuffer::Get(uint8_t *data, unsigned int *length) const { void DmxBuffer::GetRange(unsigned int slot, uint8_t *data, unsigned int *length) const { + if (slot >= m_length) { + *length = 0; + return; + } + if (m_data) { *length = min(*length, m_length - slot); memcpy(data, m_data + slot, *length); diff --git a/plugins/usbdmx/AsyncPluginImpl.cpp b/plugins/usbdmx/AsyncPluginImpl.cpp index adf1ed58cf..862f2cb18c 100644 --- a/plugins/usbdmx/AsyncPluginImpl.cpp +++ b/plugins/usbdmx/AsyncPluginImpl.cpp @@ -34,6 +34,8 @@ #include "plugins/usbdmx/AnymaWidget.h" #include "plugins/usbdmx/AnymaWidgetFactory.h" #include "plugins/usbdmx/EuroliteProWidgetFactory.h" +#include "plugins/usbdmx/FadecandyWidget.h" +#include "plugins/usbdmx/FadecandyWidgetFactory.h" #include "plugins/usbdmx/GenericDevice.h" #include "plugins/usbdmx/LibUsbAdaptor.h" #include "plugins/usbdmx/LibUsbThread.h" @@ -183,6 +185,13 @@ bool AsyncPluginImpl::NewWidget(EuroliteProWidget *widget) { "eurolite-" + widget->SerialNumber())); } +bool AsyncPluginImpl::NewWidget(FadecandyWidget *widget) { + return StartAndRegisterDevice( + widget, + new GenericDevice(m_plugin, widget, "Fadecandy USB Device", + "fadecandy-" + widget->SerialNumber())); +} + bool AsyncPluginImpl::NewWidget(SunliteWidget *widget) { return StartAndRegisterDevice( widget, @@ -203,6 +212,10 @@ void AsyncPluginImpl::WidgetRemoved(EuroliteProWidget *widget) { RemoveWidget(widget); } +void AsyncPluginImpl::WidgetRemoved(FadecandyWidget *widget) { + RemoveWidget(widget); +} + void AsyncPluginImpl::WidgetRemoved(SunliteWidget *widget) { RemoveWidget(widget); } @@ -287,7 +300,7 @@ bool AsyncPluginImpl::StartAndRegisterDevice(Widget *widget, Device *device) { * * This is run within the main thread. */ -void AsyncPluginImpl::RemoveWidget(class Widget *widget) { +void AsyncPluginImpl::RemoveWidget(Widget *widget) { Device *device = STLLookupAndRemovePtr(&m_widget_device_map, widget); if (device) { m_plugin_adaptor->UnregisterDevice(device); diff --git a/plugins/usbdmx/AsyncPluginImpl.h b/plugins/usbdmx/AsyncPluginImpl.h index c692f3b0fd..7d3623e19d 100644 --- a/plugins/usbdmx/AsyncPluginImpl.h +++ b/plugins/usbdmx/AsyncPluginImpl.h @@ -84,11 +84,13 @@ class AsyncPluginImpl: public PluginImplInterface, public WidgetObserver { bool NewWidget(class AnymaWidget *widget); bool NewWidget(class EuroliteProWidget *widget); + bool NewWidget(class FadecandyWidget *widget); bool NewWidget(class SunliteWidget *widget); bool NewWidget(class VellemanWidget *widget); void WidgetRemoved(class AnymaWidget *widget); void WidgetRemoved(class EuroliteProWidget *widget); + void WidgetRemoved(class FadecandyWidget *widget); void WidgetRemoved(class SunliteWidget *widget); void WidgetRemoved(class VellemanWidget *widget); diff --git a/plugins/usbdmx/SyncPluginImpl.cpp b/plugins/usbdmx/SyncPluginImpl.cpp index 0539e39200..cfbc54c25d 100644 --- a/plugins/usbdmx/SyncPluginImpl.cpp +++ b/plugins/usbdmx/SyncPluginImpl.cpp @@ -37,6 +37,8 @@ #include "plugins/usbdmx/AnymaWidgetFactory.h" #include "plugins/usbdmx/EuroliteProWidget.h" #include "plugins/usbdmx/EuroliteProWidgetFactory.h" +#include "plugins/usbdmx/FadecandyWidget.h" +#include "plugins/usbdmx/FadecandyWidgetFactory.h" #include "plugins/usbdmx/GenericDevice.h" #include "plugins/usbdmx/SunliteWidget.h" #include "plugins/usbdmx/SunliteWidgetFactory.h" @@ -119,6 +121,13 @@ bool SyncPluginImpl::NewWidget(EuroliteProWidget *widget) { "eurolite-" + widget->SerialNumber())); } +bool SyncPluginImpl::NewWidget(FadecandyWidget *widget) { + return StartAndRegisterDevice( + widget, + new GenericDevice(m_plugin, widget, "FadeCandy USB Device", + "fadecandy-" + widget->SerialNumber())); +} + bool SyncPluginImpl::NewWidget(SunliteWidget *widget) { return StartAndRegisterDevice( widget, diff --git a/plugins/usbdmx/SyncPluginImpl.h b/plugins/usbdmx/SyncPluginImpl.h index c54438b7bc..fd06262c96 100644 --- a/plugins/usbdmx/SyncPluginImpl.h +++ b/plugins/usbdmx/SyncPluginImpl.h @@ -69,11 +69,13 @@ class SyncPluginImpl: public PluginImplInterface, public WidgetObserver { bool NewWidget(class AnymaWidget *widget); bool NewWidget(class EuroliteProWidget *widget); + bool NewWidget(class FadecandyWidget *widget); bool NewWidget(class SunliteWidget *widget); bool NewWidget(class VellemanWidget *widget); void WidgetRemoved(OLA_UNUSED class AnymaWidget *widget) {} void WidgetRemoved(OLA_UNUSED class EuroliteProWidget *widget) {} + void WidgetRemoved(OLA_UNUSED class FadecandyWidget *widget) {} void WidgetRemoved(OLA_UNUSED class SunliteWidget *widget) {} void WidgetRemoved(OLA_UNUSED class VellemanWidget *widget) {} diff --git a/plugins/usbdmx/UsbDmxPlugin.cpp b/plugins/usbdmx/UsbDmxPlugin.cpp index a47f441472..e2a31c84e2 100644 --- a/plugins/usbdmx/UsbDmxPlugin.cpp +++ b/plugins/usbdmx/UsbDmxPlugin.cpp @@ -92,7 +92,7 @@ string UsbDmxPlugin::Description() const { "----------------------------\n" "\n" "This plugin supports various USB DMX devices including the \n" -"Anyma uDMX, Eurolite, Sunlite USBDMX2 & Velleman K8062.\n" +"Anyma uDMX, Eurolite, Fadecandy, Sunlite USBDMX2 & Velleman K8062.\n" "\n" "--- Config file : ola-usbdmx.conf ---\n" "\n" From fbffe57e93565a7c98f5c296e12ffc247f2c6b74 Mon Sep 17 00:00:00 2001 From: Simon Newton Date: Sat, 22 Nov 2014 09:13:38 -0800 Subject: [PATCH 30/34] Add the missing fadecandy files. --- plugins/usbdmx/FadecandyWidget.cpp | 326 +++++++++++++++++++++++++++++ plugins/usbdmx/FadecandyWidget.h | 123 +++++++++++ 2 files changed, 449 insertions(+) create mode 100644 plugins/usbdmx/FadecandyWidget.cpp create mode 100644 plugins/usbdmx/FadecandyWidget.h diff --git a/plugins/usbdmx/FadecandyWidget.cpp b/plugins/usbdmx/FadecandyWidget.cpp new file mode 100644 index 0000000000..427dce358e --- /dev/null +++ b/plugins/usbdmx/FadecandyWidget.cpp @@ -0,0 +1,326 @@ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * 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 Library 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * FadecandyWidget.cpp + * The synchronous and asynchronous Fadecandy widgets. + * Copyright (C) 2014 Simon Newton + */ + +#include "plugins/usbdmx/FadecandyWidget.h" + +#include +#include +#include +#include + +#include "ola/Constants.h" +#include "ola/Logging.h" +#include "ola/StringUtils.h" +#include "ola/util/Utils.h" +#include "plugins/usbdmx/AsyncUsbSender.h" +#include "plugins/usbdmx/LibUsbAdaptor.h" +#include "plugins/usbdmx/ThreadedUsbSender.h" + +namespace ola { +namespace plugin { +namespace usbdmx { + +using std::string; + +namespace { + +static const unsigned char ENDPOINT = 0x01; +// 2s is a really long time. Can we reduce this? +static const unsigned int URB_TIMEOUT_MS = 2000; +static const int INTERFACE = 0; + +// A data frame. +static const uint8_t TYPE_FRAMEBUFFER = 0x00; +// The color lookup table +static const uint8_t TYPE_LUT = 0x40; +// The initial setup message. +static const uint8_t CONFIG_MESSAGE = 0x80; +// The final packet in a set. +static const uint8_t FINAL = 0x20; + +// Options used in the first data byte of the config message. +static const uint8_t OPTION_NO_DITHERING = 0x01; +static const uint8_t OPTION_NO_INTERPOLATION = 0x02; +// static const uint8_t OPTION_NO_ACTIVITY_LED = 0x03; +// static const uint8_t OPTION_LED_CONTROL = 0x04; + +// Each 'packet' is 63 bytes, or 21 RGB pixels. +enum { SLOTS_PER_PACKET = 63 }; +static const uint8_t PACKETS_PER_UPDATE = 25; + +PACK( +struct fadecandy_packet { + uint8_t type; + uint8_t data[SLOTS_PER_PACKET]; + + void Reset() { + type = 0; + memset(data, 0, sizeof(data)); + } + + fadecandy_packet() { + Reset(); + } +}); + +bool InitializeWidget(LibUsbAdaptor *adaptor, + libusb_device_handle *usb_handle) { + // Set the fadecandy configuration. + fadecandy_packet packet; + packet.type = CONFIG_MESSAGE; + packet.data[0] = OPTION_NO_DITHERING | OPTION_NO_INTERPOLATION; + + // packet.data[0] = OPTION_NO_ACTIVITY_LED; // Manual control of LED + // packet.data[0] |= OPTION_LED_CONTROL; // Manual LED state + + int bytes_sent = 0; + adaptor->BulkTransfer(usb_handle, ENDPOINT, + reinterpret_cast(&packet), + sizeof(packet), &bytes_sent, URB_TIMEOUT_MS); + OLA_INFO << "Config transferred " << bytes_sent << " bytes"; + + // Build the Look Up Table + uint16_t lut[3][257]; + memset(&lut, 0, sizeof(lut)); + for (unsigned int channel = 0; channel < 3; channel++) { + for (unsigned int value = 0; value < 257; value++) { + // Fadecandy Python Example + // lut[channel][value] = std::min( + // static_cast(std::numeric_limits::max()), + // int(pow(value / 256.0, 2.2) * + // (std::numeric_limits::max() + 1))); + // 1:1 + lut[channel][value] = std::min( + static_cast(std::numeric_limits::max()), + (value << 8)); + OLA_DEBUG << "Generated LUT for channel " << channel << " value " + << value << " with val " << lut[channel][value]; + } + } + + OLA_INFO << "LUT size " << (sizeof(lut) / 2); + unsigned int index = 0; + packet.Reset(); + + // Transfer the lookup table. + for (unsigned int channel = 0; channel < 3; channel++) { + for (unsigned int value = 0; value < 257; value++) { + unsigned int entry = (channel * 257) + value; + unsigned int packet_entry = entry % 31; + OLA_DEBUG << "Working on channel " << channel << " value " << value + << " (" << IntToHexString(value) << ") with entry " << entry + << ", packet entry " << packet_entry << " with val " + << IntToHexString(lut[channel][value]); + ola::utils::SplitUInt16(lut[channel][value], + &packet.data[packet_entry + 1], + &packet.data[packet_entry]); + if ((packet_entry == 30) || (entry == ((3*257) - 1))) { + packet.type = TYPE_LUT | index; + if (entry == ((3*257) - 1)) { + OLA_DEBUG << "Setting final flag on packet"; + packet.type = FINAL; + } + packet.type = TYPE_LUT | index; + packet.data[0] = 0; // Reserved + + // Send the data + int lut_txed = 0; + + // TODO(Peter): Fix the calculations and transmit this + // m_adaptor->BulkTransfer(usb_handle, + // ENDPOINT, + // packet, + // sizeof(packet), + // &lut_txed, + // URB_TIMEOUT_MS); + + OLA_INFO << "LUT packet " << index << " transferred " << lut_txed + << " bytes"; + + // Get ready for the next packet + index++; + packet.Reset(); + } + } + } + return true; +} + +void UpdatePacketsWithDMX(fadecandy_packet packets[PACKETS_PER_UPDATE], + const DmxBuffer &buffer) { + for (unsigned int packet_index = 0; packet_index < PACKETS_PER_UPDATE; + packet_index++) { + packets[packet_index].Reset(); + + unsigned int dmx_offset = packet_index * SLOTS_PER_PACKET; + unsigned int slots_in_packet = SLOTS_PER_PACKET; + buffer.GetRange(dmx_offset, packets[packet_index].data, + &slots_in_packet); + + packets[packet_index].type = TYPE_FRAMEBUFFER | packet_index; + if (packet_index == PACKETS_PER_UPDATE - 1) { + packets[packet_index].type |= FINAL; + } + } +} + +} // namespace + +// FadecandyThreadedSender +// ----------------------------------------------------------------------------- + +/* + * Sends messages to a Fadecandy device in a separate thread. + */ +class FadecandyThreadedSender: public ThreadedUsbSender { + public: + FadecandyThreadedSender(LibUsbAdaptor *adaptor, + libusb_device *usb_device, + libusb_device_handle *handle) + : ThreadedUsbSender(usb_device, handle), + m_adaptor(adaptor) { + } + + private: + LibUsbAdaptor* const m_adaptor; + fadecandy_packet m_data_packets[PACKETS_PER_UPDATE]; + + bool TransmitBuffer(libusb_device_handle *handle, + const DmxBuffer &buffer); +}; + +bool FadecandyThreadedSender::TransmitBuffer(libusb_device_handle *handle, + const DmxBuffer &buffer) { + UpdatePacketsWithDMX(m_data_packets, buffer); + + int bytes_sent = 0; + // We do a single bulk transfer of the entire data, rather than on transfer + // for each 64 bytes. + int r = m_adaptor->BulkTransfer( + handle, ENDPOINT, + reinterpret_cast(&m_data_packets), + sizeof(m_data_packets), &bytes_sent, + URB_TIMEOUT_MS); + return r == 0; +} + +// SynchronousFadecandyWidget +// ----------------------------------------------------------------------------- + +SynchronousFadecandyWidget::SynchronousFadecandyWidget( + LibUsbAdaptor *adaptor, + libusb_device *usb_device, + const std::string &serial) + : FadecandyWidget(adaptor, serial), + m_usb_device(usb_device) { +} + +bool SynchronousFadecandyWidget::Init() { + libusb_device_handle *usb_handle; + + bool ok = m_adaptor->OpenDeviceAndClaimInterface( + m_usb_device, INTERFACE, &usb_handle); + if (!ok) { + return false; + } + + if (!InitializeWidget(m_adaptor, usb_handle)) { + m_adaptor->Close(usb_handle); + return false; + } + + std::auto_ptr sender( + new FadecandyThreadedSender(m_adaptor, m_usb_device, usb_handle)); + if (!sender->Start()) { + return false; + } + m_sender.reset(sender.release()); + return true; +} + +bool SynchronousFadecandyWidget::SendDMX(const DmxBuffer &buffer) { + return m_sender.get() ? m_sender->SendDMX(buffer) : false; +} + +// FadecandyAsyncUsbSender +// ----------------------------------------------------------------------------- +class FadecandyAsyncUsbSender : public AsyncUsbSender { + public: + FadecandyAsyncUsbSender(LibUsbAdaptor *adaptor, + libusb_device *usb_device) + : AsyncUsbSender(adaptor, usb_device) { + } + + libusb_device_handle* SetupHandle(); + + bool PerformTransfer(const DmxBuffer &buffer); + + private: + fadecandy_packet m_data_packets[PACKETS_PER_UPDATE]; + + DISALLOW_COPY_AND_ASSIGN(FadecandyAsyncUsbSender); +}; + +libusb_device_handle* FadecandyAsyncUsbSender::SetupHandle() { + libusb_device_handle *usb_handle; + if (!m_adaptor->OpenDeviceAndClaimInterface( + m_usb_device, INTERFACE, &usb_handle)) { + return NULL; + } + + if (!InitializeWidget(m_adaptor, usb_handle)) { + m_adaptor->Close(usb_handle); + return NULL; + } + return usb_handle; +} + +bool FadecandyAsyncUsbSender::PerformTransfer(const DmxBuffer &buffer) { + UpdatePacketsWithDMX(m_data_packets, buffer); + // We do a single bulk transfer of the entire data, rather than on transfer + // for each 64 bytes. + FillBulkTransfer(ENDPOINT, + reinterpret_cast(&m_data_packets), + sizeof(m_data_packets), + URB_TIMEOUT_MS); + return SubmitTransfer() == 0; +} + +// AsynchronousFadecandyWidget +// ----------------------------------------------------------------------------- + +AsynchronousFadecandyWidget::AsynchronousFadecandyWidget( + LibUsbAdaptor *adaptor, + libusb_device *usb_device, + const std::string &serial) + : FadecandyWidget(adaptor, serial) { + m_sender.reset(new FadecandyAsyncUsbSender(m_adaptor, usb_device)); +} + +bool AsynchronousFadecandyWidget::Init() { + return m_sender->Init(); +} + +bool AsynchronousFadecandyWidget::SendDMX(const DmxBuffer &buffer) { + return m_sender->SendDMX(buffer); +} +} // namespace usbdmx +} // namespace plugin +} // namespace ola diff --git a/plugins/usbdmx/FadecandyWidget.h b/plugins/usbdmx/FadecandyWidget.h new file mode 100644 index 0000000000..63e6e56ea8 --- /dev/null +++ b/plugins/usbdmx/FadecandyWidget.h @@ -0,0 +1,123 @@ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * 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 Library 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * FadecandyWidget.h + * The synchronous and asynchronous Fadecandy widgets. + * Copyright (C) 2014 Simon Newton + */ + +#ifndef PLUGINS_USBDMX_FADECANDYWIDGET_H_ +#define PLUGINS_USBDMX_FADECANDYWIDGET_H_ + +#include +#include +#include + +#include "ola/DmxBuffer.h" +#include "ola/base/Macro.h" +#include "ola/thread/Mutex.h" +#include "plugins/usbdmx/Widget.h" + +namespace ola { +namespace plugin { +namespace usbdmx { + +/** + * @brief The interface for the Fadecandy Widgets. + * + * Fadecandy devices have 8 physical ports. Each port can drive 64 RGB pixels. + * Ideally this means we'd model each Fadecandy port as an OLA port, but that + * introduces syncronization issues, since the underlying protocol models all 8 + * ports as a flat pixel array. For now we just expose the first 170 pixels. + * + * See https://github.com/scanlime/fadecandy/blob/master/README.md for more + * information on Fadecandy devices. + */ +class FadecandyWidget: public BaseWidget { + public: + FadecandyWidget(LibUsbAdaptor *adaptor, + const std::string &serial) + : BaseWidget(adaptor), + m_serial(serial) { + } + + /** + * @brief Get the serial number of this widget. + * @returns The serial number of the widget. + */ + std::string SerialNumber() const { + return m_serial; + } + + private: + std::string m_serial; +}; + +/** + * @brief An Fadecandy widget that uses synchronous libusb operations. + * + * Internally this spawns a new thread to avoid blocking SendDMX() calls. + */ +class SynchronousFadecandyWidget: public FadecandyWidget { + public: + /** + * @brief Create a new SynchronousFadecandyWidget. + * @param adaptor the LibUsbAdaptor to use. + * @param usb_device the libusb_device to use for the widget. + * @param serial the serial number of the widget. + */ + SynchronousFadecandyWidget(LibUsbAdaptor *adaptor, + libusb_device *usb_device, + const std::string &serial); + + bool Init(); + + bool SendDMX(const DmxBuffer &buffer); + + private: + libusb_device* const m_usb_device; + std::auto_ptr m_sender; + + DISALLOW_COPY_AND_ASSIGN(SynchronousFadecandyWidget); +}; + +/** + * @brief An Fadecandy widget that uses asynchronous libusb operations. + */ +class AsynchronousFadecandyWidget : public FadecandyWidget { + public: + /** + * @brief Create a new AsynchronousFadecandyWidget. + * @param adaptor the LibUsbAdaptor to use. + * @param usb_device the libusb_device to use for the widget. + * @param serial the serial number of the widget. + */ + AsynchronousFadecandyWidget(LibUsbAdaptor *adaptor, + libusb_device *usb_device, + const std::string &serial); + + bool Init(); + + bool SendDMX(const DmxBuffer &buffer); + + private: + std::auto_ptr m_sender; + + DISALLOW_COPY_AND_ASSIGN(AsynchronousFadecandyWidget); +}; +} // namespace usbdmx +} // namespace plugin +} // namespace ola +#endif // PLUGINS_USBDMX_FADECANDYWIDGET_H_ From d1e0b85abbeaea4b79be343ff62e08ab347011cf Mon Sep 17 00:00:00 2001 From: Simon Newton Date: Sat, 22 Nov 2014 09:33:29 -0800 Subject: [PATCH 31/34] Fix a lint warning --- plugins/usbdmx/FadecandyWidget.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/plugins/usbdmx/FadecandyWidget.cpp b/plugins/usbdmx/FadecandyWidget.cpp index 427dce358e..befc787267 100644 --- a/plugins/usbdmx/FadecandyWidget.cpp +++ b/plugins/usbdmx/FadecandyWidget.cpp @@ -23,6 +23,7 @@ #include #include #include +#include #include #include "ola/Constants.h" From 7b0e4ae3a15b3125dc752033d28c40b703b06541 Mon Sep 17 00:00:00 2001 From: Simon Newton Date: Sat, 22 Nov 2014 10:04:31 -0800 Subject: [PATCH 32/34] Fix a bug in the Async sunlite widget. --- plugins/usbdmx/SunliteWidget.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/plugins/usbdmx/SunliteWidget.cpp b/plugins/usbdmx/SunliteWidget.cpp index 5ad3955915..ca637eb006 100644 --- a/plugins/usbdmx/SunliteWidget.cpp +++ b/plugins/usbdmx/SunliteWidget.cpp @@ -167,6 +167,7 @@ class SunliteAsyncUsbSender : public AsyncUsbSender { SunliteAsyncUsbSender(LibUsbAdaptor *adaptor, libusb_device *usb_device) : AsyncUsbSender(adaptor, usb_device) { + InitPacket(m_packet); } ~SunliteAsyncUsbSender() { From 4acccb210ac841451d23ca00aff84aaa39ccf8f2 Mon Sep 17 00:00:00 2001 From: Simon Newton Date: Sun, 23 Nov 2014 07:51:30 -0800 Subject: [PATCH 33/34] Fix some spelling. --- plugins/usbdmx/FadecandyWidget.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/usbdmx/FadecandyWidget.cpp b/plugins/usbdmx/FadecandyWidget.cpp index befc787267..1fbee31476 100644 --- a/plugins/usbdmx/FadecandyWidget.cpp +++ b/plugins/usbdmx/FadecandyWidget.cpp @@ -212,7 +212,7 @@ bool FadecandyThreadedSender::TransmitBuffer(libusb_device_handle *handle, UpdatePacketsWithDMX(m_data_packets, buffer); int bytes_sent = 0; - // We do a single bulk transfer of the entire data, rather than on transfer + // We do a single bulk transfer of the entire data, rather than one transfer // for each 64 bytes. int r = m_adaptor->BulkTransfer( handle, ENDPOINT, @@ -295,7 +295,7 @@ libusb_device_handle* FadecandyAsyncUsbSender::SetupHandle() { bool FadecandyAsyncUsbSender::PerformTransfer(const DmxBuffer &buffer) { UpdatePacketsWithDMX(m_data_packets, buffer); - // We do a single bulk transfer of the entire data, rather than on transfer + // We do a single bulk transfer of the entire data, rather than one transfer // for each 64 bytes. FillBulkTransfer(ENDPOINT, reinterpret_cast(&m_data_packets), From 5965ccc602313f7a99b6314c792adfe75c7ebf2c Mon Sep 17 00:00:00 2001 From: Simon Newton Date: Sun, 23 Nov 2014 08:37:36 -0800 Subject: [PATCH 34/34] Only try to detach the kernel driver if there is one active. --- plugins/usbdmx/LibUsbAdaptor.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/plugins/usbdmx/LibUsbAdaptor.cpp b/plugins/usbdmx/LibUsbAdaptor.cpp index 591dd57c85..f8b45257a6 100644 --- a/plugins/usbdmx/LibUsbAdaptor.cpp +++ b/plugins/usbdmx/LibUsbAdaptor.cpp @@ -163,7 +163,11 @@ int BaseLibUsbAdaptor::ClaimInterface(libusb_device_handle *dev, int BaseLibUsbAdaptor::DetachKernelDriver(libusb_device_handle *dev, int interface_number) { - return libusb_detach_kernel_driver(dev, interface_number); + if (libusb_kernel_driver_active(dev, interface_number)) { + return libusb_detach_kernel_driver(dev, interface_number); + } else { + return 0; + } } int BaseLibUsbAdaptor::GetActiveConfigDescriptor(