-
Notifications
You must be signed in to change notification settings - Fork 202
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
5 changed files
with
523 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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_ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.