Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 5 additions & 3 deletions plugins/usbdmx/AnymauDMX.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}

Expand All @@ -135,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 |
Expand Down Expand Up @@ -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
Expand Down
4 changes: 2 additions & 2 deletions plugins/usbdmx/AnymauDMX.h
Original file line number Diff line number Diff line change
Expand Up @@ -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<class AnymaThreadedSender> m_sender;
Expand All @@ -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<class AnymaAsyncUsbSender> m_sender;
Expand Down
21 changes: 12 additions & 9 deletions plugins/usbdmx/AsyncUsbSender.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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)) {

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

beware: untested code block. I didn't yet get the USB device to "stall" = to have outstanding transfers at this point.

m_tx_buffers[portId].Set(buffer);
} else {
m_tx_buffers.insert(std::pair<unsigned int, DmxBuffer>(portId, buffer));
}
}
return true;
}
Expand All @@ -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<unsigned int, DmxBuffer>::iterator it = m_tx_buffers.begin();

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

not sure if using "the first" entry in the map is the best one to pick here

PerformTransfer(it->second, it->first);
m_tx_buffers.erase(it->first);
}
}
} // namespace usbdmx
Expand Down
11 changes: 6 additions & 5 deletions plugins/usbdmx/AsyncUsbSender.h
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
#define PLUGINS_USBDMX_ASYNCUSBSENDER_H_

#include <libusb.h>
#include <map>

#include "AsyncUsbTransceiverBase.h"
#include "libs/usb/LibUsbAdaptor.h"
Expand Down Expand Up @@ -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
Expand All @@ -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.
Expand All @@ -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<unsigned int, DmxBuffer> m_tx_buffers; // GUARDED_BY(m_mutex);

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure if a map is the most suitable pick here


DISALLOW_COPY_AND_ASSIGN(AsyncUsbSender);
};
Expand Down
135 changes: 101 additions & 34 deletions plugins/usbdmx/DMXCProjectsNodleU1.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't know why the Nodle U1 (and clones) are limited to 32 bytes. USB and HID both allow 64 bytes (for bulk transfers of full speed devices). Maybe it's a limitation of Nodle's hardware?
Since the RP2040 doesn't have this limitation, we can send max 64 byte at once


/*
* @brief Send chosen mode to the DMX device
Expand Down Expand Up @@ -104,13 +105,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)) {
Expand Down Expand Up @@ -263,12 +282,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);

Expand Down Expand Up @@ -301,7 +327,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 {
Expand Down Expand Up @@ -402,7 +429,7 @@ class DMXCProjectsNodleU1AsyncUsbSender : public AsyncUsbSender {
return handle;
}

bool PerformTransfer(const DmxBuffer &buffer);
bool PerformTransfer(const DmxBuffer &buffer, unsigned int portId = 0);

void PostTransferHook();

Expand All @@ -412,24 +439,26 @@ 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);

bool SendChunk() {
bool SendChunk(unsigned int size) {
FillInterruptTransfer(WRITE_ENDPOINT, m_packet,
DATABLOCK_SIZE, URB_TIMEOUT_MS);
size, URB_TIMEOUT_MS);
return (SubmitTransfer() == 0);
}

DISALLOW_COPY_AND_ASSIGN(DMXCProjectsNodleU1AsyncUsbSender);
};

bool DMXCProjectsNodleU1AsyncUsbSender::PerformTransfer(
const DmxBuffer &buffer) {
const DmxBuffer &buffer, unsigned int portId) {
if (m_buffer_offset == 0) {
m_portId = portId;
return SendInitialChunk(buffer);
}
// Otherwise we're part way through a transfer, do nothing.
Expand All @@ -452,32 +481,61 @@ void DMXCProjectsNodleU1AsyncUsbSender::PostTransferHook() {
}

bool DMXCProjectsNodleU1AsyncUsbSender::ContinueTransfer() {
unsigned int length = 32;
if (!m_portId) {
unsigned int length = 32;

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the "legacy" protocol (sending 32 byte long blocks) is used for universe "0", even if the more advanced protocol could be used instead


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 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;

unsigned int slots_sent = length;
if (slots_sent < m_tx_buffer.Size()) {
// There are more frames to send.
m_buffer_offset = slots_sent;
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;
}
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
Expand All @@ -488,8 +546,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));
Expand All @@ -505,6 +568,9 @@ AsynchronousDMXCProjectsNodleU1::AsynchronousDMXCProjectsNodleU1(

bool AsynchronousDMXCProjectsNodleU1::Init() {
bool ok = true;

OLA_DEBUG << "AsynchronousDMXCProjectsNodleU1 INIT";

if (m_sender.get()) {
ok &= m_sender->Init();
}
Expand All @@ -522,8 +588,9 @@ bool AsynchronousDMXCProjectsNodleU1::Init() {
return ok;
}

bool AsynchronousDMXCProjectsNodleU1::SendDMX(const DmxBuffer &buffer) {
return m_sender.get() ? m_sender->SendDMX(buffer) : false;
bool AsynchronousDMXCProjectsNodleU1::SendDMX(const DmxBuffer &buffer,
unsigned int portId) {
return m_sender.get() ? m_sender->SendDMX(buffer, portId) : false;
}

void AsynchronousDMXCProjectsNodleU1::SetDmxCallback(
Expand Down
Loading