diff options
| author | Thomas Tsou <ttsou@vt.edu> | 2010-08-13 13:40:13 -0700 | 
|---|---|---|
| committer | Thomas Tsou <ttsou@vt.edu> | 2010-08-13 17:54:33 -0700 | 
| commit | 86c86ede9775824411262e36f104489bcd171e21 (patch) | |
| tree | e213ee2e4cc8ca62689eada3690d0e6eb8509dfd | |
| parent | 38746242eab47845c1b1b6cb264c3e3041977488 (diff) | |
| download | uhd-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.cpp | 238 | ||||
| -rw-r--r-- | host/lib/transport/libusb1_zero_copy.cpp | 854 | 
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)); +} + + + | 
