aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorThomas Tsou <ttsou@vt.edu>2010-08-13 13:40:13 -0700
committerThomas Tsou <ttsou@vt.edu>2010-08-13 17:54:33 -0700
commit86c86ede9775824411262e36f104489bcd171e21 (patch)
treee213ee2e4cc8ca62689eada3690d0e6eb8509dfd
parent38746242eab47845c1b1b6cb264c3e3041977488 (diff)
downloaduhd-86c86ede9775824411262e36f104489bcd171e21.tar.gz
uhd-86c86ede9775824411262e36f104489bcd171e21.tar.bz2
uhd-86c86ede9775824411262e36f104489bcd171e21.zip
usrp1: Add libusb-1.0 implementations of USB interfaces
-rw-r--r--host/lib/transport/libusb1_control.cpp238
-rw-r--r--host/lib/transport/libusb1_zero_copy.cpp854
2 files changed, 1092 insertions, 0 deletions
diff --git a/host/lib/transport/libusb1_control.cpp b/host/lib/transport/libusb1_control.cpp
new file mode 100644
index 000000000..2903d943d
--- /dev/null
+++ b/host/lib/transport/libusb1_control.cpp
@@ -0,0 +1,238 @@
+//
+// Copyright 2010 Ettus Research LLC
+//
+// 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 3 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 General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see <http://www.gnu.org/licenses/>.
+//
+
+#include <uhd/types/usb_descriptor.hpp>
+#include <uhd/utils/exception.hpp>
+#include <uhd/transport/usb_control.hpp>
+#include <libusb-1.0/libusb.h>
+#include <boost/asio.hpp>
+#include <iostream>
+
+using namespace uhd::transport;
+
+static int libusb_debug_level = 0;
+static int libusb_timeout = 0;
+
+/***********************************************************************
+ * libusb-1.0 implementation of USB control transport
+ **********************************************************************/
+class libusb_control_impl : public usb_control {
+public:
+ libusb_control_impl(uhd::usb_descriptor_t descriptor);
+ ~libusb_control_impl();
+
+ size_t submit(boost::uint8_t request_type,
+ boost::uint8_t request,
+ boost::uint16_t value,
+ boost::uint16_t index,
+ unsigned char *buff,
+ boost::uint16_t length);
+
+ static uhd::usb_descriptor_t create_descriptor(libusb_device *dev);
+ static std::string get_serial(libusb_device *dev);
+
+private:
+ uhd::usb_descriptor_t _descriptor;
+
+ libusb_context *_ctx;
+ libusb_device_handle *_dev_handle;
+
+ bool open_device();
+ bool open_interface();
+ bool compare_device(libusb_device *dev, uhd::usb_descriptor_t descriptor);
+};
+
+
+libusb_control_impl::libusb_control_impl(uhd::usb_descriptor_t descriptor)
+ : _descriptor(descriptor), _ctx(NULL), _dev_handle(NULL)
+{
+ if (libusb_init(&_ctx) < 0)
+ UHD_THROW_SITE_INFO("USB: failed to initialize libusb");
+
+ libusb_set_debug(_ctx, libusb_debug_level);
+
+ if (!open_device())
+ UHD_THROW_SITE_INFO("USB: failed to open device");
+
+ if (!open_interface())
+ UHD_THROW_SITE_INFO("USB: failed to open device interface");
+}
+
+
+libusb_control_impl::~libusb_control_impl()
+{
+ libusb_close(_dev_handle);
+ libusb_exit(_ctx);
+}
+
+
+uhd::usb_descriptor_t libusb_control_impl::create_descriptor(libusb_device *dev)
+{
+ libusb_device_descriptor desc;
+
+ if (libusb_get_device_descriptor(dev, &desc) < 0)
+ UHD_THROW_SITE_INFO("USB: failed to get device descriptor");
+
+ uhd::usb_descriptor_t descriptor;
+
+ descriptor.serial = get_serial(dev);
+ descriptor.product_id = desc.idProduct;
+ descriptor.vendor_id = desc.idVendor;
+ descriptor.device_addr = libusb_get_device_address(dev);
+
+ return descriptor;
+}
+
+
+std::string libusb_control_impl::get_serial(libusb_device *dev)
+{
+ unsigned char buff[32];
+
+ libusb_device_descriptor desc;
+ if (libusb_get_device_descriptor(dev, &desc) < 0)
+ return "";
+
+ if (desc.iSerialNumber == 0)
+ return "";
+
+ //open the device because we have to
+ libusb_device_handle *dev_handle;
+ if (libusb_open(dev, &dev_handle) < 0)
+ return "";
+
+ if (libusb_get_string_descriptor_ascii(dev_handle, desc.iSerialNumber,
+ buff, sizeof(buff)) < 0) {
+ return "";
+ }
+
+ libusb_close(dev_handle);
+
+ return (char*) buff;
+}
+
+
+bool libusb_control_impl::open_device()
+{
+ libusb_device **list;
+ libusb_device *dev;
+
+ ssize_t cnt = libusb_get_device_list(_ctx, &list);
+
+ if (cnt < 0)
+ return cnt;
+
+ ssize_t i = 0;
+ for (i = 0; i < cnt; i++) {
+ dev = list[i];
+ if (compare_device(dev, _descriptor))
+ goto found;
+ }
+ return false;
+
+found:
+ int ret;
+ if ((ret = libusb_open(dev, &_dev_handle)) < 0)
+ return false;
+ else
+ return true;
+}
+
+
+bool libusb_control_impl::compare_device(libusb_device *dev,
+ uhd::usb_descriptor_t descriptor)
+{
+ std::string serial = descriptor.serial;
+ boost::uint16_t vendor_id = descriptor.vendor_id;
+ boost::uint16_t product_id = descriptor.product_id;
+ boost::uint8_t device_addr = descriptor.device_addr;
+
+ libusb_device_descriptor libusb_desc;
+ if (libusb_get_device_descriptor(dev, &libusb_desc) < 0)
+ return false;
+ if (serial != get_serial(dev))
+ return false;
+ if (vendor_id != libusb_desc.idVendor)
+ return false;
+ if (product_id != libusb_desc.idProduct)
+ return false;
+ if (device_addr != libusb_get_device_address(dev))
+ return false;
+
+ return true;
+}
+
+
+bool libusb_control_impl::open_interface()
+{
+ if (libusb_claim_interface(_dev_handle, 0) < 0)
+ return false;
+ else
+ return true;
+}
+
+
+size_t libusb_control_impl::submit(boost::uint8_t request_type,
+ boost::uint8_t request,
+ boost::uint16_t value,
+ boost::uint16_t index,
+ unsigned char *buff,
+ boost::uint16_t length)
+{
+ return libusb_control_transfer(_dev_handle,
+ request_type,
+ request,
+ value,
+ index,
+ buff,
+ length,
+ libusb_timeout);
+}
+
+
+/***********************************************************************
+ * USB control public make functions
+ **********************************************************************/
+usb_control::sptr usb_control::make(uhd::usb_descriptor_t descriptor)
+{
+ return sptr(new libusb_control_impl(descriptor));
+}
+
+uhd::usb_descriptors_t usb_control::get_device_list()
+{
+ libusb_device **list;
+ uhd::usb_descriptors_t descriptors;
+
+ if (libusb_init(NULL) < 0)
+ UHD_THROW_SITE_INFO("USB: failed to initialize libusb");
+
+ ssize_t cnt = libusb_get_device_list(NULL, &list);
+
+ if (cnt < 0)
+ UHD_THROW_SITE_INFO("USB: failed to get device list");
+
+ ssize_t i = 0;
+ for (i = 0; i < cnt; i++) {
+ libusb_device *dev = list[i];
+ descriptors.push_back(libusb_control_impl::create_descriptor(dev));
+ }
+
+ libusb_free_device_list(list, 0);
+ libusb_exit(NULL);
+ return descriptors;
+}
+
+
diff --git a/host/lib/transport/libusb1_zero_copy.cpp b/host/lib/transport/libusb1_zero_copy.cpp
new file mode 100644
index 000000000..39617e4dd
--- /dev/null
+++ b/host/lib/transport/libusb1_zero_copy.cpp
@@ -0,0 +1,854 @@
+//
+// Copyright 2010 Ettus Research LLC
+//
+// 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 3 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 General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see <http://www.gnu.org/licenses/>.
+//
+
+
+#include <uhd/transport/usb_zero_copy.hpp>
+#include <uhd/utils/assert.hpp>
+#include <libusb-1.0/libusb.h>
+#include <boost/asio.hpp>
+#include <boost/format.hpp>
+#include <iostream>
+#include <iomanip>
+
+using namespace uhd::transport;
+
+static int libusb_debug_level = 3;
+
+/***********************************************************************
+ * Helper functions
+ *
+ * Print to stdout the values of a libusb_transfer struct
+ ***********************************************************************/
+void pp_transfer(libusb_transfer *lut)
+{
+ std::cout << "Libusb transfer" << std::endl;
+ std::cout << " flags: 0x" << std::hex << (unsigned int) lut->flags << std::endl;
+ std::cout << " endpoint: 0x" << std::hex << (unsigned int) lut->endpoint << std::endl;
+ std::cout << " type: 0x" << std::hex << (unsigned int) lut->type << std::endl;
+ std::cout << " timeout: " << std::dec << lut->timeout << std::endl;
+ std::cout << " status: 0x" << std::hex << lut->status << std::endl;
+ std::cout << " length: " << std::dec << lut->length << std::endl;
+ std::cout << " actual_length: " << std::dec << lut->actual_length << std::endl;
+}
+
+/***********************************************************************
+ * USB asynchronous phony zero_copy endpoint
+ * This endpoint implementation provides asynchronous I/O to libusb-1.0
+ * devices. Each endpoint is directional and two can be combined to
+ * create a bidirectional interface. It is a zero copy implementation
+ * with respect to libusb, however, each send and recv requires a copy
+ * operation from kernel to userspace; this is due to the usbfs
+ * interface provided by the kernel.
+ **********************************************************************/
+class usb_endpoint {
+private:
+ libusb_device_handle *_dev_handle;
+ libusb_context *_ctx;
+ int _endpoint;
+ bool _input;
+
+ size_t _transfer_size;
+ size_t _num_transfers;
+
+ /*
+ * Transfer state lists (free, pending, or completed)
+ */
+ std::list<libusb_transfer *> _free_list;
+ std::list<libusb_transfer *> _pending_list;
+ std::list<libusb_transfer *> _completed_list;
+
+ /*
+ * Calls for processing asynchronous I/O
+ */
+ libusb_transfer *allocate_transfer(int buff_len);
+ bool cancel(libusb_transfer *lut);
+ bool cancel_all();
+ bool reap_pending_list();
+ bool reap_pending_list_timeout();
+ bool reap_completed_list();
+
+ /*
+ * Transfer state manipulators
+ */
+ void free_list_add(libusb_transfer *lut);
+ void pending_list_add(libusb_transfer *lut);
+ void completed_list_add(libusb_transfer *lut);
+ libusb_transfer *free_list_get();
+ libusb_transfer *completed_list_get();
+ bool pending_list_remove(libusb_transfer *lut);
+
+ /*
+ * Misc
+ */
+ void print_transfer_status(libusb_transfer *lut);
+
+public:
+ usb_endpoint(libusb_device_handle *dev_handle,
+ libusb_context *ctx, int endpoint, bool input,
+ size_t transfer_size, size_t num_transfers);
+
+ ~usb_endpoint();
+
+ /*
+ * Accessors
+ */
+ int get_endpoint() const { return _endpoint; }
+ bool get_direction() const { return _input; }
+ libusb_device_handle *get_dev_handle() const { return _dev_handle; }
+ libusb_context *get_ctx() const { return _ctx; }
+
+ /*
+ * Exposed interface for submitting / retrieving transfer buffers
+ * used in zero-copy interface
+ */
+ bool submit(libusb_transfer *lut);
+ libusb_transfer *get_completed_transfer();
+ libusb_transfer *get_free_transfer();
+
+ /*
+ * Callback use only
+ */
+ void callback_handle_transfer(libusb_transfer *lut);
+};
+
+
+/*
+ * Callback function called when submitted transfers complete.
+ * The endpoint upon which the transfer is part of is recovered
+ * and the transfer moved from pending to completed state.
+ */
+static void callback(libusb_transfer *lut)
+{
+ usb_endpoint *endpoint = (usb_endpoint *) lut->user_data;
+ endpoint->callback_handle_transfer(lut);
+}
+
+
+/*
+ * Accessor call to allow list access from callback space
+ */
+void usb_endpoint::callback_handle_transfer(libusb_transfer *lut)
+{
+ if (!pending_list_remove(lut)) {
+ std::cerr << "USB: pending remove failed" << std::endl;
+ return;
+ }
+
+ completed_list_add(lut);
+}
+
+
+/*
+ * Constructor
+ *
+ * Allocate libusb transfers. For IN endpoints, submit the transfers
+ * so that they're ready to return when data is available.
+ */
+usb_endpoint::usb_endpoint(libusb_device_handle *dev_handle,
+ libusb_context *ctx, int endpoint, bool input,
+ size_t transfer_size, size_t num_transfers)
+ : _dev_handle(dev_handle),
+ _ctx(ctx), _endpoint(endpoint), _input(input),
+ _transfer_size(transfer_size), _num_transfers(num_transfers)
+{
+ unsigned int i;
+ for (i = 0; i < _num_transfers; i++) {
+ free_list_add(allocate_transfer(_transfer_size));
+
+ if (_input)
+ submit(free_list_get());
+ }
+}
+
+
+/*
+ * Destructor
+ */
+usb_endpoint::~usb_endpoint()
+{
+ cancel_all();
+
+ while (!_pending_list.empty()) {
+ if (!reap_pending_list())
+ std::cerr << "error: destructor failed to reap" << std::endl;
+ }
+
+ while (!_completed_list.empty()) {
+ if (!reap_completed_list())
+ std::cerr << "error: destructor failed to reap" << std::endl;
+ }
+
+ while (!_free_list.empty()) {
+ libusb_free_transfer(free_list_get());
+ }
+}
+
+
+/*
+ * Allocate a libusb transfer
+ *
+ * The allocated transfer is continuously reused and should be freed at
+ * shutdown.
+ */
+libusb_transfer *usb_endpoint::allocate_transfer(int buff_len)
+{
+ libusb_transfer *lut = libusb_alloc_transfer(0);
+
+ unsigned char *buff = new unsigned char[buff_len];
+
+ unsigned int endpoint = ((_endpoint & 0x7f) | (_input ? 0x80 : 0));
+
+ libusb_fill_bulk_transfer(lut, // transfer
+ _dev_handle, // dev_handle
+ endpoint, // endpoint
+ buff, // buffer
+ buff_len, // length
+ callback, // callback
+ this, // user_data
+ 0); // timeout
+ return lut;
+}
+
+
+/*
+ * Asynchonous transfer submission
+ *
+ * Submit and mark transfer as pending.
+ */
+bool usb_endpoint::submit(libusb_transfer *lut)
+{
+ int retval;
+ if ((retval = libusb_submit_transfer(lut)) < 0) {
+ std::cerr << "error: libusb_submit_transfer: " << retval << std::endl;
+ return false;
+ }
+
+ pending_list_add(lut);
+ return true;
+}
+
+
+/*
+ * Cancel a pending transfer
+ *
+ * Search the pending list for the transfer and cancel if found.
+ * Returns true on success. False otherwise or on error.
+ *
+ * Note: success only indicates submission of cancelation request.
+ * Sucessful cancelation is not known until the callback occurs.
+ */
+bool usb_endpoint::cancel(libusb_transfer *lut)
+{
+ std::list<libusb_transfer*>::iterator iter;
+ for (iter = _pending_list.begin(); iter != _pending_list.end(); iter++) {
+ if (*iter == lut) {
+ libusb_cancel_transfer(lut);
+ return true;
+ }
+ }
+ return false;
+}
+
+
+/*
+ * Cancel all pending transfers
+ *
+ * Note: success only indicates submission of cancelation request.
+ * Sucessful cancelation is not known until the callback occurs.
+ */
+bool usb_endpoint::cancel_all()
+{
+ std::list<libusb_transfer*>::iterator iter;
+
+ for (iter = _pending_list.begin(); iter != _pending_list.end(); iter++) {
+ if (libusb_cancel_transfer(*iter) < 0) {
+ std::cerr << "error: libusb_cancal_transfer() failed" << std::endl;
+ return false;
+ }
+ }
+
+ return true;
+}
+
+
+/*
+ * Reap completed transfers
+ *
+ * return true if at least one transfer was reaped, false otherwise.
+ *
+ * Check completed transfers for errors and mark as free. This is a
+ * blocking call.
+ */
+bool usb_endpoint::reap_completed_list()
+{
+ libusb_transfer *lut;
+
+ if (_completed_list.empty()) {
+ if (!reap_pending_list_timeout())
+ return false;
+ }
+
+ while (!_completed_list.empty()) {
+ lut = completed_list_get();
+ print_transfer_status(lut);
+ free_list_add(lut);
+ }
+
+ return true;
+}
+
+
+/*
+ * Print completed transfer status error(s)
+ *
+ * return true if at least one transfer was reaped, false otherwise.
+ *
+ * Check completed transfers for errors and mark as free. This is a
+ * blocking call.
+ */
+void usb_endpoint::print_transfer_status(libusb_transfer *lut)
+{
+ switch (lut->status) {
+ case LIBUSB_TRANSFER_COMPLETED:
+ if (lut->actual_length < lut->length) {
+ std::cerr << "USB: transfer completed with short write,"
+ << " length = " << lut->length
+ << " actual = " << lut->actual_length << std::endl;
+ }
+
+ if ((lut->actual_length < 0) || (lut->length < 0)) {
+ std::cerr << "USB: transfer completed with invalid response"
+ << std::endl;
+ }
+ break;
+ case LIBUSB_TRANSFER_CANCELLED:
+ break;
+ case LIBUSB_TRANSFER_NO_DEVICE:
+ std::cerr << "USB: device was disconnected" << std::endl;
+ break;
+ case LIBUSB_TRANSFER_OVERFLOW:
+ std::cerr << "USB: device sent more data than requested" << std::endl;
+ break;
+ case LIBUSB_TRANSFER_TIMED_OUT:
+ std::cerr << "USB: transfer timed out" << std::endl;
+ break;
+ case LIBUSB_TRANSFER_STALL:
+ std::cerr << "USB: halt condition detected (endpoint stalled)" << std::endl;
+ break;
+ case LIBUSB_TRANSFER_ERROR:
+ std::cerr << "USB: transfer failed" << std::endl;
+ break;
+ default:
+ std::cerr << "USB: received unknown transfer status" << std::endl;
+ }
+}
+
+
+/*
+ * Reap pending transfers
+ *
+ * Return true if at least one transfer was reaped, false otherwise. This is
+ * a blocking call.
+ *
+ * Reaping submitted transfers is handled by libusb and the assigned callback
+ * function. Block until at least one transfer is reaped.
+ */
+bool usb_endpoint::reap_pending_list()
+{
+ int retval;
+
+ if ((retval = libusb_handle_events(_ctx)) < 0) {
+ std::cerr << "error: libusb_handle_events: " << retval << std::endl;
+ return false;
+ }
+
+ return true;
+}
+
+
+/*
+ * Reap pending transfers with timeout
+ *
+ * Return true if at least one transfer was reaped, false otherwise. This call
+ * blocks until a transfer is reaped or timeout.
+ *
+ * Reaping submitted transfers is handled by libusb and the assigned callback
+ * function. Block until at least one transfer is reaped or timeout occurs.
+ */
+bool usb_endpoint::reap_pending_list_timeout()
+{
+ int retval;
+ timeval tv;
+
+ tv.tv_sec = 0;
+ tv.tv_usec = 100000; //100ms
+
+ size_t pending_list_size = _pending_list.size();
+
+ if ((retval = libusb_handle_events_timeout(_ctx, &tv)) < 0) {
+ std::cerr << "error: libusb_handle_events: " << retval << std::endl;
+ return false;
+ }
+
+ if (_pending_list.size() < pending_list_size) {
+ return true;
+ }
+ else {
+ return false;
+ }
+}
+
+
+/*
+ * Returns a free transfer with empty data bufer for OUT requests
+ */
+libusb_transfer *usb_endpoint::get_free_transfer()
+{
+ if (_free_list.empty()) {
+ if (!reap_completed_list())
+ return NULL;
+ }
+
+ return free_list_get();
+}
+
+
+/*
+ * Returns a transfer containing data for IN requests
+ */
+libusb_transfer *usb_endpoint::get_completed_transfer()
+{
+ if (_completed_list.empty()) {
+ if (!reap_pending_list_timeout())
+ return NULL;
+ }
+
+ return completed_list_get();
+}
+
+/*
+ * List operations
+ */
+void usb_endpoint::free_list_add(libusb_transfer *lut)
+{
+ _free_list.push_back(lut);
+}
+
+void usb_endpoint::pending_list_add(libusb_transfer *lut)
+{
+ _pending_list.push_back(lut);
+}
+
+void usb_endpoint::completed_list_add(libusb_transfer *lut)
+{
+ _completed_list.push_back(lut);
+}
+
+
+/*
+ * Free and completed lists don't have ordered content
+ *
+ * Pop transfers from the front as needed
+ */
+libusb_transfer *usb_endpoint::free_list_get()
+{
+ libusb_transfer *lut;
+
+ if (_free_list.size() == 0) {
+ return NULL;
+ }
+ else {
+ lut = _free_list.front();
+ _free_list.pop_front();
+ return lut;
+ }
+}
+
+
+/*
+ * Free and completed lists don't have ordered content
+ *
+ * Pop transfers from the front as needed
+ */
+libusb_transfer *usb_endpoint::completed_list_get()
+{
+ libusb_transfer *lut;
+
+ if (_completed_list.empty()) {
+ return NULL;
+ }
+ else {
+ lut = _completed_list.front();
+ _completed_list.pop_front();
+ return lut;
+ }
+}
+
+
+/*
+ * Search and remove transfer from pending list
+ *
+ * Assuming that the callbacks occur in order, the front element
+ * should yield the correct transfer. If not, then something else
+ * is going on. If no transfers match, then something went wrong.
+ */
+bool usb_endpoint::pending_list_remove(libusb_transfer *lut)
+{
+ std::list<libusb_transfer*>::iterator iter;
+ for (iter = _pending_list.begin(); iter != _pending_list.end(); iter++) {
+ if (*iter == lut) {
+ _pending_list.erase(iter);
+ return true;
+ }
+ }
+ return false;
+}
+
+
+/***********************************************************************
+ * Managed buffers
+ **********************************************************************/
+class libusb_managed_recv_buffer_impl : public managed_recv_buffer {
+public:
+ libusb_managed_recv_buffer_impl(libusb_transfer *lut,
+ usb_endpoint *endpoint)
+ : _buff(lut->buffer, lut->length)
+ {
+ _lut = lut;
+ _endpoint = endpoint;
+ }
+
+ ~libusb_managed_recv_buffer_impl()
+ {
+ if (!_endpoint->submit(_lut))
+ std::cerr << "USB: failed to submit IN transfer" << std::endl;
+ }
+
+private:
+ const boost::asio::const_buffer &get() const
+ {
+ return _buff;
+ }
+
+ libusb_transfer *_lut;
+ usb_endpoint *_endpoint;
+ const boost::asio::const_buffer _buff;
+};
+
+
+class libusb_managed_send_buffer_impl : public managed_send_buffer {
+public:
+ libusb_managed_send_buffer_impl(libusb_transfer *lut,
+ usb_endpoint *endpoint,
+ size_t buff_size)
+ : _buff(lut->buffer, buff_size)
+ {
+ _lut = lut;
+ _endpoint = endpoint;
+ }
+
+ ~libusb_managed_send_buffer_impl()
+ {
+ /* NOP */
+ }
+
+ ssize_t commit(size_t num_bytes)
+ {
+ _lut->length = num_bytes;
+ _lut->actual_length = 0;
+
+ if (_endpoint->submit(_lut))
+ return num_bytes;
+ else
+ return 0;
+ }
+
+private:
+ const boost::asio::mutable_buffer &get() const
+ {
+ return _buff;
+ }
+
+ libusb_transfer *_lut;
+ usb_endpoint *_endpoint;
+ const boost::asio::mutable_buffer _buff;
+};
+
+
+/***********************************************************************
+ * USB zero_copy device class
+ **********************************************************************/
+class libusb_zero_copy_impl : public usb_zero_copy
+{
+private:
+ usb_endpoint *_rx_ep;
+ usb_endpoint *_tx_ep;
+
+ /*
+ * Libusb handles
+ */
+ libusb_context *_rx_ctx;
+ libusb_context *_tx_ctx;
+ libusb_device_handle *_rx_dev_handle;
+ libusb_device_handle *_tx_dev_handle;
+
+ int _rx_endpoint;
+ int _tx_endpoint;
+
+ size_t _recv_buff_size;
+ size_t _send_buff_size;
+ size_t _num_frames;
+
+ bool open_device(uhd::usb_descriptor_t descriptor);
+ bool open_interface(libusb_device_handle *dev_handle, int interface);
+ bool compare_device(libusb_device *dev, uhd::usb_descriptor_t descriptor);
+
+ std::string get_serial(libusb_device *dev);
+
+public:
+ typedef boost::shared_ptr<libusb_zero_copy_impl> sptr;
+
+ /*
+ * Structors
+ */
+ libusb_zero_copy_impl(uhd::usb_descriptor_t descriptor,
+ unsigned int rx_endpoint,
+ unsigned int tx_endpoint,
+ size_t recv_buff_size,
+ size_t send_buff_size);
+
+ ~libusb_zero_copy_impl();
+
+ managed_recv_buffer::sptr get_recv_buff(void);
+ managed_send_buffer::sptr get_send_buff(void);
+
+ size_t get_num_recv_frames(void) const { return _num_frames; }
+ size_t get_num_send_frames(void) const { return _num_frames; }
+};
+
+
+libusb_zero_copy_impl::libusb_zero_copy_impl(uhd::usb_descriptor_t descriptor,
+ unsigned int rx_endpoint,
+ unsigned int tx_endpoint,
+ size_t buff_size,
+ size_t block_size)
+ : _rx_ctx(NULL), _tx_ctx(NULL), _rx_dev_handle(NULL), _tx_dev_handle(NULL),
+ _recv_buff_size(block_size), _send_buff_size(block_size),
+ _num_frames(buff_size / block_size)
+{
+ if (libusb_init(&_rx_ctx) < 0)
+ std::cerr << "error: libusb_init" << std::endl;
+
+ if (libusb_init(&_tx_ctx) < 0)
+ std::cerr << "error: libusb_init" << std::endl;
+
+ libusb_set_debug(_rx_ctx, libusb_debug_level);
+ libusb_set_debug(_tx_ctx, libusb_debug_level);
+
+ open_device(descriptor);
+
+ open_interface(_rx_dev_handle, 2);
+ open_interface(_tx_dev_handle, 1);
+
+ _rx_ep = new usb_endpoint(_rx_dev_handle,
+ _rx_ctx,
+ rx_endpoint,
+ true,
+ _recv_buff_size,
+ _num_frames);
+
+ _tx_ep = new usb_endpoint(_tx_dev_handle,
+ _tx_ctx,
+ tx_endpoint,
+ false,
+ _send_buff_size,
+ _num_frames);
+}
+
+
+libusb_zero_copy_impl::~libusb_zero_copy_impl()
+{
+ delete _rx_ep;
+ delete _tx_ep;
+
+ libusb_close(_rx_dev_handle);
+ libusb_close(_tx_dev_handle);
+
+ libusb_exit(_rx_ctx);
+ libusb_exit(_tx_ctx);
+}
+
+
+bool libusb_zero_copy_impl::open_device(uhd::usb_descriptor_t descriptor)
+{
+ libusb_device **rx_list;
+ libusb_device **tx_list;
+
+ bool rx_found = false;
+ bool tx_found = false;
+
+ ssize_t rx_cnt = libusb_get_device_list(_rx_ctx, &rx_list);
+ ssize_t tx_cnt = libusb_get_device_list(_tx_ctx, &tx_list);
+
+ if ((rx_cnt < 0) | (tx_cnt < 0) | (rx_cnt != tx_cnt))
+ return false;
+
+ //find and open the receive device
+ for (ssize_t i = 0; i < rx_cnt; i++) {
+ libusb_device *dev = rx_list[i];
+
+ if (compare_device(dev, descriptor)) {
+ libusb_open(dev, &_rx_dev_handle);
+ rx_found = true;
+ break;
+ }
+ }
+
+ //find and open the transmit device
+ for (ssize_t i = 0; i < tx_cnt; i++) {
+ libusb_device *dev = tx_list[i];
+
+ if (compare_device(dev, descriptor)) {
+ libusb_open(dev, &_tx_dev_handle);
+ tx_found = true;
+ }
+ }
+
+ libusb_free_device_list(rx_list, 0);
+ libusb_free_device_list(tx_list, 0);
+
+ if (tx_found && rx_found)
+ return true;
+ else
+ return false;
+}
+
+bool libusb_zero_copy_impl::compare_device(libusb_device *dev,
+ uhd::usb_descriptor_t descriptor)
+{
+ std::string serial = descriptor.serial;
+ boost::uint16_t vendor_id = descriptor.vendor_id;
+ boost::uint16_t product_id = descriptor.product_id;
+ boost::uint8_t device_addr = descriptor.device_addr;
+
+ libusb_device_descriptor desc;
+ libusb_get_device_descriptor(dev, &desc);
+
+ if (serial.compare(get_serial(dev)))
+ return false;
+ if (vendor_id != desc.idVendor)
+ return false;
+ if (product_id != desc.idProduct)
+ return false;
+ if (device_addr != libusb_get_device_address(dev))
+ return false;
+
+ return true;
+}
+
+bool libusb_zero_copy_impl::open_interface(libusb_device_handle *dev_handle,
+ int interface)
+{
+ int ret = libusb_claim_interface(dev_handle, interface);
+ if (ret < 0) {
+ std::cerr << "error: libusb_claim_interface() " << ret << std::endl;
+ return false;
+ }
+ else {
+ return true;
+ }
+}
+
+std::string libusb_zero_copy_impl::get_serial(libusb_device *dev)
+{
+ unsigned char buff[32];
+
+ libusb_device_descriptor desc;
+ if (libusb_get_device_descriptor(dev, &desc) < 0) {
+ std::cerr << "error: libusb_get_device_descriptor()" << std::endl;
+ return "";
+ }
+
+ if (desc.iSerialNumber == 0)
+ return "";
+
+ //open the device because we have to
+ libusb_device_handle *dev_handle;
+ if (libusb_open(dev, &dev_handle) < 0) {
+ return "";
+ }
+
+ if (libusb_get_string_descriptor_ascii(dev_handle, desc.iSerialNumber,
+ buff, sizeof(buff)) < 0) {
+ std::cerr << "error: libusb_get_string_descriptor_ascii()" << std::endl;
+ return "";
+ }
+
+ libusb_close(dev_handle);
+
+ return (char*) buff;
+}
+
+
+managed_recv_buffer::sptr libusb_zero_copy_impl::get_recv_buff()
+{
+ libusb_transfer *lut = _rx_ep->get_completed_transfer();
+ if (lut == NULL) {
+ return managed_recv_buffer::sptr();
+ }
+ else {
+ return managed_recv_buffer::sptr(
+ new libusb_managed_recv_buffer_impl(lut,
+ _rx_ep));
+ }
+}
+
+
+managed_send_buffer::sptr libusb_zero_copy_impl::get_send_buff()
+{
+ libusb_transfer *lut = _tx_ep->get_free_transfer();
+ if (lut == NULL) {
+ return managed_send_buffer::sptr();
+ }
+ else {
+ return managed_send_buffer::sptr(
+ new libusb_managed_send_buffer_impl(lut,
+ _tx_ep,
+ _send_buff_size));
+ }
+}
+
+
+/***********************************************************************
+ * USB zero_copy make functions
+ **********************************************************************/
+usb_zero_copy::sptr usb_zero_copy::make(uhd::usb_descriptor_t descriptor,
+ unsigned int rx_endpoint,
+ unsigned int tx_endpoint,
+ size_t buff_size,
+ size_t block_size)
+
+{
+ return sptr(new libusb_zero_copy_impl(descriptor,
+ rx_endpoint,
+ tx_endpoint,
+ buff_size,
+ block_size));
+}
+
+
+