From fe95f419217c532ab7acb5cc97b5d521460f0673 Mon Sep 17 00:00:00 2001 From: Jannis Achstetter Date: Sat, 30 Jan 2021 00:10:30 +0100 Subject: [PATCH 01/11] UsbDmx plugin: Add the USB ids of "Frank Sievertsen FX5" as a clone of the Nodle U1 --- plugins/usbdmx/DMXCProjectsNodleU1Factory.cpp | 15 +++++++++++++-- plugins/usbdmx/DMXCProjectsNodleU1Factory.h | 2 ++ plugins/usbdmx/UsbDmxPlugin.cpp | 3 ++- 3 files changed, 17 insertions(+), 3 deletions(-) diff --git a/plugins/usbdmx/DMXCProjectsNodleU1Factory.cpp b/plugins/usbdmx/DMXCProjectsNodleU1Factory.cpp index 88bfdf7d11..094b228dd3 100644 --- a/plugins/usbdmx/DMXCProjectsNodleU1Factory.cpp +++ b/plugins/usbdmx/DMXCProjectsNodleU1Factory.cpp @@ -33,15 +33,26 @@ namespace usbdmx { const uint16_t DMXCProjectsNodleU1Factory::VENDOR_ID = 0x16d0; const uint16_t DMXCProjectsNodleU1Factory::PRODUCT_ID = 0x0830; +// Those IDs are not officially registered or "free for everyone" +// but there seems to be one clone using them. +// See also: https://github.com/mcallegari/qlcplus/blob/3b69452d0679333c6e60ff0928c20f2107ccc81f/plugins/hid/hiddmxdevice.h#L34 +const uint16_t DMXCProjectsNodleU1Factory::VENDOR_ID_FX5 = 0x16c0; +const uint16_t DMXCProjectsNodleU1Factory::PRODUCT_ID_FX5 = 0x088b; + bool DMXCProjectsNodleU1Factory::DeviceAdded( WidgetObserver *observer, libusb_device *usb_device, const struct libusb_device_descriptor &descriptor) { - if (descriptor.idVendor != VENDOR_ID || descriptor.idProduct != PRODUCT_ID) { + if ( + ((descriptor.idVendor != VENDOR_ID) && + (descriptor.idVendor != VENDOR_ID_FX5)) || + ((descriptor.idProduct != PRODUCT_ID) && + (descriptor.idProduct != PRODUCT_ID_FX5)) + ) { return false; } - OLA_INFO << "Found a new Nodle U1 device"; + OLA_INFO << "Found a new Nodle U1 or clone device"; ola::usb::LibUsbAdaptor::DeviceInformation info; if (!m_adaptor->GetDeviceInfo(usb_device, descriptor, &info)) { return false; diff --git a/plugins/usbdmx/DMXCProjectsNodleU1Factory.h b/plugins/usbdmx/DMXCProjectsNodleU1Factory.h index 7df0b280a4..1b09084a42 100644 --- a/plugins/usbdmx/DMXCProjectsNodleU1Factory.h +++ b/plugins/usbdmx/DMXCProjectsNodleU1Factory.h @@ -59,6 +59,8 @@ class DMXCProjectsNodleU1Factory : static const uint16_t VENDOR_ID; static const uint16_t PRODUCT_ID; + static const uint16_t VENDOR_ID_FX5; + static const uint16_t PRODUCT_ID_FX5; DISALLOW_COPY_AND_ASSIGN(DMXCProjectsNodleU1Factory); }; diff --git a/plugins/usbdmx/UsbDmxPlugin.cpp b/plugins/usbdmx/UsbDmxPlugin.cpp index 012c372c45..d02b43f1b7 100644 --- a/plugins/usbdmx/UsbDmxPlugin.cpp +++ b/plugins/usbdmx/UsbDmxPlugin.cpp @@ -92,7 +92,8 @@ string UsbDmxPlugin::Description() const { "----------------------------\n" "\n" "This plugin supports various USB DMX devices including the \n" -"Anyma uDMX, DMXControl Projects e.V. Nodle U1, Eurolite, Fadecandy, " +"Anyma uDMX, DMXControl Projects e.V. Nodle U1 (and clones like Digital \n" +"Enlightenment USB DMX or Frank Sievertsen FX5), Eurolite, Fadecandy, \n" "Sunlite USBDMX2 and Velleman K8062.\n" "\n" "--- Config file : ola-usbdmx.conf ---\n" From 2f9f7b7918c9b25e8b49f9805bee8e532401e3a9 Mon Sep 17 00:00:00 2001 From: Jannis Achstetter Date: Sat, 30 Jan 2021 00:49:54 +0100 Subject: [PATCH 02/11] Nodle U1: Don't set the USB device's config if it's a RP2040-based interface --- plugins/usbdmx/DMXCProjectsNodleU1.cpp | 32 ++++++++++++++++++++------ 1 file changed, 25 insertions(+), 7 deletions(-) diff --git a/plugins/usbdmx/DMXCProjectsNodleU1.cpp b/plugins/usbdmx/DMXCProjectsNodleU1.cpp index 15408668bb..7a82a36721 100644 --- a/plugins/usbdmx/DMXCProjectsNodleU1.cpp +++ b/plugins/usbdmx/DMXCProjectsNodleU1.cpp @@ -104,13 +104,31 @@ libusb_device_handle *OpenDMXCProjectsNodleU1Widget( return NULL; } - // this device only has one configuration - ret_code = adaptor->SetConfiguration(usb_handle, CONFIGURATION); - if (ret_code) { - OLA_WARN << "Nodle set config failed, with libusb error code " - << adaptor->ErrorCodeToString(ret_code); - adaptor->Close(usb_handle); - return NULL; + // If we are connected to a RP2040-based device, we cannot + // "Set the configuration", because the additional CDC ACM interface might + // be running that would block changing the USB device's config + bool rp2040 = false; + + libusb_device_descriptor descriptor; + ola::usb::LibUsbAdaptor::DeviceInformation info; + if (!(adaptor->GetDeviceDescriptor(usb_device, &descriptor)) && + (adaptor->GetDeviceInfo(usb_device, descriptor, &info)) && + (info.serial.find("RP2040_", 0) != std::string::npos)) + { + OLA_INFO << "Found a RP2040-based device, " + << "will skip (re) setting the USB configuration"; + rp2040 = true; + } + + if (!rp2040) { + // this device only has one configuration + ret_code = adaptor->SetConfiguration(usb_handle, CONFIGURATION); + if (ret_code) { + OLA_WARN << "Nodle set config failed, with libusb error code " + << adaptor->ErrorCodeToString(ret_code); + adaptor->Close(usb_handle); + return NULL; + } } if (adaptor->ClaimInterface(usb_handle, INTERFACE)) { From 039899c981016456c8b2ac24d9c1936c51de8964 Mon Sep 17 00:00:00 2001 From: Jannis Achstetter Date: Sat, 30 Jan 2021 22:40:26 +0100 Subject: [PATCH 03/11] First steps to make the Nodle-Widget multichannel --- plugins/usbdmx/DMXCProjectsNodleU1.h | 1 + plugins/usbdmx/GenericOutputPort.cpp | 1 + 2 files changed, 2 insertions(+) diff --git a/plugins/usbdmx/DMXCProjectsNodleU1.h b/plugins/usbdmx/DMXCProjectsNodleU1.h index dac59391f2..1146739988 100644 --- a/plugins/usbdmx/DMXCProjectsNodleU1.h +++ b/plugins/usbdmx/DMXCProjectsNodleU1.h @@ -81,6 +81,7 @@ class DMXCProjectsNodleU1: public SimpleWidget { private: std::string m_serial; + bool m_rp2040AdvancedMode; protected: unsigned int m_mode; diff --git a/plugins/usbdmx/GenericOutputPort.cpp b/plugins/usbdmx/GenericOutputPort.cpp index fdff9ba6ee..6890527c0b 100644 --- a/plugins/usbdmx/GenericOutputPort.cpp +++ b/plugins/usbdmx/GenericOutputPort.cpp @@ -33,6 +33,7 @@ GenericOutputPort::GenericOutputPort(Device *parent, WidgetInterface *widget) : BasicOutputPort(parent, id), m_widget(widget) { + OLA_DEBUG << "NEW GENERICOUTPUTPORT ID: " << id; } bool GenericOutputPort::WriteDMX(const DmxBuffer &buffer, From 8f7fef64ff6c11c8330547544d1455f86d07df58 Mon Sep 17 00:00:00 2001 From: Jannis Achstetter Date: Sun, 31 Jan 2021 22:03:19 +0100 Subject: [PATCH 04/11] Add some more debugging output --- plugins/usbdmx/DMXCProjectsNodleU1.cpp | 23 +++++++++++++--- plugins/usbdmx/DMXCProjectsNodleU1.h | 14 +++++++--- plugins/usbdmx/DMXCProjectsNodleU1Device.cpp | 2 ++ plugins/usbdmx/DMXCProjectsNodleU1Device.h | 18 +++++++++++++ plugins/usbdmx/DMXCProjectsNodleU1Factory.cpp | 26 ++++++++++++++++--- 5 files changed, 73 insertions(+), 10 deletions(-) diff --git a/plugins/usbdmx/DMXCProjectsNodleU1.cpp b/plugins/usbdmx/DMXCProjectsNodleU1.cpp index 7a82a36721..6eff4360e2 100644 --- a/plugins/usbdmx/DMXCProjectsNodleU1.cpp +++ b/plugins/usbdmx/DMXCProjectsNodleU1.cpp @@ -281,12 +281,19 @@ SynchronousDMXCProjectsNodleU1::SynchronousDMXCProjectsNodleU1( libusb_device *usb_device, PluginAdaptor *plugin_adaptor, const string &serial, - unsigned int mode) - : DMXCProjectsNodleU1(adaptor, usb_device, plugin_adaptor, serial, mode), + unsigned int mode, + unsigned int ins, + unsigned int outs) + : DMXCProjectsNodleU1(adaptor, usb_device, plugin_adaptor, serial, mode, + ins, outs), m_usb_device(usb_device) { + OLA_DEBUG << "SynchronousDMXCProjectsNodleU1 CTOR"; } bool SynchronousDMXCProjectsNodleU1::Init() { + + OLA_DEBUG << "SynchronousDMXCProjectsNodleU1 INIT"; + libusb_device_handle *usb_handle = OpenDMXCProjectsNodleU1Widget( m_adaptor, m_usb_device); @@ -506,8 +513,13 @@ AsynchronousDMXCProjectsNodleU1::AsynchronousDMXCProjectsNodleU1( libusb_device *usb_device, PluginAdaptor *plugin_adaptor, const string &serial, - unsigned int mode) - : DMXCProjectsNodleU1(adaptor, usb_device, plugin_adaptor, serial, mode) { + unsigned int mode, + unsigned int ins, + unsigned int outs) + : DMXCProjectsNodleU1(adaptor, usb_device, plugin_adaptor, serial, mode, + ins, outs) { + OLA_DEBUG << "AsynchronousDMXCProjectsNodleU1 CTOR"; + if (mode & OUTPUT_ENABLE_MASK) { // output port active m_sender.reset(new DMXCProjectsNodleU1AsyncUsbSender(m_adaptor, usb_device, mode)); @@ -523,6 +535,9 @@ AsynchronousDMXCProjectsNodleU1::AsynchronousDMXCProjectsNodleU1( bool AsynchronousDMXCProjectsNodleU1::Init() { bool ok = true; + + OLA_DEBUG << "AsynchronousDMXCProjectsNodleU1 INIT"; + if (m_sender.get()) { ok &= m_sender->Init(); } diff --git a/plugins/usbdmx/DMXCProjectsNodleU1.h b/plugins/usbdmx/DMXCProjectsNodleU1.h index 1146739988..3c82f1c7aa 100644 --- a/plugins/usbdmx/DMXCProjectsNodleU1.h +++ b/plugins/usbdmx/DMXCProjectsNodleU1.h @@ -46,7 +46,9 @@ class DMXCProjectsNodleU1: public SimpleWidget { libusb_device *usb_device, PluginAdaptor *plugin_adaptor, const std::string &serial, - unsigned int mode) + unsigned int mode, + unsigned int ins, + unsigned int out) : SimpleWidget(adaptor, usb_device), m_serial(serial), m_mode(mode), @@ -81,7 +83,7 @@ class DMXCProjectsNodleU1: public SimpleWidget { private: std::string m_serial; - bool m_rp2040AdvancedMode; + std::string m_product; protected: unsigned int m_mode; @@ -100,6 +102,7 @@ class SynchronousDMXCProjectsNodleU1: public DMXCProjectsNodleU1 { * @param adaptor the LibUsbAdaptor to use. * @param usb_device the libusb_device to use for the widget. * @param plugin_adaptor the PluginAdaptor used to execute callbacks + * @param product the product name of this widget * @param serial the serial number of this widget * @param mode the send/receive mode to be used by the widget. */ @@ -107,7 +110,9 @@ class SynchronousDMXCProjectsNodleU1: public DMXCProjectsNodleU1 { libusb_device *usb_device, PluginAdaptor *plugin_adaptor, const std::string &serial, - unsigned int mode); + unsigned int mode, + unsigned int ins, + unsigned int outs); bool Init(); @@ -134,6 +139,7 @@ class AsynchronousDMXCProjectsNodleU1 : public DMXCProjectsNodleU1 { * @param adaptor the LibUsbAdaptor to use. * @param usb_device the libusb_device to use for the widget. * @param plugin_adaptor the PluginAdaptor used to execute callbacks + * @param product the product name of this widget * @param serial the serial number of this widget * @param mode the send/receive mode to be used by the widget. */ @@ -141,6 +147,8 @@ class AsynchronousDMXCProjectsNodleU1 : public DMXCProjectsNodleU1 { libusb_device *usb_device, PluginAdaptor *plugin_adaptor, const std::string &serial, + unsigned int ins, + unsigned int outs, unsigned int mode); bool Init(); diff --git a/plugins/usbdmx/DMXCProjectsNodleU1Device.cpp b/plugins/usbdmx/DMXCProjectsNodleU1Device.cpp index 531dd94653..e30598ee82 100644 --- a/plugins/usbdmx/DMXCProjectsNodleU1Device.cpp +++ b/plugins/usbdmx/DMXCProjectsNodleU1Device.cpp @@ -40,6 +40,8 @@ DMXCProjectsNodleU1Device::DMXCProjectsNodleU1Device( m_in_port() { unsigned int mode = widget->Mode(); + OLA_DEBUG << "Device CTOR: checking serial number: " << widget->SerialNumber(); + if (mode & DMXCProjectsNodleU1::OUTPUT_ENABLE_MASK) { // output port active m_out_port.reset(new GenericOutputPort(this, 0, widget)); } diff --git a/plugins/usbdmx/DMXCProjectsNodleU1Device.h b/plugins/usbdmx/DMXCProjectsNodleU1Device.h index 842644487c..8db58cb331 100644 --- a/plugins/usbdmx/DMXCProjectsNodleU1Device.h +++ b/plugins/usbdmx/DMXCProjectsNodleU1Device.h @@ -24,6 +24,7 @@ #include #include #include "ola/base/Macro.h" +#include "ola/Logging.h" #include "olad/Device.h" #include "plugins/usbdmx/DMXCProjectsNodleU1.h" @@ -62,6 +63,23 @@ class DMXCProjectsNodleU1Device: public Device { private: const std::string m_device_id; std::auto_ptr m_out_port; + /* + std::auto_ptr m_out_port1; + std::auto_ptr m_out_port2; + std::auto_ptr m_out_port3; + std::auto_ptr m_out_port4; + std::auto_ptr m_out_port5; + std::auto_ptr m_out_port6; + std::auto_ptr m_out_port7; + std::auto_ptr m_out_port8; + std::auto_ptr m_out_port9; + std::auto_ptr m_out_port10; + std::auto_ptr m_out_port11; + std::auto_ptr m_out_port12; + std::auto_ptr m_out_port13; + std::auto_ptr m_out_port14; + std::auto_ptr m_out_port15; + */ std::auto_ptr m_in_port; DISALLOW_COPY_AND_ASSIGN(DMXCProjectsNodleU1Device); diff --git a/plugins/usbdmx/DMXCProjectsNodleU1Factory.cpp b/plugins/usbdmx/DMXCProjectsNodleU1Factory.cpp index 094b228dd3..7235ada5c3 100644 --- a/plugins/usbdmx/DMXCProjectsNodleU1Factory.cpp +++ b/plugins/usbdmx/DMXCProjectsNodleU1Factory.cpp @@ -43,6 +43,7 @@ bool DMXCProjectsNodleU1Factory::DeviceAdded( WidgetObserver *observer, libusb_device *usb_device, const struct libusb_device_descriptor &descriptor) { + OLA_DEBUG << "Factory, DeviceAdded"; if ( ((descriptor.idVendor != VENDOR_ID) && (descriptor.idVendor != VENDOR_ID_FX5)) || @@ -60,6 +61,25 @@ bool DMXCProjectsNodleU1Factory::DeviceAdded( OLA_INFO << "Nodle U1 serial: " << info.serial; + // Check if it's a RP2040-based widget and if so, how many ins and outs it has + int ret = 0; + unsigned int ins = 1; // Input universes + unsigned int outs = 1; // Output universes + char variant = 'S'; // Variant: S = simple, R = RDM (not yet implemented) + if (info.serial.find('RP2040_') != std::string::npos) { + // Model format: ??Tx ??Rx S + ret = sscanf(info.product.c_str(), "%u2Tx %u2Rx %c", &ins, &outs, &variant); + if (ret == 3) { + OLA_INFO << "It's a RP2040-based device with " << ins << " INs and " << + outs << " OUTs"; + } else { + // Reset the values back to their default, just in case + ins = 1; + outs = 1; + variant = 'S'; + } + } + if (m_preferences->SetDefaultValue( "nodle-" + info.serial + "-mode", UIntValidator(DMXCProjectsNodleU1::NODLE_MIN_MODE, @@ -79,12 +99,12 @@ bool DMXCProjectsNodleU1Factory::DeviceAdded( DMXCProjectsNodleU1 *widget = NULL; if (FLAGS_use_async_libusb) { widget = new AsynchronousDMXCProjectsNodleU1(m_adaptor, usb_device, - m_plugin_adaptor, info.serial, - mode); + m_plugin_adaptor, info.serial, + mode, ins, outs); } else { widget = new SynchronousDMXCProjectsNodleU1(m_adaptor, usb_device, m_plugin_adaptor, info.serial, - mode); + mode, ins, outs); } return AddWidget(observer, widget); } From 8880e98c8ee4d3f4184e9cdbd928caf74d36f209 Mon Sep 17 00:00:00 2001 From: Jannis Achstetter Date: Mon, 1 Feb 2021 22:14:41 +0100 Subject: [PATCH 05/11] Support multiple INs and OUTs --- plugins/usbdmx/DMXCProjectsNodleU1.h | 22 +++++- plugins/usbdmx/DMXCProjectsNodleU1Device.cpp | 69 ++++++++++++++----- plugins/usbdmx/DMXCProjectsNodleU1Device.h | 23 ++----- plugins/usbdmx/DMXCProjectsNodleU1Factory.cpp | 4 +- plugins/usbdmx/GenericOutputPort.cpp | 1 + plugins/usbdmx/GenericOutputPort.h | 3 +- 6 files changed, 81 insertions(+), 41 deletions(-) diff --git a/plugins/usbdmx/DMXCProjectsNodleU1.h b/plugins/usbdmx/DMXCProjectsNodleU1.h index 3c82f1c7aa..13925a9dc1 100644 --- a/plugins/usbdmx/DMXCProjectsNodleU1.h +++ b/plugins/usbdmx/DMXCProjectsNodleU1.h @@ -48,10 +48,12 @@ class DMXCProjectsNodleU1: public SimpleWidget { const std::string &serial, unsigned int mode, unsigned int ins, - unsigned int out) + unsigned int outs) : SimpleWidget(adaptor, usb_device), m_serial(serial), m_mode(mode), + m_ins(ins), + m_outs(outs), m_plugin_adaptor(plugin_adaptor) { } @@ -71,6 +73,22 @@ class DMXCProjectsNodleU1: public SimpleWidget { return m_mode; } + /** + * @brief Get the number of inputs suppported by this widget. + * @returns The number of inputs. + */ + unsigned int Ins() const { + return m_ins; + } + + /** + * @brief Get the number of outouts suppported by this widget. + * @returns The number of outouts. + */ + unsigned int Outs() const { + return m_outs; + } + virtual void SetDmxCallback(Callback0 *callback) = 0; virtual const DmxBuffer &GetDmxInBuffer() = 0; @@ -87,6 +105,8 @@ class DMXCProjectsNodleU1: public SimpleWidget { protected: unsigned int m_mode; + unsigned int m_ins; + unsigned int m_outs; PluginAdaptor *m_plugin_adaptor; }; diff --git a/plugins/usbdmx/DMXCProjectsNodleU1Device.cpp b/plugins/usbdmx/DMXCProjectsNodleU1Device.cpp index e30598ee82..465d753dd6 100644 --- a/plugins/usbdmx/DMXCProjectsNodleU1Device.cpp +++ b/plugins/usbdmx/DMXCProjectsNodleU1Device.cpp @@ -36,30 +36,63 @@ DMXCProjectsNodleU1Device::DMXCProjectsNodleU1Device( PluginAdaptor *plugin_adaptor) : Device(owner, device_name), m_device_id(device_id), - m_out_port(), - m_in_port() { - unsigned int mode = widget->Mode(); + m_widget(widget), + m_plugin_adaptor(plugin_adaptor) { +} - OLA_DEBUG << "Device CTOR: checking serial number: " << widget->SerialNumber(); +bool DMXCProjectsNodleU1Device::StartHook() { + unsigned int mode = m_widget->Mode(); + unsigned int ins = m_widget->Ins(); + unsigned int outs = m_widget->Outs(); - if (mode & DMXCProjectsNodleU1::OUTPUT_ENABLE_MASK) { // output port active - m_out_port.reset(new GenericOutputPort(this, 0, widget)); - } + bool ok = true; - if (mode & DMXCProjectsNodleU1::INPUT_ENABLE_MASK) { // input port active - m_in_port.reset(new DMXCProjectsNodleU1InputPort(this, 0, plugin_adaptor, - widget)); - } -} + OLA_DEBUG << "StartHook, will create " << ins << " INs and " << outs << " OUTs"; -bool DMXCProjectsNodleU1Device::StartHook() { - if (m_out_port.get()) { - AddPort(m_out_port.release()); + if (ins != 1) { + for (unsigned i = 0; i < ins; i++) { + OLA_DEBUG << "IN " << i; + DMXCProjectsNodleU1InputPort *port = new DMXCProjectsNodleU1InputPort( + this, i, m_plugin_adaptor, + m_widget); + if (!AddPort(port)) { + OLA_DEBUG << "FAILED"; + delete port; + ok = false; + } + } + } else if (mode & DMXCProjectsNodleU1::INPUT_ENABLE_MASK) { + // input port active + DMXCProjectsNodleU1InputPort *port = new DMXCProjectsNodleU1InputPort( + this, 0, m_plugin_adaptor, + m_widget); + if (!AddPort(port)) { + delete port; + ok = false; + } } - if (m_in_port.get()) { - AddPort(m_in_port.release()); + + if (outs != 1) { + for (unsigned i = 0; i < outs; i++) { + OLA_DEBUG << "OUT " << i; + GenericOutputPort *port = new GenericOutputPort(this, i, m_widget); + if (!AddPort(port)) { + OLA_DEBUG << "FAILED"; + delete port; + ok = false; + } + } + } else if (mode & DMXCProjectsNodleU1::OUTPUT_ENABLE_MASK) { + // output port active + GenericOutputPort *port = new GenericOutputPort(this, 0, m_widget); + if (!AddPort(port)) { + delete port; + ok = false; + } } - return true; + + OLA_DEBUG << "StartHook will return " << ok; + return ok; } } // namespace usbdmx } // namespace plugin diff --git a/plugins/usbdmx/DMXCProjectsNodleU1Device.h b/plugins/usbdmx/DMXCProjectsNodleU1Device.h index 8db58cb331..031d6183ab 100644 --- a/plugins/usbdmx/DMXCProjectsNodleU1Device.h +++ b/plugins/usbdmx/DMXCProjectsNodleU1Device.h @@ -57,30 +57,15 @@ class DMXCProjectsNodleU1Device: public Device { return m_device_id; } + bool AllowMultiPortPatching() const { return true; } + protected: bool StartHook(); private: const std::string m_device_id; - std::auto_ptr m_out_port; - /* - std::auto_ptr m_out_port1; - std::auto_ptr m_out_port2; - std::auto_ptr m_out_port3; - std::auto_ptr m_out_port4; - std::auto_ptr m_out_port5; - std::auto_ptr m_out_port6; - std::auto_ptr m_out_port7; - std::auto_ptr m_out_port8; - std::auto_ptr m_out_port9; - std::auto_ptr m_out_port10; - std::auto_ptr m_out_port11; - std::auto_ptr m_out_port12; - std::auto_ptr m_out_port13; - std::auto_ptr m_out_port14; - std::auto_ptr m_out_port15; - */ - std::auto_ptr m_in_port; + DMXCProjectsNodleU1 *m_widget; + PluginAdaptor*m_plugin_adaptor; DISALLOW_COPY_AND_ASSIGN(DMXCProjectsNodleU1Device); }; diff --git a/plugins/usbdmx/DMXCProjectsNodleU1Factory.cpp b/plugins/usbdmx/DMXCProjectsNodleU1Factory.cpp index 7235ada5c3..001b542856 100644 --- a/plugins/usbdmx/DMXCProjectsNodleU1Factory.cpp +++ b/plugins/usbdmx/DMXCProjectsNodleU1Factory.cpp @@ -68,10 +68,10 @@ bool DMXCProjectsNodleU1Factory::DeviceAdded( char variant = 'S'; // Variant: S = simple, R = RDM (not yet implemented) if (info.serial.find('RP2040_') != std::string::npos) { // Model format: ??Tx ??Rx S - ret = sscanf(info.product.c_str(), "%u2Tx %u2Rx %c", &ins, &outs, &variant); + ret = sscanf(info.product.c_str(), "%uTx %uRx %c", &outs, &ins, &variant); if (ret == 3) { OLA_INFO << "It's a RP2040-based device with " << ins << " INs and " << - outs << " OUTs"; + outs << " OUTs"; } else { // Reset the values back to their default, just in case ins = 1; diff --git a/plugins/usbdmx/GenericOutputPort.cpp b/plugins/usbdmx/GenericOutputPort.cpp index 6890527c0b..fe22221416 100644 --- a/plugins/usbdmx/GenericOutputPort.cpp +++ b/plugins/usbdmx/GenericOutputPort.cpp @@ -34,6 +34,7 @@ GenericOutputPort::GenericOutputPort(Device *parent, : BasicOutputPort(parent, id), m_widget(widget) { OLA_DEBUG << "NEW GENERICOUTPUTPORT ID: " << id; + m_description = "Output " + std::to_string(id); } bool GenericOutputPort::WriteDMX(const DmxBuffer &buffer, diff --git a/plugins/usbdmx/GenericOutputPort.h b/plugins/usbdmx/GenericOutputPort.h index 8cdc03b42a..e18c1bb6d4 100644 --- a/plugins/usbdmx/GenericOutputPort.h +++ b/plugins/usbdmx/GenericOutputPort.h @@ -51,10 +51,11 @@ class GenericOutputPort: public BasicOutputPort { bool WriteDMX(const DmxBuffer &buffer, uint8_t priority); - std::string Description() const { return ""; } + std::string Description() const { return m_description; } private: class WidgetInterface* const m_widget; + std::string m_description; DISALLOW_COPY_AND_ASSIGN(GenericOutputPort); }; From 1fce273296c9d700b05c7de820711214bfe0985e Mon Sep 17 00:00:00 2001 From: Jannis Achstetter Date: Tue, 2 Feb 2021 08:17:56 +0100 Subject: [PATCH 06/11] Make the GenericOutputPort pass the port's id to the widget so they can be distinguished --- plugins/usbdmx/GenericOutputPort.cpp | 2 +- plugins/usbdmx/Widget.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/usbdmx/GenericOutputPort.cpp b/plugins/usbdmx/GenericOutputPort.cpp index fe22221416..399a5f7cf1 100644 --- a/plugins/usbdmx/GenericOutputPort.cpp +++ b/plugins/usbdmx/GenericOutputPort.cpp @@ -39,7 +39,7 @@ GenericOutputPort::GenericOutputPort(Device *parent, bool GenericOutputPort::WriteDMX(const DmxBuffer &buffer, OLA_UNUSED uint8_t priority) { - m_widget->SendDMX(buffer); + m_widget->SendDMX(buffer, this->PortId()); return true; } } // namespace usbdmx diff --git a/plugins/usbdmx/Widget.h b/plugins/usbdmx/Widget.h index 19fb99046b..7686a82632 100644 --- a/plugins/usbdmx/Widget.h +++ b/plugins/usbdmx/Widget.h @@ -54,7 +54,7 @@ class WidgetInterface { * @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; + virtual bool SendDMX(const DmxBuffer &buffer, unsigned int portId = 0) = 0; }; /** From cc866c4cc4974e8f227c7a893fa753090c2eceee Mon Sep 17 00:00:00 2001 From: Jannis Achstetter Date: Tue, 2 Feb 2021 09:55:21 +0100 Subject: [PATCH 07/11] Adapt all affected widgets to support the additional port parameter --- plugins/usbdmx/AnymauDMX.cpp | 6 ++++-- plugins/usbdmx/AnymauDMX.h | 4 ++-- plugins/usbdmx/DMXCProjectsNodleU1.cpp | 6 ++++-- plugins/usbdmx/DMXCProjectsNodleU1.h | 4 ++-- plugins/usbdmx/EurolitePro.cpp | 6 ++++-- plugins/usbdmx/EurolitePro.h | 4 ++-- plugins/usbdmx/ScanlimeFadecandy.cpp | 6 ++++-- plugins/usbdmx/ScanlimeFadecandy.h | 4 ++-- plugins/usbdmx/Sunlite.cpp | 6 ++++-- plugins/usbdmx/Sunlite.h | 4 ++-- plugins/usbdmx/VellemanK8062.cpp | 6 ++++-- plugins/usbdmx/VellemanK8062.h | 4 ++-- 12 files changed, 36 insertions(+), 24 deletions(-) diff --git a/plugins/usbdmx/AnymauDMX.cpp b/plugins/usbdmx/AnymauDMX.cpp index 240ce2f979..be3e19dcfd 100644 --- a/plugins/usbdmx/AnymauDMX.cpp +++ b/plugins/usbdmx/AnymauDMX.cpp @@ -109,7 +109,8 @@ bool SynchronousAnymauDMX::Init() { return true; } -bool SynchronousAnymauDMX::SendDMX(const DmxBuffer &buffer) { +bool SynchronousAnymauDMX::SendDMX(const DmxBuffer &buffer, + unsigned int portId) { return m_sender.get() ? m_sender->SendDMX(buffer) : false; } @@ -173,7 +174,8 @@ bool AsynchronousAnymauDMX::Init() { return m_sender->Init(); } -bool AsynchronousAnymauDMX::SendDMX(const DmxBuffer &buffer) { +bool AsynchronousAnymauDMX::SendDMX(const DmxBuffer &buffer, + unsigned int portId) { return m_sender->SendDMX(buffer); } } // namespace usbdmx diff --git a/plugins/usbdmx/AnymauDMX.h b/plugins/usbdmx/AnymauDMX.h index 26bf8cc621..5b46166eb4 100644 --- a/plugins/usbdmx/AnymauDMX.h +++ b/plugins/usbdmx/AnymauDMX.h @@ -85,7 +85,7 @@ class SynchronousAnymauDMX: public AnymauDMX { bool Init(); - bool SendDMX(const DmxBuffer &buffer); + bool SendDMX(const DmxBuffer &buffer, unsigned int portId = 0); private: std::auto_ptr m_sender; @@ -110,7 +110,7 @@ class AsynchronousAnymauDMX : public AnymauDMX { bool Init(); - bool SendDMX(const DmxBuffer &buffer); + bool SendDMX(const DmxBuffer &buffer, unsigned int portId = 0); private: std::auto_ptr m_sender; diff --git a/plugins/usbdmx/DMXCProjectsNodleU1.cpp b/plugins/usbdmx/DMXCProjectsNodleU1.cpp index 6eff4360e2..d2b7fc1f70 100644 --- a/plugins/usbdmx/DMXCProjectsNodleU1.cpp +++ b/plugins/usbdmx/DMXCProjectsNodleU1.cpp @@ -326,7 +326,8 @@ bool SynchronousDMXCProjectsNodleU1::Init() { return true; } -bool SynchronousDMXCProjectsNodleU1::SendDMX(const DmxBuffer &buffer) { +bool SynchronousDMXCProjectsNodleU1::SendDMX(const DmxBuffer &buffer, + unsigned int portId) { if (m_sender.get()) { return m_sender->SendDMX(buffer); } else { @@ -555,7 +556,8 @@ bool AsynchronousDMXCProjectsNodleU1::Init() { return ok; } -bool AsynchronousDMXCProjectsNodleU1::SendDMX(const DmxBuffer &buffer) { +bool AsynchronousDMXCProjectsNodleU1::SendDMX(const DmxBuffer &buffer, + unsigned int portId) { return m_sender.get() ? m_sender->SendDMX(buffer) : false; } diff --git a/plugins/usbdmx/DMXCProjectsNodleU1.h b/plugins/usbdmx/DMXCProjectsNodleU1.h index 13925a9dc1..90a977a4ec 100644 --- a/plugins/usbdmx/DMXCProjectsNodleU1.h +++ b/plugins/usbdmx/DMXCProjectsNodleU1.h @@ -136,7 +136,7 @@ class SynchronousDMXCProjectsNodleU1: public DMXCProjectsNodleU1 { bool Init(); - bool SendDMX(const DmxBuffer &buffer); + bool SendDMX(const DmxBuffer &buffer, unsigned int portId = 0); void SetDmxCallback(Callback0 *callback); const DmxBuffer &GetDmxInBuffer(); @@ -173,7 +173,7 @@ class AsynchronousDMXCProjectsNodleU1 : public DMXCProjectsNodleU1 { bool Init(); - bool SendDMX(const DmxBuffer &buffer); + bool SendDMX(const DmxBuffer &buffer, unsigned int portId = 0); void SetDmxCallback(Callback0 *callback); const DmxBuffer &GetDmxInBuffer(); diff --git a/plugins/usbdmx/EurolitePro.cpp b/plugins/usbdmx/EurolitePro.cpp index 31781ed1e5..add6aa637a 100644 --- a/plugins/usbdmx/EurolitePro.cpp +++ b/plugins/usbdmx/EurolitePro.cpp @@ -184,7 +184,8 @@ bool SynchronousEurolitePro::Init() { return true; } -bool SynchronousEurolitePro::SendDMX(const DmxBuffer &buffer) { +bool SynchronousEurolitePro::SendDMX(const DmxBuffer &buffer, + unsigned int portId) { return m_sender.get() ? m_sender->SendDMX(buffer) : false; } @@ -241,7 +242,8 @@ bool AsynchronousEurolitePro::Init() { return m_sender->Init(); } -bool AsynchronousEurolitePro::SendDMX(const DmxBuffer &buffer) { +bool AsynchronousEurolitePro::SendDMX(const DmxBuffer &buffer, + unsigned int portId) { return m_sender->SendDMX(buffer); } } // namespace usbdmx diff --git a/plugins/usbdmx/EurolitePro.h b/plugins/usbdmx/EurolitePro.h index 8b8516cb96..4ec4cee51d 100644 --- a/plugins/usbdmx/EurolitePro.h +++ b/plugins/usbdmx/EurolitePro.h @@ -85,7 +85,7 @@ class SynchronousEurolitePro: public EurolitePro { bool Init(); - bool SendDMX(const DmxBuffer &buffer); + bool SendDMX(const DmxBuffer &buffer, unsigned int portId = 0); private: std::auto_ptr m_sender; @@ -110,7 +110,7 @@ class AsynchronousEurolitePro: public EurolitePro { bool Init(); - bool SendDMX(const DmxBuffer &buffer); + bool SendDMX(const DmxBuffer &buffer, unsigned int portId = 0); private: std::auto_ptr m_sender; diff --git a/plugins/usbdmx/ScanlimeFadecandy.cpp b/plugins/usbdmx/ScanlimeFadecandy.cpp index 375539cba4..a74c9dfc51 100644 --- a/plugins/usbdmx/ScanlimeFadecandy.cpp +++ b/plugins/usbdmx/ScanlimeFadecandy.cpp @@ -275,7 +275,8 @@ bool SynchronousScanlimeFadecandy::Init() { return true; } -bool SynchronousScanlimeFadecandy::SendDMX(const DmxBuffer &buffer) { +bool SynchronousScanlimeFadecandy::SendDMX(const DmxBuffer &buffer, + unsigned int portId) { return m_sender.get() ? m_sender->SendDMX(buffer) : false; } @@ -338,7 +339,8 @@ bool AsynchronousScanlimeFadecandy::Init() { return m_sender->Init(); } -bool AsynchronousScanlimeFadecandy::SendDMX(const DmxBuffer &buffer) { +bool AsynchronousScanlimeFadecandy::SendDMX(const DmxBuffer &buffer, + unsigned int portId) { return m_sender->SendDMX(buffer); } } // namespace usbdmx diff --git a/plugins/usbdmx/ScanlimeFadecandy.h b/plugins/usbdmx/ScanlimeFadecandy.h index b88deada4f..41601e2fae 100644 --- a/plugins/usbdmx/ScanlimeFadecandy.h +++ b/plugins/usbdmx/ScanlimeFadecandy.h @@ -86,7 +86,7 @@ class SynchronousScanlimeFadecandy: public ScanlimeFadecandy { bool Init(); - bool SendDMX(const DmxBuffer &buffer); + bool SendDMX(const DmxBuffer &buffer, unsigned int portId = 0); private: std::auto_ptr m_sender; @@ -111,7 +111,7 @@ class AsynchronousScanlimeFadecandy : public ScanlimeFadecandy { bool Init(); - bool SendDMX(const DmxBuffer &buffer); + bool SendDMX(const DmxBuffer &buffer, unsigned int portId = 0); private: std::auto_ptr m_sender; diff --git a/plugins/usbdmx/Sunlite.cpp b/plugins/usbdmx/Sunlite.cpp index 621ef4462b..07ab305dd5 100644 --- a/plugins/usbdmx/Sunlite.cpp +++ b/plugins/usbdmx/Sunlite.cpp @@ -158,7 +158,8 @@ bool SynchronousSunlite::Init() { return true; } -bool SynchronousSunlite::SendDMX(const DmxBuffer &buffer) { +bool SynchronousSunlite::SendDMX(const DmxBuffer &buffer, + unsigned int portId) { return m_sender.get() ? m_sender->SendDMX(buffer) : false; } @@ -209,7 +210,8 @@ bool AsynchronousSunlite::Init() { return m_sender->Init(); } -bool AsynchronousSunlite::SendDMX(const DmxBuffer &buffer) { +bool AsynchronousSunlite::SendDMX(const DmxBuffer &buffer, + unsigned int portId) { return m_sender->SendDMX(buffer); } } // namespace usbdmx diff --git a/plugins/usbdmx/Sunlite.h b/plugins/usbdmx/Sunlite.h index 1db1461d64..87bbf6d5a6 100644 --- a/plugins/usbdmx/Sunlite.h +++ b/plugins/usbdmx/Sunlite.h @@ -63,7 +63,7 @@ class SynchronousSunlite: public Sunlite { bool Init(); - bool SendDMX(const DmxBuffer &buffer); + bool SendDMX(const DmxBuffer &buffer, unsigned int portId = 0); private: std::auto_ptr m_sender; @@ -86,7 +86,7 @@ class AsynchronousSunlite: public Sunlite { bool Init(); - bool SendDMX(const DmxBuffer &buffer); + bool SendDMX(const DmxBuffer &buffer, unsigned int portId = 0); private: std::auto_ptr m_sender; diff --git a/plugins/usbdmx/VellemanK8062.cpp b/plugins/usbdmx/VellemanK8062.cpp index 72c8f49426..2c7590776a 100644 --- a/plugins/usbdmx/VellemanK8062.cpp +++ b/plugins/usbdmx/VellemanK8062.cpp @@ -320,7 +320,8 @@ bool SynchronousVellemanK8062::Init() { return true; } -bool SynchronousVellemanK8062::SendDMX(const DmxBuffer &buffer) { +bool SynchronousVellemanK8062::SendDMX(const DmxBuffer &buffer, + unsigned int portId) { return m_sender.get() ? m_sender->SendDMX(buffer) : false; } @@ -508,7 +509,8 @@ bool AsynchronousVellemanK8062::Init() { return m_sender->Init(); } -bool AsynchronousVellemanK8062::SendDMX(const DmxBuffer &buffer) { +bool AsynchronousVellemanK8062::SendDMX(const DmxBuffer &buffer, + unsigned int portId) { return m_sender->SendDMX(buffer); } } // namespace usbdmx diff --git a/plugins/usbdmx/VellemanK8062.h b/plugins/usbdmx/VellemanK8062.h index c20be290c3..30d2578929 100644 --- a/plugins/usbdmx/VellemanK8062.h +++ b/plugins/usbdmx/VellemanK8062.h @@ -63,7 +63,7 @@ class SynchronousVellemanK8062: public VellemanK8062 { bool Init(); - bool SendDMX(const DmxBuffer &buffer); + bool SendDMX(const DmxBuffer &buffer, unsigned int portId = 0); private: std::auto_ptr m_sender; @@ -86,7 +86,7 @@ class AsynchronousVellemanK8062 : public VellemanK8062 { bool Init(); - bool SendDMX(const DmxBuffer &buffer); + bool SendDMX(const DmxBuffer &buffer, unsigned int portId = 0); private: std::auto_ptr m_sender; From 9b69af847b29096cb7194b3e8c1af8ad4491d2e6 Mon Sep 17 00:00:00 2001 From: Jannis Achstetter Date: Tue, 2 Feb 2021 12:34:50 +0100 Subject: [PATCH 08/11] Adapt the AsyncUsbSender to support multiple ports --- plugins/usbdmx/AnymauDMX.cpp | 2 +- plugins/usbdmx/AsyncUsbSender.cpp | 21 ++++++++++++--------- plugins/usbdmx/AsyncUsbSender.h | 11 ++++++----- plugins/usbdmx/DMXCProjectsNodleU1.cpp | 15 +++++++++------ plugins/usbdmx/EurolitePro.cpp | 2 +- plugins/usbdmx/ScanlimeFadecandy.cpp | 5 +++-- plugins/usbdmx/Sunlite.cpp | 2 +- plugins/usbdmx/VellemanK8062.cpp | 7 ++++--- 8 files changed, 37 insertions(+), 28 deletions(-) diff --git a/plugins/usbdmx/AnymauDMX.cpp b/plugins/usbdmx/AnymauDMX.cpp index be3e19dcfd..92408f4fe2 100644 --- a/plugins/usbdmx/AnymauDMX.cpp +++ b/plugins/usbdmx/AnymauDMX.cpp @@ -136,7 +136,7 @@ class AnymaAsyncUsbSender : public AsyncUsbSender { return ok ? usb_handle : NULL; } - bool PerformTransfer(const DmxBuffer &buffer) { + bool PerformTransfer(const DmxBuffer &buffer, unsigned int portId) { m_adaptor->FillControlSetup( m_control_setup_buffer, LIBUSB_REQUEST_TYPE_VENDOR | LIBUSB_RECIPIENT_DEVICE | diff --git a/plugins/usbdmx/AsyncUsbSender.cpp b/plugins/usbdmx/AsyncUsbSender.cpp index 22e8a12f89..1ee8fb2c24 100644 --- a/plugins/usbdmx/AsyncUsbSender.cpp +++ b/plugins/usbdmx/AsyncUsbSender.cpp @@ -31,27 +31,29 @@ using ola::usb::LibUsbAdaptor; AsyncUsbSender::AsyncUsbSender(LibUsbAdaptor *adaptor, libusb_device *usb_device) - : AsyncUsbTransceiverBase(adaptor, usb_device), - m_pending_tx(false) { + : AsyncUsbTransceiverBase(adaptor, usb_device) { } AsyncUsbSender::~AsyncUsbSender() { m_adaptor->Close(m_usb_handle); } -bool AsyncUsbSender::SendDMX(const DmxBuffer &buffer) { +bool AsyncUsbSender::SendDMX(const DmxBuffer &buffer, unsigned int portId) { if (!m_usb_handle) { OLA_WARN << "AsyncUsbSender hasn't been initialized"; return false; } ola::thread::MutexLocker locker(&m_mutex); if (m_transfer_state == IDLE) { - PerformTransfer(buffer); + PerformTransfer(buffer, portId); } else { // Buffer incoming data so we can send it when the outstanding transfers // complete. - m_pending_tx = true; - m_tx_buffer.Set(buffer); + if (m_tx_buffers.count(portId)) { + m_tx_buffers[portId].Set(buffer); + } else { + m_tx_buffers.insert(std::pair(portId, buffer)); + } } return true; } @@ -77,9 +79,10 @@ void AsyncUsbSender::TransferComplete(struct libusb_transfer *transfer) { PostTransferHook(); - if ((m_transfer_state == IDLE) && m_pending_tx) { - m_pending_tx = false; - PerformTransfer(m_tx_buffer); + if ((m_transfer_state == IDLE) && m_tx_buffers.size()) { + std::map::iterator it = m_tx_buffers.begin(); + PerformTransfer(it->second, it->first); + m_tx_buffers.erase(it->first); } } } // namespace usbdmx diff --git a/plugins/usbdmx/AsyncUsbSender.h b/plugins/usbdmx/AsyncUsbSender.h index 24a35b5ade..8b33430236 100644 --- a/plugins/usbdmx/AsyncUsbSender.h +++ b/plugins/usbdmx/AsyncUsbSender.h @@ -22,6 +22,7 @@ #define PLUGINS_USBDMX_ASYNCUSBSENDER_H_ #include +#include #include "AsyncUsbTransceiverBase.h" #include "libs/usb/LibUsbAdaptor.h" @@ -59,7 +60,7 @@ class AsyncUsbSender: public AsyncUsbTransceiverBase { * @param buffer the DMX data to send. * @returns the value of PerformTransfer(). */ - bool SendDMX(const DmxBuffer &buffer); + bool SendDMX(const DmxBuffer &buffer, unsigned int portId = 0); /** * @brief Called from the libusb callback when the asynchronous transfer @@ -78,7 +79,8 @@ class AsyncUsbSender: public AsyncUsbTransceiverBase { * FillControlTransfer() / FillBulkTransfer() as appropriate and then call * SubmitTransfer(). */ - virtual bool PerformTransfer(const DmxBuffer &buffer) = 0; + virtual bool PerformTransfer(const DmxBuffer &buffer, + unsigned int portId) = 0; /** * @brief Called when the transfer completes. @@ -92,11 +94,10 @@ class AsyncUsbSender: public AsyncUsbTransceiverBase { * @brief Check if there is a pending transfer. * @returns true if there is a transfer in progress, false otherwise. */ - bool TransferPending() const { return m_pending_tx; } + bool TransferPending() const { return (bool)(m_tx_buffers.size()); } private: - DmxBuffer m_tx_buffer; // GUARDED_BY(m_mutex); - bool m_pending_tx; // GUARDED_BY(m_mutex); + std::map m_tx_buffers; // GUARDED_BY(m_mutex); DISALLOW_COPY_AND_ASSIGN(AsyncUsbSender); }; diff --git a/plugins/usbdmx/DMXCProjectsNodleU1.cpp b/plugins/usbdmx/DMXCProjectsNodleU1.cpp index d2b7fc1f70..b989cb26db 100644 --- a/plugins/usbdmx/DMXCProjectsNodleU1.cpp +++ b/plugins/usbdmx/DMXCProjectsNodleU1.cpp @@ -428,7 +428,7 @@ class DMXCProjectsNodleU1AsyncUsbSender : public AsyncUsbSender { return handle; } - bool PerformTransfer(const DmxBuffer &buffer); + bool PerformTransfer(const DmxBuffer &buffer, unsigned int portId = 0); void PostTransferHook(); @@ -442,7 +442,7 @@ class DMXCProjectsNodleU1AsyncUsbSender : public AsyncUsbSender { bool ContinueTransfer(); - bool SendInitialChunk(const DmxBuffer &buffer); + bool SendInitialChunk(const DmxBuffer &buffer, unsigned int portId); bool SendChunk() { FillInterruptTransfer(WRITE_ENDPOINT, m_packet, @@ -454,9 +454,9 @@ class DMXCProjectsNodleU1AsyncUsbSender : public AsyncUsbSender { }; bool DMXCProjectsNodleU1AsyncUsbSender::PerformTransfer( - const DmxBuffer &buffer) { + const DmxBuffer &buffer, unsigned int portId) { if (m_buffer_offset == 0) { - return SendInitialChunk(buffer); + return SendInitialChunk(buffer, portId); } // Otherwise we're part way through a transfer, do nothing. return true; @@ -489,7 +489,10 @@ bool DMXCProjectsNodleU1AsyncUsbSender::ContinueTransfer() { } bool DMXCProjectsNodleU1AsyncUsbSender::SendInitialChunk( - const DmxBuffer &buffer) { + const DmxBuffer &buffer, unsigned int portId) { + + OLA_DEBUG << "SendInitialChunk for portId" << portId; + unsigned int length = 32; m_tx_buffer.SetRange(0, buffer.GetRaw(), buffer.Size()); @@ -558,7 +561,7 @@ bool AsynchronousDMXCProjectsNodleU1::Init() { bool AsynchronousDMXCProjectsNodleU1::SendDMX(const DmxBuffer &buffer, unsigned int portId) { - return m_sender.get() ? m_sender->SendDMX(buffer) : false; + return m_sender.get() ? m_sender->SendDMX(buffer, portId) : false; } void AsynchronousDMXCProjectsNodleU1::SetDmxCallback( diff --git a/plugins/usbdmx/EurolitePro.cpp b/plugins/usbdmx/EurolitePro.cpp index add6aa637a..d9a569a48d 100644 --- a/plugins/usbdmx/EurolitePro.cpp +++ b/plugins/usbdmx/EurolitePro.cpp @@ -214,7 +214,7 @@ class EuroliteProAsyncUsbSender : public AsyncUsbSender { return ok ? usb_handle : NULL; } - bool PerformTransfer(const DmxBuffer &buffer) { + bool PerformTransfer(const DmxBuffer &buffer, unsigned int port) { CreateFrame(buffer, m_tx_frame); FillBulkTransfer(ENDPOINT, m_tx_frame, EUROLITE_PRO_FRAME_SIZE, URB_TIMEOUT_MS); diff --git a/plugins/usbdmx/ScanlimeFadecandy.cpp b/plugins/usbdmx/ScanlimeFadecandy.cpp index a74c9dfc51..e2938a85da 100644 --- a/plugins/usbdmx/ScanlimeFadecandy.cpp +++ b/plugins/usbdmx/ScanlimeFadecandy.cpp @@ -291,7 +291,7 @@ class FadecandyAsyncUsbSender : public AsyncUsbSender { libusb_device_handle* SetupHandle(); - bool PerformTransfer(const DmxBuffer &buffer); + bool PerformTransfer(const DmxBuffer &buffer, unsigned int portId); private: fadecandy_packet m_data_packets[PACKETS_PER_UPDATE]; @@ -313,7 +313,8 @@ libusb_device_handle* FadecandyAsyncUsbSender::SetupHandle() { return usb_handle; } -bool FadecandyAsyncUsbSender::PerformTransfer(const DmxBuffer &buffer) { +bool FadecandyAsyncUsbSender::PerformTransfer(const DmxBuffer &buffer, + unsigned int portId) { UpdatePacketsWithDMX(m_data_packets, buffer); // We do a single bulk transfer of the entire data, rather than one transfer // for each 64 bytes. diff --git a/plugins/usbdmx/Sunlite.cpp b/plugins/usbdmx/Sunlite.cpp index 07ab305dd5..0ea4c9dfb6 100644 --- a/plugins/usbdmx/Sunlite.cpp +++ b/plugins/usbdmx/Sunlite.cpp @@ -184,7 +184,7 @@ class SunliteAsyncUsbSender : public AsyncUsbSender { return ok ? usb_handle : NULL; } - bool PerformTransfer(const DmxBuffer &buffer) { + bool PerformTransfer(const DmxBuffer &buffer, unsigned int portId) { UpdatePacket(buffer, m_packet); FillBulkTransfer(ENDPOINT, m_packet, SUNLITE_PACKET_SIZE, TIMEOUT); return (SubmitTransfer() == 0); diff --git a/plugins/usbdmx/VellemanK8062.cpp b/plugins/usbdmx/VellemanK8062.cpp index 2c7590776a..fa8d8f5884 100644 --- a/plugins/usbdmx/VellemanK8062.cpp +++ b/plugins/usbdmx/VellemanK8062.cpp @@ -352,7 +352,7 @@ class VellemanAsyncUsbSender : public AsyncUsbSender { return handle; } - bool PerformTransfer(const DmxBuffer &buffer); + bool PerformTransfer(const DmxBuffer &buffer, unsigned int portId); void PostTransferHook(); @@ -380,7 +380,8 @@ class VellemanAsyncUsbSender : public AsyncUsbSender { DISALLOW_COPY_AND_ASSIGN(VellemanAsyncUsbSender); }; -bool VellemanAsyncUsbSender::PerformTransfer(const DmxBuffer &buffer) { +bool VellemanAsyncUsbSender::PerformTransfer(const DmxBuffer &buffer, + unsigned int portId) { if (m_buffer_offset == 0) { return SendInitialChunk(buffer); } @@ -402,7 +403,7 @@ void VellemanAsyncUsbSender::PostTransferHook() { } else { // No pending transfer. The widget only actually sends a frame once the // next frame begins, so kick off the next frame here. - PerformTransfer(m_tx_buffer); + PerformTransfer(m_tx_buffer, 0); } } } From 65332f7eb724dfa566123a677d3542c620a8e6e3 Mon Sep 17 00:00:00 2001 From: Jannis Achstetter Date: Tue, 2 Feb 2021 13:31:03 +0100 Subject: [PATCH 09/11] Implement actually sending universes 1 - 15 --- plugins/usbdmx/DMXCProjectsNodleU1.cpp | 75 ++++++++++++++++++-------- 1 file changed, 52 insertions(+), 23 deletions(-) diff --git a/plugins/usbdmx/DMXCProjectsNodleU1.cpp b/plugins/usbdmx/DMXCProjectsNodleU1.cpp index b989cb26db..f0d52eb47a 100644 --- a/plugins/usbdmx/DMXCProjectsNodleU1.cpp +++ b/plugins/usbdmx/DMXCProjectsNodleU1.cpp @@ -54,6 +54,7 @@ static const int CONFIGURATION = 1; static const int INTERFACE = 0; static const unsigned int DATABLOCK_SIZE = 33; +static const unsigned int DATABLOCK_MAX_SIZE = 64; /* * @brief Send chosen mode to the DMX device @@ -438,15 +439,16 @@ class DMXCProjectsNodleU1AsyncUsbSender : public AsyncUsbSender { // This tracks where we are in m_tx_buffer. A value of 0 means we're at the // start of a DMX frame. unsigned int m_buffer_offset; - uint8_t m_packet[DATABLOCK_SIZE]; + unsigned int m_portId; + uint8_t m_packet[DATABLOCK_MAX_SIZE]; bool ContinueTransfer(); - bool SendInitialChunk(const DmxBuffer &buffer, unsigned int portId); + bool SendInitialChunk(const DmxBuffer &buffer); - bool SendChunk() { + bool SendChunk(unsigned int size) { FillInterruptTransfer(WRITE_ENDPOINT, m_packet, - DATABLOCK_SIZE, URB_TIMEOUT_MS); + size, URB_TIMEOUT_MS); return (SubmitTransfer() == 0); } @@ -456,7 +458,8 @@ class DMXCProjectsNodleU1AsyncUsbSender : public AsyncUsbSender { bool DMXCProjectsNodleU1AsyncUsbSender::PerformTransfer( const DmxBuffer &buffer, unsigned int portId) { if (m_buffer_offset == 0) { - return SendInitialChunk(buffer, portId); + m_portId = portId; + return SendInitialChunk(buffer); } // Otherwise we're part way through a transfer, do nothing. return true; @@ -478,35 +481,61 @@ void DMXCProjectsNodleU1AsyncUsbSender::PostTransferHook() { } bool DMXCProjectsNodleU1AsyncUsbSender::ContinueTransfer() { - unsigned int length = 32; + if (!m_portId) { + unsigned int length = 32; + + m_packet[0] = m_buffer_offset / 32; + + m_tx_buffer.GetRange(m_buffer_offset, m_packet + 1, &length); + memset(m_packet + 1 + length, 0, 32 - length); + m_buffer_offset += length; + return (SendChunk(DATABLOCK_SIZE) == 0); + } else { + unsigned int length = 60; - m_packet[0] = m_buffer_offset / 32; + m_packet[0] = 32; + m_packet[1] = ((m_portId << 4) & 0xF0) | ((m_buffer_offset / 60) & 0x0F); - m_tx_buffer.GetRange(m_buffer_offset, m_packet + 1, &length); - memset(m_packet + 1 + length, 0, 32 - length); - m_buffer_offset += length; - return (SendChunk() == 0); + m_tx_buffer.GetRange(m_buffer_offset, m_packet + 2, &length); + m_buffer_offset += length; + return (SendChunk(length + 2) == 0); + } } bool DMXCProjectsNodleU1AsyncUsbSender::SendInitialChunk( - const DmxBuffer &buffer, unsigned int portId) { - - OLA_DEBUG << "SendInitialChunk for portId" << portId; + const DmxBuffer &buffer) { - unsigned int length = 32; + OLA_DEBUG << "SendInitialChunk for portId " << m_portId; + // Copy the incoming buffer to our m_tx_buffer m_tx_buffer.SetRange(0, buffer.GetRaw(), buffer.Size()); - m_packet[0] = 0; - m_tx_buffer.GetRange(0, m_packet + 1, &length); - memset(m_packet + 1 + length, 0, 32 - length); + if (!m_portId) { + unsigned int length = 32; + + m_packet[0] = 0; + m_tx_buffer.GetRange(0, m_packet + 1, &length); + memset(m_packet + 1 + length, 0, 32 - length); - unsigned int slots_sent = length; - if (slots_sent < m_tx_buffer.Size()) { - // There are more frames to send. - m_buffer_offset = slots_sent; + unsigned int slots_sent = length; + if (slots_sent < m_tx_buffer.Size()) { + // There are more frames to send. + m_buffer_offset = slots_sent; + } + return (SendChunk(DATABLOCK_SIZE) == 0); + } else { + unsigned int length = 60; + m_packet[0] = 32; // Command code for advanced data (universe1-15) + m_packet[1] = m_portId << 4 & 0xF0; // Universe are the first 4 bits, offset the remaining 4 + m_tx_buffer.GetRange(0, m_packet + 2, &length); + + unsigned int slots_sent = length; + if (slots_sent < m_tx_buffer.Size()) { + // There are more frames to send. + m_buffer_offset = slots_sent; + } + return (SendChunk(slots_sent + 2) == 0); } - return (SendChunk() == 0); } // AsynchronousDMXCProjectsNodleU1 From 79fdaf97cdea44d7bafd91333b7435b3fa8c6703 Mon Sep 17 00:00:00 2001 From: Jannis Achstetter Date: Sat, 20 Feb 2021 21:46:15 +0100 Subject: [PATCH 10/11] Allow MultiPortPatching for the JaRule device --- plugins/usbdmx/JaRuleDevice.h | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/plugins/usbdmx/JaRuleDevice.h b/plugins/usbdmx/JaRuleDevice.h index c2cc4af0d6..be987019c5 100644 --- a/plugins/usbdmx/JaRuleDevice.h +++ b/plugins/usbdmx/JaRuleDevice.h @@ -53,6 +53,10 @@ class JaRuleDevice: public Device { return m_device_id; } + bool AllowMultiPortPatching() const { + return true; + } + protected: bool StartHook(); From 978d14733acba55e8e8b73c3f72c35abf3f99504 Mon Sep 17 00:00:00 2001 From: Jannis Achstetter Date: Tue, 13 Jul 2021 10:12:34 +0200 Subject: [PATCH 11/11] Make the rp2040-dongle detection more flexible --- plugins/usbdmx/DMXCProjectsNodleU1Factory.cpp | 19 +++++-------------- 1 file changed, 5 insertions(+), 14 deletions(-) diff --git a/plugins/usbdmx/DMXCProjectsNodleU1Factory.cpp b/plugins/usbdmx/DMXCProjectsNodleU1Factory.cpp index 001b542856..fa2ac03445 100644 --- a/plugins/usbdmx/DMXCProjectsNodleU1Factory.cpp +++ b/plugins/usbdmx/DMXCProjectsNodleU1Factory.cpp @@ -61,23 +61,14 @@ bool DMXCProjectsNodleU1Factory::DeviceAdded( OLA_INFO << "Nodle U1 serial: " << info.serial; - // Check if it's a RP2040-based widget and if so, how many ins and outs it has - int ret = 0; + // Check if it's a rp2040-dongle that supports multiple universes unsigned int ins = 1; // Input universes unsigned int outs = 1; // Output universes - char variant = 'S'; // Variant: S = simple, R = RDM (not yet implemented) if (info.serial.find('RP2040_') != std::string::npos) { - // Model format: ??Tx ??Rx S - ret = sscanf(info.product.c_str(), "%uTx %uRx %c", &outs, &ins, &variant); - if (ret == 3) { - OLA_INFO << "It's a RP2040-based device with " << ins << " INs and " << - outs << " OUTs"; - } else { - // Reset the values back to their default, just in case - ins = 1; - outs = 1; - variant = 'S'; - } + // The rp2040-dongle supports at most 8 inputs or 16 outputs + // However, inputs are not yet implemented + ins = 0; + outs = 16; } if (m_preferences->SetDefaultValue(