From e5b3b0cab0041059cf86d4fbb81c67b693f9720f Mon Sep 17 00:00:00 2001 From: Alexander Simon Date: Thu, 27 Jul 2023 16:05:39 +0200 Subject: [PATCH 01/10] Support added for Nicolaudie SIUDI Support Nicolaudie Sunlite intelligent USB DMX interface. These interfaces require a constant stream of the whole DMX field. Otherwise, the interface will go into a standby mode. --- plugins/usbdmx/AsyncPluginImpl.cpp | 8 + plugins/usbdmx/AsyncPluginImpl.h | 1 + plugins/usbdmx/Makefile.mk | 4 + plugins/usbdmx/README.md | 1 + plugins/usbdmx/Siudi.cpp | 155 ++++++++++++++++++++ plugins/usbdmx/Siudi.h | 76 ++++++++++ plugins/usbdmx/SiudiFactory.cpp | 51 +++++++ plugins/usbdmx/SiudiFactory.h | 62 ++++++++ plugins/usbdmx/SyncPluginImpl.cpp | 9 ++ plugins/usbdmx/SyncPluginImpl.h | 2 + plugins/usbdmx/SynchronizedWidgetObserver.h | 4 + plugins/usbdmx/UsbDmxPlugin.h | 2 +- plugins/usbdmx/WidgetFactory.h | 9 ++ 13 files changed, 383 insertions(+), 1 deletion(-) create mode 100644 plugins/usbdmx/Siudi.cpp create mode 100644 plugins/usbdmx/Siudi.h create mode 100644 plugins/usbdmx/SiudiFactory.cpp create mode 100644 plugins/usbdmx/SiudiFactory.h diff --git a/plugins/usbdmx/AsyncPluginImpl.cpp b/plugins/usbdmx/AsyncPluginImpl.cpp index c7bd75bfae..2bd0a68a58 100644 --- a/plugins/usbdmx/AsyncPluginImpl.cpp +++ b/plugins/usbdmx/AsyncPluginImpl.cpp @@ -53,6 +53,7 @@ #include "plugins/usbdmx/ScanlimeFadecandy.h" #include "plugins/usbdmx/ScanlimeFadecandyFactory.h" #include "plugins/usbdmx/ShowJockeyDMXU1Factory.h" +#include "plugins/usbdmx/SiudiFactory.h" #include "plugins/usbdmx/SunliteFactory.h" #include "plugins/usbdmx/VellemanK8062.h" #include "plugins/usbdmx/VellemanK8062Factory.h" @@ -132,6 +133,7 @@ bool AsyncPluginImpl::Start() { m_widget_factories.push_back( new ScanlimeFadecandyFactory(m_usb_adaptor)); m_widget_factories.push_back(new ShowJockeyDMXU1Factory(m_usb_adaptor)); + m_widget_factories.push_back(new SiudiFactory(m_usb_adaptor)); m_widget_factories.push_back(new SunliteFactory(m_usb_adaptor)); m_widget_factories.push_back(new VellemanK8062Factory(m_usb_adaptor)); @@ -233,6 +235,12 @@ bool AsyncPluginImpl::NewWidget(ShowJockeyDMXU1 *widget) { "showjockey-dmx-u1-" + widget->SerialNumber())); } +bool AsyncPluginImpl::NewWidget(Siudi *widget) { + return StartAndRegisterDevice( + widget, + new GenericDevice(m_plugin, widget, "Nicolaudie SIUDI", "usbsiudi")); +} + bool AsyncPluginImpl::NewWidget(Sunlite *widget) { return StartAndRegisterDevice( widget, diff --git a/plugins/usbdmx/AsyncPluginImpl.h b/plugins/usbdmx/AsyncPluginImpl.h index 56282281c9..7fec41109a 100644 --- a/plugins/usbdmx/AsyncPluginImpl.h +++ b/plugins/usbdmx/AsyncPluginImpl.h @@ -85,6 +85,7 @@ class AsyncPluginImpl: public PluginImplInterface, public WidgetObserver { bool NewWidget(ola::usb::JaRuleWidget *widget); bool NewWidget(class ScanlimeFadecandy *widget); bool NewWidget(class ShowJockeyDMXU1 *widget); + bool NewWidget(class Siudi *widget); bool NewWidget(class Sunlite *widget); bool NewWidget(class VellemanK8062 *widget); diff --git a/plugins/usbdmx/Makefile.mk b/plugins/usbdmx/Makefile.mk index daf83a2297..536bac3d00 100644 --- a/plugins/usbdmx/Makefile.mk +++ b/plugins/usbdmx/Makefile.mk @@ -49,6 +49,10 @@ plugins_usbdmx_libolausbdmxwidget_la_SOURCES = \ plugins/usbdmx/ShowJockeyDMXU1.h \ plugins/usbdmx/ShowJockeyDMXU1Factory.cpp \ plugins/usbdmx/ShowJockeyDMXU1Factory.h \ + plugins/usbdmx/Siudi.cpp \ + plugins/usbdmx/Siudi.h \ + plugins/usbdmx/SiudiFactory.cpp \ + plugins/usbdmx/SiudiFactory.h \ plugins/usbdmx/Sunlite.cpp \ plugins/usbdmx/Sunlite.h \ plugins/usbdmx/SunliteFactory.cpp \ diff --git a/plugins/usbdmx/README.md b/plugins/usbdmx/README.md index 79d0eb8b88..c654960870 100644 --- a/plugins/usbdmx/README.md +++ b/plugins/usbdmx/README.md @@ -15,6 +15,7 @@ This plugin supports various USB DMX devices including: * FX5 DMX * ShowJockey SJ-DMX-U1 * Sunlite USBDMX2 +* Nicoleaudie Sunlite intelligent USB DMX interface (SIUDI) (also ADJ MyDMX) * Velleman K8062. diff --git a/plugins/usbdmx/Siudi.cpp b/plugins/usbdmx/Siudi.cpp new file mode 100644 index 0000000000..996f3e0dbd --- /dev/null +++ b/plugins/usbdmx/Siudi.cpp @@ -0,0 +1,155 @@ +/* + * 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. + * + * Siudi.cpp + * The synchronous SIUDI widgets. + * Copyright (C) 2023 Alexander Simon + */ + +#include "plugins/usbdmx/Siudi.h" + +#include +#include + +#include "libs/usb/LibUsbAdaptor.h" +#include "ola/Constants.h" +#include "ola/Logging.h" +#include "plugins/usbdmx/AsyncUsbSender.h" +#include "plugins/usbdmx/ThreadedUsbSender.h" + +namespace ola { +namespace plugin { +namespace usbdmx { + +using ola::usb::LibUsbAdaptor; + +namespace { + +static const uint8_t ENDPOINT = 2; +static const unsigned int TIMEOUT = 50; // 50ms is ok +enum {SIUDI_PACKET_SIZE = 512}; + +} // namespace + +// SiudiThreadedSender +// ----------------------------------------------------------------------------- + +/* + * Sends messages to a SIUDI device in a separate thread. + */ +class SiudiThreadedSender: public ThreadedUsbSender { + public: + SiudiThreadedSender(LibUsbAdaptor *adaptor, + libusb_device *usb_device, + libusb_device_handle *handle); + + bool Start(); + +private: + LibUsbAdaptor* const m_adaptor; + uint8_t m_packet[SIUDI_PACKET_SIZE]; + libusb_device_handle* const m_usb_handle; + + bool TransmitBuffer(libusb_device_handle *handle, + const DmxBuffer &buffer); +}; + +SiudiThreadedSender::SiudiThreadedSender( + LibUsbAdaptor *adaptor, + libusb_device *usb_device, + libusb_device_handle *usb_handle) + : ThreadedUsbSender(usb_device, usb_handle), + m_adaptor(adaptor), m_usb_handle(usb_handle) { + memset(m_packet, 0x00, SIUDI_PACKET_SIZE); +} + +bool SiudiThreadedSender::Start() { + if (!ThreadedUsbSender::Start()) { + return false; + } + + // Read device info. This call takes about 270 ms. + // Discard the buffer as the format is currently unknown. + uint8_t buf[64]; + int ret = libusb_control_transfer(m_usb_handle, + LIBUSB_REQUEST_TYPE_VENDOR | LIBUSB_ENDPOINT_IN, + 0x3f, 0x00c4, 1, buf, 64, 500); + if (ret != 64) { + OLA_WARN << "Failed to read SIUDI information: " + << (ret < 0 ? LibUsbAdaptor::ErrorCodeToString(ret) : "Short read"); + return false; + } + + // Unstall the endpoint. The original software seems to call this regularly. + ret = libusb_clear_halt(m_usb_handle, ENDPOINT); + if (ret != 0) { + OLA_WARN << "Failed to reset SIUDI endpoint: " + << (ret < 0 ? LibUsbAdaptor::ErrorCodeToString(ret) : "Unknown"); + return false; + } + usleep(10000); + + return true; +} + +bool SiudiThreadedSender::TransmitBuffer(libusb_device_handle *handle, + const DmxBuffer &buffer) { + for (unsigned int i = 0; i < buffer.Size(); i++) { + m_packet[i] = buffer.Get(i); + } + int transferred; + int r = m_adaptor->BulkTransfer( + handle, ENDPOINT, (unsigned char*) m_packet, + SIUDI_PACKET_SIZE, &transferred, TIMEOUT); + if (transferred != SIUDI_PACKET_SIZE) { + // not sure if this is fatal or not + OLA_WARN << "SIUDI driver failed to transfer all data"; + } + usleep(20000); + return r == 0; +} + +// SynchronousSiudi +// ----------------------------------------------------------------------------- + +SynchronousSiudi::SynchronousSiudi(LibUsbAdaptor *adaptor, + libusb_device *usb_device) + : Siudi(adaptor, usb_device) { +} + +bool SynchronousSiudi::Init() { + libusb_device_handle *usb_handle; + + bool ok = m_adaptor->OpenDeviceAndClaimInterface( + m_usb_device, 0, &usb_handle); + if (!ok) { + return false; + } + + std::auto_ptr sender( + new SiudiThreadedSender(m_adaptor, m_usb_device, usb_handle)); + if (!sender->Start()) { + return false; + } + m_sender.reset(sender.release()); + return true; +} + +bool SynchronousSiudi::SendDMX(const DmxBuffer &buffer) { + return m_sender.get() ? m_sender->SendDMX(buffer) : false; +} +} // namespace usbdmx +} // namespace plugin +} // namespace ola diff --git a/plugins/usbdmx/Siudi.h b/plugins/usbdmx/Siudi.h new file mode 100644 index 0000000000..22c52e19ba --- /dev/null +++ b/plugins/usbdmx/Siudi.h @@ -0,0 +1,76 @@ +/* + * 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. + * + * Siudi.h + * The synchronous SIUDI widgets. + * Copyright (C) 2023 Alexander Simon + */ + +#ifndef PLUGINS_USBDMX_SIUDI_H_ +#define PLUGINS_USBDMX_SIUDI_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 { + +class SiudiThreadedSender; + +/** + * @brief The interface for the Siudi Widgets + */ +class Siudi : public SimpleWidget { + public: + explicit Siudi(ola::usb::LibUsbAdaptor *adaptor, + libusb_device *usb_device) + : SimpleWidget(adaptor, usb_device) { + } +}; + + +/** + * @brief An SIUDI widget that uses synchronous libusb operations. + * + * Internally this spawns a new thread to avoid blocking SendDMX() calls. + */ +class SynchronousSiudi: public Siudi { + public: + /** + * @brief Create a new SynchronousSiudi. + * @param adaptor the LibUsbAdaptor to use. + * @param usb_device the libusb_device to use for the widget. + */ + SynchronousSiudi(ola::usb::LibUsbAdaptor *adaptor, + libusb_device *usb_device); + + bool Init(); + + bool SendDMX(const DmxBuffer &buffer); + + private: + std::auto_ptr m_sender; + + DISALLOW_COPY_AND_ASSIGN(SynchronousSiudi); +}; +} // namespace usbdmx +} // namespace plugin +} // namespace ola +#endif // PLUGINS_USBDMX_SIUDI_H_ diff --git a/plugins/usbdmx/SiudiFactory.cpp b/plugins/usbdmx/SiudiFactory.cpp new file mode 100644 index 0000000000..1d8a0fe1fd --- /dev/null +++ b/plugins/usbdmx/SiudiFactory.cpp @@ -0,0 +1,51 @@ +/* + * 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. + * + * SiudiFactory.cpp + * The WidgetFactory for SIUDI widgets. + * Copyright (C) 2023 Alexander Simon + */ + +#include "plugins/usbdmx/SiudiFactory.h" + +#include "ola/Logging.h" +#include "ola/base/Flags.h" + +DECLARE_bool(use_async_libusb); + +namespace ola { +namespace plugin { +namespace usbdmx { + +const uint16_t SiudiFactory::VENDOR_ID = 0x6244; +const uint16_t SiudiFactory::COLD_PRODUCT_ID = 0x0300; +const uint16_t SiudiFactory::HOT_PRODUCT_ID = 0x0301; + +bool SiudiFactory::DeviceAdded( + WidgetObserver *observer, + libusb_device *usb_device, + const struct libusb_device_descriptor &descriptor) { + if (descriptor.idVendor == VENDOR_ID && + descriptor.idProduct == HOT_PRODUCT_ID) { + OLA_INFO << "Found a new Nicoleaudie SIUDI-6 device"; + Siudi *widget = NULL; + widget = new SynchronousSiudi(m_adaptor, usb_device); + return AddWidget(observer, widget); + } + return false; +} +} // namespace usbdmx +} // namespace plugin +} // namespace ola diff --git a/plugins/usbdmx/SiudiFactory.h b/plugins/usbdmx/SiudiFactory.h new file mode 100644 index 0000000000..720e47bbeb --- /dev/null +++ b/plugins/usbdmx/SiudiFactory.h @@ -0,0 +1,62 @@ +/* + * 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. + * + * SiudiFactory.h + * The WidgetFactory for SIUDI widgets. + * Copyright (C) 2023 Alexander Simon + */ + +#ifndef PLUGINS_USBDMX_SIUDIFACTORY_H_ +#define PLUGINS_USBDMX_SIUDIFACTORY_H_ + +#include "libs/usb/LibUsbAdaptor.h" +#include "ola/base/Macro.h" +#include "plugins/usbdmx/Siudi.h" +#include "plugins/usbdmx/Siudi.h" +#include "plugins/usbdmx/WidgetFactory.h" + +namespace ola { +namespace plugin { +namespace usbdmx { + +/** + * @brief Creates SunLite widgets. + */ +class SiudiFactory : public BaseWidgetFactory { + public: + explicit SiudiFactory(ola::usb::LibUsbAdaptor *adaptor) + : BaseWidgetFactory("SiudiFactory"), + m_adaptor(adaptor) {} + + bool DeviceAdded( + WidgetObserver *observer, + libusb_device *usb_device, + const struct libusb_device_descriptor &descriptor); + + private: + ola::usb::LibUsbAdaptor* const m_adaptor; + + // The product ID for widgets that are missing their firmware. + static const uint16_t COLD_PRODUCT_ID; + // The product ID for widgets with the firmware. + static const uint16_t HOT_PRODUCT_ID; + static const uint16_t VENDOR_ID; + + DISALLOW_COPY_AND_ASSIGN(SiudiFactory); +}; +} // namespace usbdmx +} // namespace plugin +} // namespace ola +#endif // PLUGINS_USBDMX_SIUDIFACTORY_H_ diff --git a/plugins/usbdmx/SyncPluginImpl.cpp b/plugins/usbdmx/SyncPluginImpl.cpp index 99c5a19c3d..3b759c4446 100644 --- a/plugins/usbdmx/SyncPluginImpl.cpp +++ b/plugins/usbdmx/SyncPluginImpl.cpp @@ -50,6 +50,8 @@ #include "plugins/usbdmx/GenericDevice.h" #include "plugins/usbdmx/ShowJockeyDMXU1.h" #include "plugins/usbdmx/ShowJockeyDMXU1Factory.h" +#include "plugins/usbdmx/Siudi.h" +#include "plugins/usbdmx/SiudiFactory.h" #include "plugins/usbdmx/Sunlite.h" #include "plugins/usbdmx/SunliteFactory.h" #include "plugins/usbdmx/VellemanK8062.h" @@ -81,6 +83,7 @@ SyncPluginImpl::SyncPluginImpl(PluginAdaptor *plugin_adaptor, m_preferences)); m_widget_factories.push_back(new ScanlimeFadecandyFactory(&m_usb_adaptor)); m_widget_factories.push_back(new ShowJockeyDMXU1Factory(&m_usb_adaptor)); + m_widget_factories.push_back(new SiudiFactory(&m_usb_adaptor)); m_widget_factories.push_back(new SunliteFactory(&m_usb_adaptor)); m_widget_factories.push_back(new VellemanK8062Factory(&m_usb_adaptor)); } @@ -192,6 +195,12 @@ bool SyncPluginImpl::NewWidget(ShowJockeyDMXU1 *widget) { "showjockey-dmx-u1-" + widget->SerialNumber())); } +bool SyncPluginImpl::NewWidget(Siudi *widget) { + return StartAndRegisterDevice( + widget, + new GenericDevice(m_plugin, widget, "Sunlite SIUDI", "usbsiudi")); +} + bool SyncPluginImpl::NewWidget(Sunlite *widget) { return StartAndRegisterDevice( widget, diff --git a/plugins/usbdmx/SyncPluginImpl.h b/plugins/usbdmx/SyncPluginImpl.h index 92694b5464..df01e04c8a 100644 --- a/plugins/usbdmx/SyncPluginImpl.h +++ b/plugins/usbdmx/SyncPluginImpl.h @@ -79,6 +79,7 @@ class SyncPluginImpl: public PluginImplInterface, public WidgetObserver { bool NewWidget(ola::usb::JaRuleWidget *widget); bool NewWidget(class ScanlimeFadecandy *widget); bool NewWidget(class ShowJockeyDMXU1 *widget); + bool NewWidget(class Siudi *widget); bool NewWidget(class Sunlite *widget); bool NewWidget(class VellemanK8062 *widget); @@ -90,6 +91,7 @@ class SyncPluginImpl: public PluginImplInterface, public WidgetObserver { void WidgetRemoved(OLA_UNUSED ola::usb::JaRuleWidget *widget) {} void WidgetRemoved(OLA_UNUSED class ScanlimeFadecandy *widget) {} void WidgetRemoved(OLA_UNUSED class ShowJockeyDMXU1 *widget) {} + void WidgetRemoved(OLA_UNUSED class Siudi *widget) {} void WidgetRemoved(OLA_UNUSED class Sunlite *widget) {} void WidgetRemoved(OLA_UNUSED class VellemanK8062 *widget) {} diff --git a/plugins/usbdmx/SynchronizedWidgetObserver.h b/plugins/usbdmx/SynchronizedWidgetObserver.h index 227551f04c..0f3bf009dc 100644 --- a/plugins/usbdmx/SynchronizedWidgetObserver.h +++ b/plugins/usbdmx/SynchronizedWidgetObserver.h @@ -79,6 +79,10 @@ class SynchronizedWidgetObserver : public WidgetObserver { return DispatchNewWidget(widget); } + bool NewWidget(class Siudi *widget) { + return DispatchNewWidget(widget); + } + bool NewWidget(class Sunlite *widget) { return DispatchNewWidget(widget); } diff --git a/plugins/usbdmx/UsbDmxPlugin.h b/plugins/usbdmx/UsbDmxPlugin.h index 8e7e403460..8d375993d7 100644 --- a/plugins/usbdmx/UsbDmxPlugin.h +++ b/plugins/usbdmx/UsbDmxPlugin.h @@ -43,7 +43,7 @@ namespace usbdmx { * - Eurolite DMX USB Pro. * - Eurolite DMX USB Pro MK2. * - Scanlime's Fadecandy. - * - Sunlite. + * - Sunlite USBDMX2 and SIUDI. * - Velleman K8062. */ class UsbDmxPlugin: public ola::Plugin { diff --git a/plugins/usbdmx/WidgetFactory.h b/plugins/usbdmx/WidgetFactory.h index a7a00c5b70..065cc478c2 100644 --- a/plugins/usbdmx/WidgetFactory.h +++ b/plugins/usbdmx/WidgetFactory.h @@ -122,6 +122,15 @@ class WidgetObserver { */ virtual bool NewWidget(class ScanlimeFadecandy *widget) = 0; + /** + * @brief Called when a new SIUDI device 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 Siudi *widget) = 0; + /** * @brief Called when a new Sunlite is added. * @param widget the new Widget, ownership is not transferred but the object From 20c6dda78b2f2ab5aabc43e738eee85e3f95cc6b Mon Sep 17 00:00:00 2001 From: Alexander Simon Date: Fri, 28 Jul 2023 15:37:39 +0200 Subject: [PATCH 02/10] Split comment in UsbDmxPlugin.h to one driver per line Co-authored-by: Peter Newman --- plugins/usbdmx/UsbDmxPlugin.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/plugins/usbdmx/UsbDmxPlugin.h b/plugins/usbdmx/UsbDmxPlugin.h index 8d375993d7..db6e419733 100644 --- a/plugins/usbdmx/UsbDmxPlugin.h +++ b/plugins/usbdmx/UsbDmxPlugin.h @@ -43,7 +43,8 @@ namespace usbdmx { * - Eurolite DMX USB Pro. * - Eurolite DMX USB Pro MK2. * - Scanlime's Fadecandy. - * - Sunlite USBDMX2 and SIUDI. + * - Sunlite SIUDI. + * - Sunlite USBDMX2. * - Velleman K8062. */ class UsbDmxPlugin: public ola::Plugin { From 75d5f5670e5e2db74cdd1aaf9c363b46a0920054 Mon Sep 17 00:00:00 2001 From: Alexander Simon Date: Fri, 28 Jul 2023 15:33:42 +0200 Subject: [PATCH 03/10] SIUDI: Add USB IDs for SIUDI-6 variants It seems that all SIUDI-6 behave the same, according to the docs. Although not tested, allow USB IDs for all SIUDI-6 variants. Print a warning if a SIUDI-6 device is found in a cold state. At least for the SIUDI-6C, there is no firmware download necessary. --- plugins/usbdmx/SiudiFactory.cpp | 29 +++++++++++++++++++++++------ plugins/usbdmx/SiudiFactory.h | 8 +++++--- 2 files changed, 28 insertions(+), 9 deletions(-) diff --git a/plugins/usbdmx/SiudiFactory.cpp b/plugins/usbdmx/SiudiFactory.cpp index 1d8a0fe1fd..9f33de9f99 100644 --- a/plugins/usbdmx/SiudiFactory.cpp +++ b/plugins/usbdmx/SiudiFactory.cpp @@ -29,17 +29,34 @@ namespace ola { namespace plugin { namespace usbdmx { -const uint16_t SiudiFactory::VENDOR_ID = 0x6244; -const uint16_t SiudiFactory::COLD_PRODUCT_ID = 0x0300; -const uint16_t SiudiFactory::HOT_PRODUCT_ID = 0x0301; +const uint16_t SiudiFactory::NICOLAUDIE_ID = 0x6244; +const uint16_t SiudiFactory::SIUDI6_COLD_ID = 0x0300; +const uint16_t SiudiFactory::SIUDI6C_HOT_ID = 0x301; +const uint16_t SiudiFactory::SIUDI6A_HOT_ID = 0x302; +const uint16_t SiudiFactory::SIUDI6D_HOT_ID = 0x303; bool SiudiFactory::DeviceAdded( WidgetObserver *observer, libusb_device *usb_device, const struct libusb_device_descriptor &descriptor) { - if (descriptor.idVendor == VENDOR_ID && - descriptor.idProduct == HOT_PRODUCT_ID) { - OLA_INFO << "Found a new Nicoleaudie SIUDI-6 device"; + if (descriptor.idVendor != NICOLAUDIE_ID) { + return false; + } + if (descriptor.idProduct == SIUDI6_COLD_ID) { + OLA_WARN << "Found a Nicoleaudie SIUDI-6 device in cold state. " + "Firmware download is currently not supported."; + return false; + } + if (descriptor.idProduct == SIUDI6C_HOT_ID || + descriptor.idProduct == SIUDI6A_HOT_ID || + descriptor.idProduct == SIUDI6D_HOT_ID) { + if (descriptor.idProduct == SIUDI6C_HOT_ID) { + OLA_INFO << "Found a new Nicoleaudie SIUDI-6C device"; + } else if (descriptor.idProduct == SIUDI6A_HOT_ID) { + OLA_INFO << "Found a new Nicoleaudie SIUDI-6A device"; + } else if (descriptor.idProduct == SIUDI6A_HOT_ID) { + OLA_INFO << "Found a new Nicoleaudie SIUDI-6D device"; + } Siudi *widget = NULL; widget = new SynchronousSiudi(m_adaptor, usb_device); return AddWidget(observer, widget); diff --git a/plugins/usbdmx/SiudiFactory.h b/plugins/usbdmx/SiudiFactory.h index 720e47bbeb..5b8c6c1025 100644 --- a/plugins/usbdmx/SiudiFactory.h +++ b/plugins/usbdmx/SiudiFactory.h @@ -48,11 +48,13 @@ class SiudiFactory : public BaseWidgetFactory { private: ola::usb::LibUsbAdaptor* const m_adaptor; + static const uint16_t NICOLAUDIE_ID; // The product ID for widgets that are missing their firmware. - static const uint16_t COLD_PRODUCT_ID; + static const uint16_t SIUDI6_COLD_ID; // The product ID for widgets with the firmware. - static const uint16_t HOT_PRODUCT_ID; - static const uint16_t VENDOR_ID; + static const uint16_t SIUDI6C_HOT_ID; + static const uint16_t SIUDI6A_HOT_ID; + static const uint16_t SIUDI6D_HOT_ID; DISALLOW_COPY_AND_ASSIGN(SiudiFactory); }; From 96e10f70e7d02ce70151973234a2cd4a310aeebf Mon Sep 17 00:00:00 2001 From: Alexander Simon Date: Fri, 28 Jul 2023 16:49:10 +0200 Subject: [PATCH 04/10] SIUDI: Rework building the transmit buffer --- plugins/usbdmx/Siudi.cpp | 27 +++++++++++++++++---------- 1 file changed, 17 insertions(+), 10 deletions(-) diff --git a/plugins/usbdmx/Siudi.cpp b/plugins/usbdmx/Siudi.cpp index 996f3e0dbd..3ce388b5df 100644 --- a/plugins/usbdmx/Siudi.cpp +++ b/plugins/usbdmx/Siudi.cpp @@ -39,7 +39,6 @@ namespace { static const uint8_t ENDPOINT = 2; static const unsigned int TIMEOUT = 50; // 50ms is ok -enum {SIUDI_PACKET_SIZE = 512}; } // namespace @@ -59,7 +58,6 @@ class SiudiThreadedSender: public ThreadedUsbSender { private: LibUsbAdaptor* const m_adaptor; - uint8_t m_packet[SIUDI_PACKET_SIZE]; libusb_device_handle* const m_usb_handle; bool TransmitBuffer(libusb_device_handle *handle, @@ -72,7 +70,6 @@ SiudiThreadedSender::SiudiThreadedSender( libusb_device_handle *usb_handle) : ThreadedUsbSender(usb_device, usb_handle), m_adaptor(adaptor), m_usb_handle(usb_handle) { - memset(m_packet, 0x00, SIUDI_PACKET_SIZE); } bool SiudiThreadedSender::Start() { @@ -106,14 +103,24 @@ bool SiudiThreadedSender::Start() { bool SiudiThreadedSender::TransmitBuffer(libusb_device_handle *handle, const DmxBuffer &buffer) { - for (unsigned int i = 0; i < buffer.Size(); i++) { - m_packet[i] = buffer.Get(i); + int transferred, r; + unsigned int buf_size = buffer.Size(); + if (buf_size == ola::DMX_UNIVERSE_SIZE) { + // As we are sending, we can cast the const buffer to a writeable pointer. + r = m_adaptor->BulkTransfer( + handle, ENDPOINT, (unsigned char*)buffer.GetRaw(), + ola::DMX_UNIVERSE_SIZE, &transferred, TIMEOUT); + } else { + unsigned char buf[buf_size]; + unsigned int buf_get_size = ola::DMX_UNIVERSE_SIZE; + buffer.GetRange(0, buf, &buf_get_size); + if (buf_get_size < ola::DMX_UNIVERSE_SIZE) { + memset(&buf[buf_get_size], 0x00, ola::DMX_UNIVERSE_SIZE - buf_get_size); + } + r = m_adaptor->BulkTransfer( + handle, ENDPOINT, buf, ola::DMX_UNIVERSE_SIZE, &transferred, TIMEOUT); } - int transferred; - int r = m_adaptor->BulkTransfer( - handle, ENDPOINT, (unsigned char*) m_packet, - SIUDI_PACKET_SIZE, &transferred, TIMEOUT); - if (transferred != SIUDI_PACKET_SIZE) { + if (transferred != ola::DMX_UNIVERSE_SIZE) { // not sure if this is fatal or not OLA_WARN << "SIUDI driver failed to transfer all data"; } From 8e48a0a3971ef78e1a04bdbb8fc70ce934a76840 Mon Sep 17 00:00:00 2001 From: Alexander Simon Date: Fri, 28 Jul 2023 16:54:57 +0200 Subject: [PATCH 05/10] SIUDI: Rework USB timing --- plugins/usbdmx/Siudi.cpp | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/plugins/usbdmx/Siudi.cpp b/plugins/usbdmx/Siudi.cpp index 3ce388b5df..2d759639a5 100644 --- a/plugins/usbdmx/Siudi.cpp +++ b/plugins/usbdmx/Siudi.cpp @@ -38,7 +38,12 @@ using ola::usb::LibUsbAdaptor; namespace { static const uint8_t ENDPOINT = 2; -static const unsigned int TIMEOUT = 50; // 50ms is ok +// SIUDI-6 blocks USB transfers during an ongoing DMX TX. +// One package needs about 32 ms to be sent. +// Wait 30 ms between two USB bulk transfers and expect 2 ms USB response delay. +static const unsigned int BULK_TIMEOUT = 10; +static const unsigned int BULK_DELAY = (30 * 1000); +static const unsigned int CONTROL_TIMEOUT = 500; } // namespace @@ -82,7 +87,7 @@ bool SiudiThreadedSender::Start() { uint8_t buf[64]; int ret = libusb_control_transfer(m_usb_handle, LIBUSB_REQUEST_TYPE_VENDOR | LIBUSB_ENDPOINT_IN, - 0x3f, 0x00c4, 1, buf, 64, 500); + 0x3f, 0x00c4, 1, buf, 64, CONTROL_TIMEOUT); if (ret != 64) { OLA_WARN << "Failed to read SIUDI information: " << (ret < 0 ? LibUsbAdaptor::ErrorCodeToString(ret) : "Short read"); @@ -96,7 +101,7 @@ bool SiudiThreadedSender::Start() { << (ret < 0 ? LibUsbAdaptor::ErrorCodeToString(ret) : "Unknown"); return false; } - usleep(10000); + usleep(BULK_DELAY); // Might receive errors if writing too early. return true; } @@ -109,7 +114,7 @@ bool SiudiThreadedSender::TransmitBuffer(libusb_device_handle *handle, // As we are sending, we can cast the const buffer to a writeable pointer. r = m_adaptor->BulkTransfer( handle, ENDPOINT, (unsigned char*)buffer.GetRaw(), - ola::DMX_UNIVERSE_SIZE, &transferred, TIMEOUT); + ola::DMX_UNIVERSE_SIZE, &transferred, BULK_TIMEOUT); } else { unsigned char buf[buf_size]; unsigned int buf_get_size = ola::DMX_UNIVERSE_SIZE; @@ -118,13 +123,13 @@ bool SiudiThreadedSender::TransmitBuffer(libusb_device_handle *handle, memset(&buf[buf_get_size], 0x00, ola::DMX_UNIVERSE_SIZE - buf_get_size); } r = m_adaptor->BulkTransfer( - handle, ENDPOINT, buf, ola::DMX_UNIVERSE_SIZE, &transferred, TIMEOUT); + handle, ENDPOINT, buf, ola::DMX_UNIVERSE_SIZE, &transferred, BULK_TIMEOUT); } if (transferred != ola::DMX_UNIVERSE_SIZE) { // not sure if this is fatal or not OLA_WARN << "SIUDI driver failed to transfer all data"; } - usleep(20000); + usleep(BULK_DELAY); return r == 0; } From 768def2d844b9cd0739ed302310aa48e207d9a17 Mon Sep 17 00:00:00 2001 From: Alexander Simon Date: Fri, 28 Jul 2023 17:02:12 +0200 Subject: [PATCH 06/10] SIUDI: Move fixed values to constants --- plugins/usbdmx/Siudi.cpp | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/plugins/usbdmx/Siudi.cpp b/plugins/usbdmx/Siudi.cpp index 2d759639a5..5399384ee4 100644 --- a/plugins/usbdmx/Siudi.cpp +++ b/plugins/usbdmx/Siudi.cpp @@ -44,6 +44,8 @@ static const uint8_t ENDPOINT = 2; static const unsigned int BULK_TIMEOUT = 10; static const unsigned int BULK_DELAY = (30 * 1000); static const unsigned int CONTROL_TIMEOUT = 500; +static const unsigned int DEVINFO_REQUEST = 0x3f; +static const unsigned int DEVINFO_SIZE = 64; } // namespace @@ -84,11 +86,11 @@ bool SiudiThreadedSender::Start() { // Read device info. This call takes about 270 ms. // Discard the buffer as the format is currently unknown. - uint8_t buf[64]; + uint8_t buf[DEVINFO_SIZE]; int ret = libusb_control_transfer(m_usb_handle, - LIBUSB_REQUEST_TYPE_VENDOR | LIBUSB_ENDPOINT_IN, - 0x3f, 0x00c4, 1, buf, 64, CONTROL_TIMEOUT); - if (ret != 64) { + LIBUSB_REQUEST_TYPE_VENDOR | LIBUSB_ENDPOINT_IN, + DEVINFO_REQUEST, 0x0000, 1, buf, DEVINFO_SIZE, CONTROL_TIMEOUT); + if (ret != DEVINFO_SIZE) { OLA_WARN << "Failed to read SIUDI information: " << (ret < 0 ? LibUsbAdaptor::ErrorCodeToString(ret) : "Short read"); return false; From abcddc15de2aca4c0915825edea2f7bd61e3c9d2 Mon Sep 17 00:00:00 2001 From: Alexander Simon Date: Fri, 28 Jul 2023 18:00:02 +0200 Subject: [PATCH 07/10] SIUDI: Updated authors, udev and Debian metainfo --- AUTHORS | 1 + debian/ola.udev | 3 +++ debian/org.openlighting.ola.ola.metainfo.xml | 1 + 3 files changed, 5 insertions(+) diff --git a/AUTHORS b/AUTHORS index a3966eadc0..bc9a08bcd9 100644 --- a/AUTHORS +++ b/AUTHORS @@ -34,3 +34,4 @@ Contributors: Stefan S, improved timing with monotonic clock Tobi Schäfer, for the MacPort files Tor Håkon Gjerde, added AVLdiy D512 support + Alexander Simon, added SIUDI support diff --git a/debian/ola.udev b/debian/ola.udev index 40532ddfae..b27ded1ec1 100644 --- a/debian/ola.udev +++ b/debian/ola.udev @@ -43,5 +43,8 @@ SUBSYSTEM=="usb|usb_device", ACTION=="add", ATTRS{idVendor}=="1d50", ATTRS{idPro SUBSYSTEM=="usb|usb_device", ACTION=="add", ATTRS{idVendor}=="1209", ATTRS{idProduct}=="aced", GROUP="plugdev", MODE="660", TAG+="uaccess" SUBSYSTEM=="usb|usb_device", ACTION=="add", ATTRS{idVendor}=="1209", ATTRS{idProduct}=="acee", GROUP="plugdev", MODE="660", TAG+="uaccess" +# udev rules for Nicoleaudie Intelligent USB DMX Interface (SIUDI) +SUBSYSTEM=="usb|usb_device", ACTION=="add", ATTRS{idVendor}=="6244", ATTRS{idProduct}=="030?", GROUP="plugdev", MODE="660", TAG+="uaccess" + # udev rules for SPI SUBSYSTEM=="spidev", MODE="0666" diff --git a/debian/org.openlighting.ola.ola.metainfo.xml b/debian/org.openlighting.ola.ola.metainfo.xml index 8a663fc06f..5436067a54 100644 --- a/debian/org.openlighting.ola.ola.metainfo.xml +++ b/debian/org.openlighting.ola.ola.metainfo.xml @@ -35,6 +35,7 @@ usb:v1D50p607Ad* usb:v109pACEDd* usb:v109pACEEd* + usb:v6244p030*d* lkmodule:spidev lkmodule:dmxi2c lkmodule:okdmx From fd056b5b64dc0be25e685c0a7e8f319062648ae4 Mon Sep 17 00:00:00 2001 From: Alexander Simon Date: Fri, 28 Jul 2023 18:18:41 +0200 Subject: [PATCH 08/10] SIUDI: OLA_DEBUG out device info upon start --- plugins/usbdmx/Siudi.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/plugins/usbdmx/Siudi.cpp b/plugins/usbdmx/Siudi.cpp index 5399384ee4..4458c79c43 100644 --- a/plugins/usbdmx/Siudi.cpp +++ b/plugins/usbdmx/Siudi.cpp @@ -26,6 +26,7 @@ #include "libs/usb/LibUsbAdaptor.h" #include "ola/Constants.h" #include "ola/Logging.h" +#include "ola/strings/Format.h" #include "plugins/usbdmx/AsyncUsbSender.h" #include "plugins/usbdmx/ThreadedUsbSender.h" @@ -95,6 +96,11 @@ bool SiudiThreadedSender::Start() { << (ret < 0 ? LibUsbAdaptor::ErrorCodeToString(ret) : "Short read"); return false; } + if (ola::OLA_LOG_DEBUG <= ola::LogLevel()) { + ola::LogLine(__FILE__, __LINE__, ola::OLA_LOG_DEBUG).stream() << "SIUDI device info:"; + ola::strings::FormatData(&std::cout, buf, DEVINFO_SIZE, 4); + ola::LogLine(__FILE__, __LINE__, ola::OLA_LOG_DEBUG).stream() << "If you know how to interprete this, please let us know."; + } // Unstall the endpoint. The original software seems to call this regularly. ret = libusb_clear_halt(m_usb_handle, ENDPOINT); From 42b23c745c3ef089f3543c4a098e51c9cd791af1 Mon Sep 17 00:00:00 2001 From: Alexander Simon Date: Fri, 28 Jul 2023 18:43:12 +0200 Subject: [PATCH 09/10] SIUDI: Fix buffer size TransmitBuffer --- plugins/usbdmx/Siudi.cpp | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/plugins/usbdmx/Siudi.cpp b/plugins/usbdmx/Siudi.cpp index 4458c79c43..33429cf758 100644 --- a/plugins/usbdmx/Siudi.cpp +++ b/plugins/usbdmx/Siudi.cpp @@ -117,14 +117,13 @@ bool SiudiThreadedSender::Start() { bool SiudiThreadedSender::TransmitBuffer(libusb_device_handle *handle, const DmxBuffer &buffer) { int transferred, r; - unsigned int buf_size = buffer.Size(); - if (buf_size == ola::DMX_UNIVERSE_SIZE) { + if (buffer.Size() == ola::DMX_UNIVERSE_SIZE) { // As we are sending, we can cast the const buffer to a writeable pointer. r = m_adaptor->BulkTransfer( handle, ENDPOINT, (unsigned char*)buffer.GetRaw(), ola::DMX_UNIVERSE_SIZE, &transferred, BULK_TIMEOUT); } else { - unsigned char buf[buf_size]; + unsigned char buf[ola::DMX_UNIVERSE_SIZE]; unsigned int buf_get_size = ola::DMX_UNIVERSE_SIZE; buffer.GetRange(0, buf, &buf_get_size); if (buf_get_size < ola::DMX_UNIVERSE_SIZE) { From 60bc8febeed6bda5189e8990289a0b6c57fb8a88 Mon Sep 17 00:00:00 2001 From: Alexander Simon Date: Sun, 30 Jul 2023 15:12:28 +0200 Subject: [PATCH 10/10] SIUDI: Fix a typo in SiudiFactory.cpp --- plugins/usbdmx/SiudiFactory.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/usbdmx/SiudiFactory.cpp b/plugins/usbdmx/SiudiFactory.cpp index 9f33de9f99..8f4e4e5e48 100644 --- a/plugins/usbdmx/SiudiFactory.cpp +++ b/plugins/usbdmx/SiudiFactory.cpp @@ -54,7 +54,7 @@ bool SiudiFactory::DeviceAdded( OLA_INFO << "Found a new Nicoleaudie SIUDI-6C device"; } else if (descriptor.idProduct == SIUDI6A_HOT_ID) { OLA_INFO << "Found a new Nicoleaudie SIUDI-6A device"; - } else if (descriptor.idProduct == SIUDI6A_HOT_ID) { + } else if (descriptor.idProduct == SIUDI6D_HOT_ID) { OLA_INFO << "Found a new Nicoleaudie SIUDI-6D device"; } Siudi *widget = NULL;