Skip to content

Commit

Permalink
Merge 51d0e43 into 2620e9c
Browse files Browse the repository at this point in the history
  • Loading branch information
nomis52 committed Nov 25, 2014
2 parents 2620e9c + 51d0e43 commit 8876440
Show file tree
Hide file tree
Showing 5 changed files with 523 additions and 0 deletions.
158 changes: 158 additions & 0 deletions plugins/usbdmx/AsyncUsbSender.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,158 @@
/*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Library General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*
* AsyncUsbSender.cpp
* A Asynchronous DMX USB sender.
* Copyright (C) 2014 Simon Newton
*/

#include "plugins/usbdmx/AsyncUsbSender.h"

#include "ola/Logging.h"
#include "plugins/usbdmx/LibUsbAdaptor.h"

namespace ola {
namespace plugin {
namespace usbdmx {

namespace {

/*
* Called by libusb when the transfer completes.
*/
void AsyncCallback(struct libusb_transfer *transfer) {
AsyncUsbSender *widget = reinterpret_cast<AsyncUsbSender*>(
transfer->user_data);
widget->TransferComplete(transfer);
}
} // namespace

AsyncUsbSender::AsyncUsbSender(LibUsbAdaptor *adaptor,
libusb_device *usb_device)
: m_adaptor(adaptor),
m_usb_device(usb_device),
m_usb_handle(NULL),
m_transfer_state(IDLE),
m_pending_tx(false) {
m_transfer = m_adaptor->AllocTransfer(0);
m_adaptor->RefDevice(usb_device);
}

AsyncUsbSender::~AsyncUsbSender() {
CancelTransfer();
m_adaptor->Close(m_usb_handle);
m_adaptor->UnrefDevice(m_usb_device);
}

bool AsyncUsbSender::Init() {
m_usb_handle = SetupHandle();
return m_usb_handle ? true : false;
}

bool AsyncUsbSender::SendDMX(const DmxBuffer &buffer) {
if (!m_usb_handle) {
OLA_WARN << "AsynchronousAnymaWidget hasn't been initialized";
return false;
}
ola::thread::MutexLocker locker(&m_mutex);
if (m_transfer_state == IDLE) {
PerformTransfer(buffer);
} else {
m_pending_tx = true;
m_tx_buffer.Set(buffer);
}
return true;
}

void AsyncUsbSender::CancelTransfer() {
if (!m_transfer) {
return;
}

bool canceled = false;
while (1) {
ola::thread::MutexLocker locker(&m_mutex);
if (m_transfer_state == IDLE || m_transfer_state == DISCONNECTED) {
break;
}
if (!canceled) {
m_adaptor->CancelTransfer(m_transfer);
canceled = true;
}
}

m_adaptor->FreeTransfer(m_transfer);
m_transfer = NULL;
}

void AsyncUsbSender::FillControlTransfer(unsigned char *buffer,
unsigned int timeout) {
m_adaptor->FillControlTransfer(m_transfer, m_usb_handle, buffer,
&AsyncCallback, this, timeout);
}

void AsyncUsbSender::FillBulkTransfer(unsigned char endpoint,
unsigned char *buffer,
int length,
unsigned int timeout) {
m_adaptor->FillBulkTransfer(m_transfer, m_usb_handle, endpoint, buffer,
length, &AsyncCallback, this, timeout);
}

void AsyncUsbSender::FillInterruptTransfer(unsigned char endpoint,
unsigned char *buffer,
int length,
unsigned int timeout) {
m_adaptor->FillInterruptTransfer(m_transfer, m_usb_handle, endpoint, buffer,
length, &AsyncCallback, this, timeout);
}

int AsyncUsbSender::SubmitTransfer() {
int ret = m_adaptor->SubmitTransfer(m_transfer);
if (ret) {
OLA_WARN << "libusb_submit_transfer returned " << libusb_error_name(ret);
if (ret == LIBUSB_ERROR_NO_DEVICE) {
m_transfer_state = DISCONNECTED;
}
return false;
}
m_transfer_state = IN_PROGRESS;
return ret;
}

void AsyncUsbSender::TransferComplete(struct libusb_transfer *transfer) {
if (transfer != m_transfer) {
OLA_WARN << "Mismatched libusb transfer: " << transfer << " != "
<< m_transfer;
return;
}

if (transfer->status != LIBUSB_TRANSFER_COMPLETED) {
OLA_WARN << "Transfer returned " << transfer->status;
}

ola::thread::MutexLocker locker(&m_mutex);
m_transfer_state = transfer->status == LIBUSB_TRANSFER_NO_DEVICE ?
DISCONNECTED : IDLE;
PostTransferHook();

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

#ifndef PLUGINS_USBDMX_ASYNCUSBSENDER_H_
#define PLUGINS_USBDMX_ASYNCUSBSENDER_H_

#include <libusb.h>

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

namespace ola {
namespace plugin {
namespace usbdmx {

/**
* @brief A base class that send DMX data asynchronously.
*
* This encapsulates much of the asynchronous libusb logic. Subclasses should
* implement the SetupHandle() and PerformTransfer() methods.
*/
class AsyncUsbSender {
public:
/**
* @brief Create a new AsynchronousAnymaWidget.
* @param adaptor the LibUsbAdaptor to use.
* @param usb_device the libusb_device to use for the widget.
*/
AsyncUsbSender(class LibUsbAdaptor* const adaptor,
libusb_device *usb_device);

/**
* @brief Destructor
*/
virtual ~AsyncUsbSender();

/**
* @brief Initialize the sender.
* @returns true if SetupHandle() returned a valid handle, false otherwise.
*/
bool Init();

/**
* @brief Send on frame of DMX data.
* @param buffer the DMX data to send.
* @returns the value of PerformTransfer().
*/
bool SendDMX(const DmxBuffer &buffer);

/**
* @brief Called from the libusb callback when the asynchronous transfer
* completes.
* @param transfer the completed transfer.
*/
void TransferComplete(struct libusb_transfer *transfer);

protected:
/**
* @brief The LibUsbAdaptor passed in the constructor.
*/
class LibUsbAdaptor* const m_adaptor;

/**
* @brief The libusb_device passed in the constructor.
*/
libusb_device* const m_usb_device;

/**
* @brief Open the device handle.
* @returns A valid libusb_device_handle or NULL if the device could not be
* opened.
*/
virtual libusb_device_handle* SetupHandle() = 0;

/**
* @brief Perform the DMX transfer.
* @param buffer the DMX buffer to send.
* @returns true if the transfer was scheduled, false otherwise.
*
* This method is implemented by the subclass. The subclass should call
* FillControlTransfer() / FillBulkTransfer() as appropriate and then call
* SubmitTransfer().
*/
virtual bool PerformTransfer(const DmxBuffer &buffer) = 0;


virtual void PostTransferHook() {}

/**
* @brief Cancel any pending transfers.
*/
void CancelTransfer();

/**
* @brief Fill a control transfer.
* @param buffer passed to libusb_fill_control_transfer.
* @param timeout passed to libusb_fill_control_transfer.
*/
void FillControlTransfer(unsigned char *buffer, unsigned int timeout);

/**
* @brief Fill a bulk transfer.
*/
void FillBulkTransfer(unsigned char endpoint, unsigned char *buffer,
int length, unsigned int timeout);

/**
* @brief Fill a interrupt transfer.
*/
void FillInterruptTransfer(unsigned char endpoint, unsigned char *buffer,
int length, unsigned int timeout);

/**
* @brief Submit the transfer for tx.
* @returns the result of libusb_submit_transfer().
*/
int SubmitTransfer();

protected:
bool TransferPending() const { return m_pending_tx; }

private:
enum TransferState {
IDLE,
IN_PROGRESS,
DISCONNECTED,
};

libusb_device_handle *m_usb_handle;
struct libusb_transfer *m_transfer;

TransferState m_transfer_state; // GUARDED_BY(m_mutex);
DmxBuffer m_tx_buffer; // GUARDED_BY(m_mutex);
bool m_pending_tx; // GUARDED_BY(m_mutex);
ola::thread::Mutex m_mutex;

DISALLOW_COPY_AND_ASSIGN(AsyncUsbSender);
};
} // namespace usbdmx
} // namespace plugin
} // namespace ola
#endif // PLUGINS_USBDMX_ASYNCUSBSENDER_H_
4 changes: 4 additions & 0 deletions plugins/usbdmx/Makefile.mk
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,14 @@
if USE_LIBUSB
noinst_LTLIBRARIES += plugins/usbdmx/libolausbdmxwidget.la
plugins_usbdmx_libolausbdmxwidget_la_SOURCES = \
plugins/usbdmx/AsyncUsbSender.cpp \
plugins/usbdmx/AsyncUsbSender.h \
plugins/usbdmx/LibUsbAdaptor.cpp \
plugins/usbdmx/LibUsbAdaptor.h \
plugins/usbdmx/LibUsbThread.cpp \
plugins/usbdmx/LibUsbThread.h \
plugins/usbdmx/ThreadedUsbSender.cpp \
plugins/usbdmx/ThreadedUsbSender.h \
plugins/usbdmx/Widget.h \
plugins/usbdmx/WidgetFactory.h
plugins_usbdmx_libolausbdmxwidget_la_CXXFLAGS = \
Expand Down
Loading

0 comments on commit 8876440

Please sign in to comment.