Skip to content

Commit

Permalink
Merge branch 'master' of https://github.com/OpenLightingProject/ola i…
Browse files Browse the repository at this point in the history
…nto trivial-fixes
  • Loading branch information
peternewman committed Nov 28, 2014
2 parents e67b83b + 8bc7854 commit 849dcb5
Show file tree
Hide file tree
Showing 17 changed files with 1,818 additions and 12 deletions.
181 changes: 181 additions & 0 deletions plugins/usbdmx/AnymauDMX.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,181 @@
/*
* 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.
*
* AnymauDMX.cpp
* The synchronous and asynchronous Anyma uDMX widgets.
* Copyright (C) 2014 Simon Newton
*/

#include "plugins/usbdmx/AnymauDMX.h"

#include <unistd.h>
#include <string>

#include "ola/Logging.h"
#include "ola/Constants.h"
#include "plugins/usbdmx/AsyncUsbSender.h"
#include "plugins/usbdmx/LibUsbAdaptor.h"
#include "plugins/usbdmx/ThreadedUsbSender.h"

namespace ola {
namespace plugin {
namespace usbdmx {

using std::string;

namespace {

static const unsigned int URB_TIMEOUT_MS = 500;
static const unsigned int UDMX_SET_CHANNEL_RANGE = 0x0002;

} // namespace

// AnymaThreadedSender
// -----------------------------------------------------------------------------

/*
* Sends messages to a Anyma device in a separate thread.
*/
class AnymaThreadedSender: public ThreadedUsbSender {
public:
AnymaThreadedSender(LibUsbAdaptor *adaptor,
libusb_device *usb_device,
libusb_device_handle *handle)
: ThreadedUsbSender(usb_device, handle),
m_adaptor(adaptor) {
}

private:
LibUsbAdaptor* const m_adaptor;

bool TransmitBuffer(libusb_device_handle *handle,
const DmxBuffer &buffer);
};

bool AnymaThreadedSender::TransmitBuffer(libusb_device_handle *handle,
const DmxBuffer &buffer) {
int r = m_adaptor->ControlTransfer(
handle,
LIBUSB_REQUEST_TYPE_VENDOR | LIBUSB_RECIPIENT_DEVICE |
LIBUSB_ENDPOINT_OUT, // bmRequestType
UDMX_SET_CHANNEL_RANGE, // bRequest
buffer.Size(), // wValue
0, // wIndex
const_cast<unsigned char*>(buffer.GetRaw()), // data
buffer.Size(), // wLength
URB_TIMEOUT_MS); // timeout
// Sometimes we get PIPE errors here, those are non-fatal
return r > 0 || r == LIBUSB_ERROR_PIPE;
}


// SynchronousAnymauDMX
// -----------------------------------------------------------------------------

SynchronousAnymauDMX::SynchronousAnymauDMX(LibUsbAdaptor *adaptor,
libusb_device *usb_device,
const string &serial)
: AnymauDMX(adaptor, serial),
m_usb_device(usb_device) {
}

bool SynchronousAnymauDMX::Init() {
libusb_device_handle *usb_handle;

bool ok = m_adaptor->OpenDeviceAndClaimInterface(
m_usb_device, 0, &usb_handle);
if (!ok) {
return false;
}

std::auto_ptr<AnymaThreadedSender> sender(
new AnymaThreadedSender(m_adaptor, m_usb_device, usb_handle));
if (!sender->Start()) {
return false;
}
m_sender.reset(sender.release());
return true;
}

bool SynchronousAnymauDMX::SendDMX(const DmxBuffer &buffer) {
return m_sender.get() ? m_sender->SendDMX(buffer) : false;
}

// AnymaAsyncUsbSender
// -----------------------------------------------------------------------------
class AnymaAsyncUsbSender : public AsyncUsbSender {
public:
AnymaAsyncUsbSender(LibUsbAdaptor *adaptor, libusb_device *usb_device)
: AsyncUsbSender(adaptor, usb_device) {
m_control_setup_buffer =
new uint8_t[LIBUSB_CONTROL_SETUP_SIZE + DMX_UNIVERSE_SIZE];
}

~AnymaAsyncUsbSender() {
CancelTransfer();
delete[] m_control_setup_buffer;
}

libusb_device_handle* SetupHandle() {
libusb_device_handle *usb_handle;
bool ok = m_adaptor->OpenDeviceAndClaimInterface(
m_usb_device, 0, &usb_handle);
return ok ? usb_handle : NULL;
}

bool PerformTransfer(const DmxBuffer &buffer) {
m_adaptor->FillControlSetup(
m_control_setup_buffer,
LIBUSB_REQUEST_TYPE_VENDOR | LIBUSB_RECIPIENT_DEVICE |
LIBUSB_ENDPOINT_OUT, // bmRequestType
UDMX_SET_CHANNEL_RANGE, // bRequest
buffer.Size(), // wValue
0, // wIndex
buffer.Size()); // wLength

unsigned int length = DMX_UNIVERSE_SIZE;
buffer.Get(m_control_setup_buffer + LIBUSB_CONTROL_SETUP_SIZE, &length);

FillControlTransfer(m_control_setup_buffer, URB_TIMEOUT_MS);
return SubmitTransfer() == 0;
}

private:
uint8_t *m_control_setup_buffer;

DISALLOW_COPY_AND_ASSIGN(AnymaAsyncUsbSender);
};

// AsynchronousAnymauDMX
// -----------------------------------------------------------------------------

AsynchronousAnymauDMX::AsynchronousAnymauDMX(
LibUsbAdaptor *adaptor,
libusb_device *usb_device,
const string &serial)
: AnymauDMX(adaptor, serial) {
m_sender.reset(new AnymaAsyncUsbSender(m_adaptor, usb_device));
}

bool AsynchronousAnymauDMX::Init() {
return m_sender->Init();
}

bool AsynchronousAnymauDMX::SendDMX(const DmxBuffer &buffer) {
return m_sender->SendDMX(buffer);
}
} // namespace usbdmx
} // namespace plugin
} // namespace ola
121 changes: 121 additions & 0 deletions plugins/usbdmx/AnymauDMX.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
/*
* 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.
*
* AnymauDMX.h
* The synchronous and asynchronous Anyma uDMX widgets.
* Copyright (C) 2014 Simon Newton
*/

#ifndef PLUGINS_USBDMX_ANYMAUDMX_H_
#define PLUGINS_USBDMX_ANYMAUDMX_H_

#include <libusb.h>
#include <memory>
#include <string>

#include "ola/DmxBuffer.h"
#include "ola/base/Macro.h"
#include "ola/thread/Mutex.h"
#include "plugins/usbdmx/Widget.h"

namespace ola {
namespace plugin {
namespace usbdmx {

/**
* @brief The base class for Anyma Widgets.
*/
class AnymauDMX: public BaseWidget {
public:
/**
* @brief Create a new AnymauDMX.
* @param adaptor the LibUsbAdaptor to use.
* @param serial the serial number of the widget.
*/
AnymauDMX(LibUsbAdaptor *adaptor,
const std::string &serial)
: BaseWidget(adaptor),
m_serial(serial) {}

virtual ~AnymauDMX() {}

/**
* @brief Get the serial number of this widget.
* @returns The serial number of the widget.
*/
std::string SerialNumber() const {
return m_serial;
}

private:
std::string m_serial;
};

/**
* @brief An Anyma widget that uses synchronous libusb operations.
*
* Internally this spawns a new thread to avoid blocking SendDMX() calls.
*/
class SynchronousAnymauDMX: public AnymauDMX {
public:
/**
* @brief Create a new SynchronousAnymauDMX.
* @param adaptor the LibUsbAdaptor to use.
* @param usb_device the libusb_device to use for the widget.
* @param serial the serial number of the widget.
*/
SynchronousAnymauDMX(LibUsbAdaptor *adaptor,
libusb_device *usb_device,
const std::string &serial);

bool Init();

bool SendDMX(const DmxBuffer &buffer);

private:
libusb_device* const m_usb_device;
std::auto_ptr<class AnymaThreadedSender> m_sender;

DISALLOW_COPY_AND_ASSIGN(SynchronousAnymauDMX);
};

/**
* @brief An Anyma widget that uses asynchronous libusb operations.
*/
class AsynchronousAnymauDMX : public AnymauDMX {
public:
/**
* @brief Create a new AsynchronousAnymauDMX.
* @param adaptor the LibUsbAdaptor to use.
* @param usb_device the libusb_device to use for the widget.
* @param serial the serial number of the widget.
*/
AsynchronousAnymauDMX(LibUsbAdaptor *adaptor,
libusb_device *usb_device,
const std::string &serial);

bool Init();

bool SendDMX(const DmxBuffer &buffer);

private:
std::auto_ptr<class AnymaAsyncUsbSender> m_sender;

DISALLOW_COPY_AND_ASSIGN(AsynchronousAnymauDMX);
};
} // namespace usbdmx
} // namespace plugin
} // namespace ola
#endif // PLUGINS_USBDMX_ANYMAUDMX_H_
88 changes: 88 additions & 0 deletions plugins/usbdmx/AnymauDMXFactory.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
/*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Library General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*
* AnymauDMXFactory.cpp
* The factory for Anyma uDMX widgets.
* Copyright (C) 2014 Simon Newton
*/

#include "plugins/usbdmx/AnymauDMXFactory.h"

#include "ola/Logging.h"
#include "ola/base/Flags.h"
#include "plugins/usbdmx/AnymauDMX.h"
#include "plugins/usbdmx/LibUsbAdaptor.h"

DECLARE_bool(use_async_libusb);

namespace ola {
namespace plugin {
namespace usbdmx {

const char AnymauDMXFactory::EXPECTED_MANUFACTURER[] = "www.anyma.ch";
const char AnymauDMXFactory::EXPECTED_PRODUCT[] = "uDMX";
const uint16_t AnymauDMXFactory::PRODUCT_ID = 0x05DC;
const uint16_t AnymauDMXFactory::VENDOR_ID = 0x16C0;

bool AnymauDMXFactory::DeviceAdded(
WidgetObserver *observer,
libusb_device *usb_device,
const struct libusb_device_descriptor &descriptor) {
if (descriptor.idVendor != VENDOR_ID || descriptor.idProduct != PRODUCT_ID ||
HasDevice(usb_device)) {
return false;
}

OLA_INFO << "Found a new Anyma device";
LibUsbAdaptor::DeviceInformation info;
if (!m_adaptor->GetDeviceInfo(usb_device, descriptor, &info)) {
return false;
}

if (!m_adaptor->CheckManufacturer(EXPECTED_MANUFACTURER, info)) {
return false;
}

if (!m_adaptor->CheckProduct(EXPECTED_PRODUCT, info)) {
return false;
}

// Some Anyma devices don't have serial numbers. Since there isn't another
// good way to uniquely identify a USB device, we only support one of these
// types of devices per host.
if (info.serial.empty()) {
if (m_missing_serial_number) {
OLA_WARN << "Failed to read serial number or serial number empty. "
<< "We can only support one device without a serial number.";
return false;
} else {
OLA_WARN << "Failed to read serial number from " << info.manufacturer
<< " : " << info.product
<< " the device probably doesn't have one";
m_missing_serial_number = true;
}
}

AnymauDMX *widget = NULL;
if (FLAGS_use_async_libusb) {
widget = new AsynchronousAnymauDMX(m_adaptor, usb_device, info.serial);
} else {
widget = new SynchronousAnymauDMX(m_adaptor, usb_device, info.serial);
}
return AddWidget(observer, usb_device, widget);
}
} // namespace usbdmx
} // namespace plugin
} // namespace ola
Loading

0 comments on commit 849dcb5

Please sign in to comment.