From f97c4338458a965502d73903983ae5a3ceeebd53 Mon Sep 17 00:00:00 2001 From: Josh Blum Date: Thu, 23 Sep 2010 18:07:31 -0700 Subject: usrp1: fixes to remove warnings and errors for usrp1 + libusb windows --- host/lib/transport/libusb1_base.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'host/lib/transport/libusb1_base.cpp') diff --git a/host/lib/transport/libusb1_base.cpp b/host/lib/transport/libusb1_base.cpp index e21c39aa3..1f816c6e2 100644 --- a/host/lib/transport/libusb1_base.cpp +++ b/host/lib/transport/libusb1_base.cpp @@ -67,7 +67,7 @@ bool libusb::compare_device(libusb_device *dev, std::string serial = handle->get_serial(); boost::uint16_t vendor_id = handle->get_vendor_id(); boost::uint16_t product_id = handle->get_product_id(); - boost::uint8_t device_addr = handle->get_device_addr(); + boost::uint16_t device_addr = handle->get_device_addr(); libusb_device_descriptor libusb_desc; if (libusb_get_device_descriptor(dev, &libusb_desc) < 0) -- cgit v1.2.3 From 1dffd5bb5fe16cd35ba54e44e927888e0192e905 Mon Sep 17 00:00:00 2001 From: Josh Blum Date: Fri, 24 Sep 2010 18:14:32 -0700 Subject: usb: tweaks to usb code to cleanup properly and/or in error conditions --- host/lib/transport/libusb1_base.cpp | 17 +++++++---------- host/lib/transport/libusb1_control.cpp | 1 + host/lib/transport/libusb1_device_handle.cpp | 9 +++++---- 3 files changed, 13 insertions(+), 14 deletions(-) (limited to 'host/lib/transport/libusb1_base.cpp') diff --git a/host/lib/transport/libusb1_base.cpp b/host/lib/transport/libusb1_base.cpp index 1f816c6e2..cd3e4adcf 100644 --- a/host/lib/transport/libusb1_base.cpp +++ b/host/lib/transport/libusb1_base.cpp @@ -30,9 +30,7 @@ using namespace uhd::transport; **********************************************************/ void libusb::init(libusb_context **ctx, int debug_level) { - if (libusb_init(ctx) < 0) - std::cerr << "error: libusb_init" << std::endl; - + UHD_ASSERT_THROW(libusb_init(ctx) == 0); libusb_set_debug(*ctx, debug_level); } @@ -48,14 +46,13 @@ libusb_device_handle *libusb::open_device(libusb_context *ctx, libusb_device *dev = libusb_dev_list[i]; if (compare_device(dev, handle)) { - libusb_open(dev, &dev_handle); - libusb_unref_device(dev); - break; + if (libusb_open(dev, &dev_handle) == 0) break; } - - libusb_unref_device(dev); } + libusb_free_device_list(libusb_dev_list, true); + if(dev_handle == NULL) + throw std::runtime_error("USB: cannot open device handle"); return dev_handle; } @@ -70,12 +67,12 @@ bool libusb::compare_device(libusb_device *dev, boost::uint16_t device_addr = handle->get_device_addr(); libusb_device_descriptor libusb_desc; - if (libusb_get_device_descriptor(dev, &libusb_desc) < 0) + if (libusb_get_device_descriptor(dev, &libusb_desc) != 0) return false; if (vendor_id != libusb_desc.idVendor) return false; if (product_id != libusb_desc.idProduct) - return false; + return false; if (serial != get_serial(dev)) return false; if (device_addr != libusb_get_device_address(dev)) diff --git a/host/lib/transport/libusb1_control.cpp b/host/lib/transport/libusb1_control.cpp index 3531128b2..be9b51111 100644 --- a/host/lib/transport/libusb1_control.cpp +++ b/host/lib/transport/libusb1_control.cpp @@ -46,6 +46,7 @@ private: libusb_control_impl::libusb_control_impl(usb_device_handle::sptr handle) { + _ctx = NULL; libusb::init(&_ctx, libusb_debug_level); // Find and open the libusb_device corresponding to the diff --git a/host/lib/transport/libusb1_device_handle.cpp b/host/lib/transport/libusb1_device_handle.cpp index 7efddd410..6bef37ed2 100644 --- a/host/lib/transport/libusb1_device_handle.cpp +++ b/host/lib/transport/libusb1_device_handle.cpp @@ -77,7 +77,7 @@ usb_device_handle::sptr make_usb_device_handle(libusb_device *dev) libusb_device_descriptor desc; if (libusb_get_device_descriptor(dev, &desc) < 0) { - UHD_ASSERT_THROW("USB: failed to get device descriptor"); + throw std::runtime_error("USB: failed to get device descriptor"); } std::string serial = libusb::get_serial(dev); @@ -104,14 +104,15 @@ std::vector usb_device_handle::get_device_list(boost::u size_t dev_size = libusb_get_device_list(ctx, &libusb_device_list); for (size_t i = 0; i < dev_size; i++) { libusb_device *dev = libusb_device_list[i]; - if(libusb_get_device_descriptor(dev, &desc) < 0) { - UHD_ASSERT_THROW("USB: failed to get device descriptor"); + if(libusb_get_device_descriptor(dev, &desc) != 0) { + continue; //just try the next device, do not throw } if(desc.idVendor == vid && desc.idProduct == pid) { - device_handle_list.push_back(make_usb_device_handle(dev)); + device_handle_list.push_back(make_usb_device_handle(dev)); } } + libusb_free_device_list(libusb_device_list, true); libusb_exit(ctx); return device_handle_list; } -- cgit v1.2.3 From dc8bcfde805228ed5d00334ce44c6c0684dcfe2c Mon Sep 17 00:00:00 2001 From: Josh Blum Date: Sat, 25 Sep 2010 21:07:15 -0700 Subject: usb: work on libusb code to use a single context across all calls libusb allocation stuff had been moved inside of smart pointer classes to handle automatic cleanup the public device handle implementation now holds an actual libusb device inside of it needs testing - all platforms --- host/include/uhd/transport/usb_device_handle.hpp | 6 - host/lib/transport/CMakeLists.txt | 1 - host/lib/transport/libusb1_base.cpp | 289 +++++++++++++++++------ host/lib/transport/libusb1_base.hpp | 162 ++++++++----- host/lib/transport/libusb1_control.cpp | 74 ++---- host/lib/transport/libusb1_device_handle.cpp | 118 --------- host/lib/transport/libusb1_zero_copy.cpp | 83 +++---- 7 files changed, 367 insertions(+), 366 deletions(-) delete mode 100644 host/lib/transport/libusb1_device_handle.cpp (limited to 'host/lib/transport/libusb1_base.cpp') diff --git a/host/include/uhd/transport/usb_device_handle.hpp b/host/include/uhd/transport/usb_device_handle.hpp index 735a3acbe..9bb7db9c4 100644 --- a/host/include/uhd/transport/usb_device_handle.hpp +++ b/host/include/uhd/transport/usb_device_handle.hpp @@ -60,12 +60,6 @@ public: */ virtual boost::uint16_t get_product_id() const = 0; - /*! - * Return the device's USB address - * \return a Product ID - */ - virtual boost::uint16_t get_device_addr() const = 0; - /*! * Return a vector of USB devices on this host * \return a vector of USB device handles that match vid and pid diff --git a/host/lib/transport/CMakeLists.txt b/host/lib/transport/CMakeLists.txt index 48b6e9677..61616d077 100644 --- a/host/lib/transport/CMakeLists.txt +++ b/host/lib/transport/CMakeLists.txt @@ -31,7 +31,6 @@ IF(LIBUSB_FOUND) ${CMAKE_SOURCE_DIR}/lib/transport/libusb1_zero_copy.cpp ${CMAKE_SOURCE_DIR}/lib/transport/libusb1_base.cpp ${CMAKE_SOURCE_DIR}/lib/transport/libusb1_base.hpp - ${CMAKE_SOURCE_DIR}/lib/transport/libusb1_device_handle.cpp ) IF(MSVC) #include our custom stdint for libusb INCLUDE_DIRECTORIES(${CMAKE_SOURCE_DIR}/lib/transport/msvc) diff --git a/host/lib/transport/libusb1_base.cpp b/host/lib/transport/libusb1_base.cpp index cd3e4adcf..5ff996642 100644 --- a/host/lib/transport/libusb1_base.cpp +++ b/host/lib/transport/libusb1_base.cpp @@ -17,107 +17,242 @@ #include "libusb1_base.hpp" #include +#include +#include +#include #include using namespace uhd::transport; -/********************************************************** - * Helper Methods - **********************************************************/ - -/********************************************************** - * libusb namespace - **********************************************************/ -void libusb::init(libusb_context **ctx, int debug_level) -{ - UHD_ASSERT_THROW(libusb_init(ctx) == 0); - libusb_set_debug(*ctx, debug_level); +/*********************************************************************** + * libusb session + **********************************************************************/ +class libusb_session_impl : public libusb::session{ +public: + libusb_session_impl(void){ + UHD_ASSERT_THROW(libusb_init(&_context) == 0); + libusb_set_debug(_context, debug_level); + } + + ~libusb_session_impl(void){ + libusb_exit(_context); + } + + libusb_context *get_context(void) const{ + return _context; + } + +private: + libusb_context *_context; +}; + +libusb::session::sptr libusb::session::get_global_session(void){ + static boost::weak_ptr global_session; + + //not expired -> get existing session + if (not global_session.expired()) return global_session.lock(); + + //create a new global session + sptr new_global_session(new libusb_session_impl()); + global_session = new_global_session; + return new_global_session; } -libusb_device_handle *libusb::open_device(libusb_context *ctx, - usb_device_handle::sptr handle) -{ - libusb_device_handle *dev_handle = NULL; - libusb_device **libusb_dev_list; - size_t dev_cnt = libusb_get_device_list(ctx, &libusb_dev_list); +/*********************************************************************** + * libusb device + **********************************************************************/ +class libusb_device_impl : public libusb::device{ +public: + libusb_device_impl(libusb_device *dev){ + _session = libusb::session::get_global_session(); + _dev = dev; + } - //find and open the USB device - for (size_t i = 0; i < dev_cnt; i++) { - libusb_device *dev = libusb_dev_list[i]; + ~libusb_device_impl(void){ + libusb_unref_device(this->get()); + } - if (compare_device(dev, handle)) { - if (libusb_open(dev, &dev_handle) == 0) break; - } + libusb_device *get(void) const{ + return _dev; } - libusb_free_device_list(libusb_dev_list, true); - if(dev_handle == NULL) - throw std::runtime_error("USB: cannot open device handle"); - return dev_handle; -} +private: + libusb::session::sptr _session; //always keep a reference to session + libusb_device *_dev; +}; + +/*********************************************************************** + * libusb device list + **********************************************************************/ +class libusb_device_list_impl : public libusb::device_list{ +public: + libusb_device_list_impl(void){ + libusb::session::sptr sess = libusb::session::get_global_session(); -//note: changed order of checks so it only tries to get_serial and get_device_address if vid and pid match -//doing this so it doesn't try to open the device if it's not ours -bool libusb::compare_device(libusb_device *dev, - usb_device_handle::sptr handle) -{ - std::string serial = handle->get_serial(); - boost::uint16_t vendor_id = handle->get_vendor_id(); - boost::uint16_t product_id = handle->get_product_id(); - boost::uint16_t device_addr = handle->get_device_addr(); - - libusb_device_descriptor libusb_desc; - if (libusb_get_device_descriptor(dev, &libusb_desc) != 0) - return false; - if (vendor_id != libusb_desc.idVendor) - return false; - if (product_id != libusb_desc.idProduct) - return false; - if (serial != get_serial(dev)) - return false; - if (device_addr != libusb_get_device_address(dev)) - return false; - - return true; + //allocate a new list of devices + libusb_device** dev_list; + ssize_t ret = libusb_get_device_list(sess->get_context(), &dev_list); + if (ret < 0) throw std::runtime_error("cannot enumerate usb devices"); + + //fill the vector of device references + for (size_t i = 0; i < size_t(ret); i++) _devs.push_back( + libusb::device::sptr(new libusb_device_impl(dev_list[i])) + ); + + //free the device list but dont unref (done in ~device) + libusb_free_device_list(dev_list, false/*dont unref*/); + } + + size_t size(void) const{ + return _devs.size(); + } + + libusb::device::sptr at(size_t i) const{ + return _devs.at(i); + } + +private: + std::vector _devs; +}; + +libusb::device_list::sptr libusb::device_list::make(void){ + return sptr(new libusb_device_list_impl()); } +/*********************************************************************** + * libusb device descriptor + **********************************************************************/ +class libusb_device_descriptor_impl : public libusb::device_descriptor{ +public: + libusb_device_descriptor_impl(libusb::device::sptr dev){ + _dev = dev; + UHD_ASSERT_THROW(libusb_get_device_descriptor(_dev->get(), &_desc) == 0); + } -bool libusb::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; + const libusb_device_descriptor &get(void) const{ + return _desc; } - else { - return true; + + std::string get_ascii_serial(void) const{ + if (this->get().iSerialNumber == 0) return ""; + + libusb::device_handle::sptr handle( + libusb::device_handle::get_cached_handle(_dev) + ); + + unsigned char buff[512]; + ssize_t ret = libusb_get_string_descriptor_ascii( + handle->get(), this->get().iSerialNumber, buff, sizeof(buff) + ); + if (ret < 0) return ""; //on error, just return empty string + + return std::string((char *)buff, ret); } + +private: + libusb::device::sptr _dev; //always keep a reference to device + libusb_device_descriptor _desc; +}; + +libusb::device_descriptor::sptr libusb::device_descriptor::make(device::sptr dev){ + return sptr(new libusb_device_descriptor_impl(dev)); } +/*********************************************************************** + * libusb device handle + **********************************************************************/ +class libusb_device_handle_impl : public libusb::device_handle{ +public: + libusb_device_handle_impl(libusb::device::sptr dev){ + _dev = dev; + UHD_ASSERT_THROW(libusb_open(_dev->get(), &_handle) == 0); + } + + ~libusb_device_handle_impl(void){ + //release all claimed interfaces + for (size_t i = 0; i < _claimed.size(); i++){ + libusb_release_interface(this->get(), _claimed[i]); + } + libusb_close(_handle); + } + + libusb_device_handle *get(void) const{ + return _handle; + } -std::string libusb::get_serial(libusb_device *dev) -{ - unsigned char buff[32]; + void claim_interface(int interface){ + UHD_ASSERT_THROW(libusb_claim_interface(this->get(), interface) == 0); + _claimed.push_back(interface); + } - libusb_device_descriptor desc; - if (libusb_get_device_descriptor(dev, &desc) < 0) - return ""; +private: + libusb::device::sptr _dev; //always keep a reference to device + libusb_device_handle *_handle; + std::vector _claimed; +}; - if (desc.iSerialNumber == 0) - return ""; +libusb::device_handle::sptr libusb::device_handle::get_cached_handle(device::sptr dev){ + static uhd::dict > handles; - //open the device because we have to - libusb_device_handle *dev_handle; - if (libusb_open(dev, &dev_handle) < 0) - return ""; + //not expired -> get existing session + if (handles.has_key(dev->get()) and not handles[dev->get()].expired()){ + return handles[dev->get()].lock(); + } - if (libusb_get_string_descriptor_ascii(dev_handle, desc.iSerialNumber, - buff, sizeof(buff)) < 0) { - return ""; + //create a new global session + sptr new_handle(new libusb_device_handle_impl(dev)); + handles[dev->get()] = new_handle; + return new_handle; +} + +/*********************************************************************** + * libusb special handle + **********************************************************************/ +class libusb_special_handle_impl : public libusb::special_handle{ +public: + libusb_special_handle_impl(libusb::device::sptr dev){ + _dev = dev; } - libusb_close(dev_handle); + libusb::device::sptr get_device(void) const{ + return _dev; + } + + std::string get_serial(void) const{ + return libusb::device_descriptor::make(this->get_device())->get_ascii_serial(); + } + + boost::uint16_t get_vendor_id(void) const{ + return libusb::device_descriptor::make(this->get_device())->get().idVendor; + } + + boost::uint16_t get_product_id(void) const{ + return libusb::device_descriptor::make(this->get_device())->get().idProduct; + } + +private: + libusb::device::sptr _dev; //always keep a reference to device +}; + +libusb::special_handle::sptr libusb::special_handle::make(device::sptr dev){ + return sptr(new libusb_special_handle_impl(dev)); +} + +/*********************************************************************** + * list device handles implementations + **********************************************************************/ +std::vector usb_device_handle::get_device_list( + boost::uint16_t vid, boost::uint16_t pid +){ + std::vector handles; + + libusb::device_list::sptr dev_list = libusb::device_list::make(); + for (size_t i = 0; i < dev_list->size(); i++){ + usb_device_handle::sptr handle = libusb::special_handle::make(dev_list->at(i)); + if (handle->get_vendor_id() == vid and handle->get_product_id() == pid){ + handles.push_back(handle); + } + } - return (char*) buff; + return handles; } diff --git a/host/lib/transport/libusb1_base.hpp b/host/lib/transport/libusb1_base.hpp index 484bcf3d9..47d078cde 100644 --- a/host/lib/transport/libusb1_base.hpp +++ b/host/lib/transport/libusb1_base.hpp @@ -19,74 +19,122 @@ #define INCLUDED_LIBUHD_TRANSPORT_LIBUSB_HPP #include +#include +#include #include #include namespace uhd { namespace transport { namespace libusb { - /* - * Initialize libusb and set debug level - * Takes a pointer to context pointer because that's - * how libusb rolls. Debug levels. - * - * Level 0: no messages ever printed by the library (default) - * Level 1: error messages are printed to stderr - * Level 2: warning and error messages are printed to stderr - * Level 3: informational messages are printed to stdout, warning - * and error messages are printed to stderr - * - * \param ctx pointer to context pointer - * \param debug_level + + /*! + * This session class holds a global libusb context for this process. + * The get global session call will create a new context if none exists. + * When all references to session are destroyed, the context will be freed. */ - void init(libusb_context **ctx, int debug_level); - - /* - * Open the device specified by a generic handle - * Find the libusb_device cooresponding to the generic handle - * and open it for I/O, which returns a libusb_device_handle - * ready for an interface - * \param ctx the libusb context used for init - * \return a libusb_device_handle ready for action + class session : boost::noncopyable { + public: + typedef boost::shared_ptr sptr; + + /*! + * Level 0: no messages ever printed by the library (default) + * Level 1: error messages are printed to stderr + * Level 2: warning and error messages are printed to stderr + * Level 3: informational messages are printed to stdout, warning + * and error messages are printed to stderr + */ + static const int debug_level = 0; + + //! get a shared pointer to the global session + static sptr get_global_session(void); + + //! get the underlying libusb context pointer + virtual libusb_context *get_context(void) const = 0; + }; + + /*! + * Holds a device pointer with a reference to the session. */ - libusb_device_handle *open_device(libusb_context *ctx, - usb_device_handle::sptr handle); - - /* - * Compare a libusb device with a generic handle - * Check the descriptors and open the device to check the - * serial number string. Compare values against the given - * handle. The libusb context is already implied in the - * libusb_device. - * \param dev a libusb_device pointer - * \param handle a generic handle specifier - * \return true if handle and device match, false otherwise + class device : boost::noncopyable { + public: + typedef boost::shared_ptr sptr; + + //! get the underlying device pointer + virtual libusb_device *get(void) const = 0; + }; + + /*! + * This device list class holds a device list that will be + * automatically freed when the last reference is destroyed. */ - bool compare_device(libusb_device *dev, usb_device_handle::sptr handle); - - /* - * Open an interface to the device - * This is a logical operation for operating system housekeeping as - * nothing is sent over the bus. The interface much correspond - * to the USB device descriptors. - * \param dev_handle libusb handle to an opened device - * \param interface integer of the interface to use - * \return true on success, false on error + class device_list : boost::noncopyable { + public: + typedef boost::shared_ptr sptr; + + //! make a new device list + static sptr make(void); + + //! the number of devices in this list + virtual size_t size() const = 0; + + //! get the device pointer at a particular index + virtual device::sptr at(size_t index) const = 0; + }; + + /*! + * Holds a device descriptor and a reference to the device. */ - bool open_interface(libusb_device_handle *dev_handle, int interface); - - /* - * Get serial number - * The standard USB device descriptor contains an index to an - * actual serial number string descriptor. The index is readily - * readble, but the string descriptor requires probing the device. - * Because this call attempts to open the device, it may not - * succeed because not all USB devices are readily opened. - * The default language is used for the request (English). - * \param dev a libusb_device pointer - * \return string serial number or 0 on error or unavailablity + class device_descriptor : boost::noncopyable { + public: + typedef boost::shared_ptr sptr; + + //! make a new descriptor from a device reference + static sptr make(device::sptr); + + //! get the underlying device descriptor + virtual const libusb_device_descriptor &get(void) const = 0; + + virtual std::string get_ascii_serial(void) const = 0; + }; + + /*! + * Holds a device handle and a reference to the device. */ - std::string get_serial(libusb_device *dev); + class device_handle : boost::noncopyable { + public: + typedef boost::shared_ptr sptr; + + //! get a cached handle or make a new one given the device + static sptr get_cached_handle(device::sptr); + + //! get the underlying device handle + virtual libusb_device_handle *get(void) const = 0; + + /*! + * Open USB interfaces for control using magic value + * IN interface: 2 + * OUT interface: 1 + * Control interface: 0 + */ + virtual void claim_interface(int) = 0; + }; + + /*! + * The special handle is our internal implementation of the + * usb device handle which is used publicly to identify a device. + */ + class special_handle : public usb_device_handle { + public: + typedef boost::shared_ptr sptr; + + //! make a new special handle from device + static sptr make(device::sptr); + + //! get the underlying device reference + virtual device::sptr get_device(void) const = 0; + }; + } }} //namespace diff --git a/host/lib/transport/libusb1_control.cpp b/host/lib/transport/libusb1_control.cpp index be9b51111..c989a788c 100644 --- a/host/lib/transport/libusb1_control.cpp +++ b/host/lib/transport/libusb1_control.cpp @@ -20,7 +20,6 @@ using namespace uhd::transport; -const int libusb_debug_level = 0; const int libusb_timeout = 0; /*********************************************************************** @@ -28,69 +27,38 @@ const int libusb_timeout = 0; **********************************************************************/ class libusb_control_impl : public usb_control { public: - libusb_control_impl(usb_device_handle::sptr handle); - ~libusb_control_impl(); + libusb_control_impl(libusb::device_handle::sptr handle): + _handle(handle) + { + _handle->claim_interface(0 /* control interface */); + } 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); + boost::uint16_t length + ){ + return libusb_control_transfer(_handle->get(), + request_type, + request, + value, + index, + buff, + length, + libusb_timeout); + } private: - libusb_context *_ctx; - libusb_device_handle *_dev_handle; + libusb::device_handle::sptr _handle; }; - -libusb_control_impl::libusb_control_impl(usb_device_handle::sptr handle) -{ - _ctx = NULL; - libusb::init(&_ctx, libusb_debug_level); - - // Find and open the libusb_device corresponding to the - // given handle and return the libusb_device_handle - // that can be used for I/O purposes. - _dev_handle = libusb::open_device(_ctx, handle); - - // Open USB interfaces for control using magic value - // IN interface: 2 - // OUT interface: 1 - // Control interface: 0 - libusb::open_interface(_dev_handle, 0); -} - - -libusb_control_impl::~libusb_control_impl() -{ - libusb_close(_dev_handle); - libusb_exit(_ctx); -} - - -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(usb_device_handle::sptr handle) -{ - return sptr(new libusb_control_impl(handle)); +usb_control::sptr usb_control::make(usb_device_handle::sptr handle){ + return sptr(new libusb_control_impl(libusb::device_handle::get_cached_handle( + boost::static_pointer_cast(handle)->get_device() + ))); } diff --git a/host/lib/transport/libusb1_device_handle.cpp b/host/lib/transport/libusb1_device_handle.cpp deleted file mode 100644 index 6bef37ed2..000000000 --- a/host/lib/transport/libusb1_device_handle.cpp +++ /dev/null @@ -1,118 +0,0 @@ -// -// 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 . -// - -#include "libusb1_base.hpp" -#include -#include - -using namespace uhd::transport; - -const int libusb_debug_level = 0; - -/**************************************************************** - * libusb USB device handle implementation class - ***************************************************************/ -class libusb1_device_handle_impl : public usb_device_handle { -public: - libusb1_device_handle_impl(std::string serial, - boost::uint16_t product_id, - boost::uint16_t vendor_id, - boost::uint16_t device_addr) - : _serial(serial), _product_id(product_id), - _vendor_id(vendor_id), _device_addr(device_addr) - { - /* NOP */ - } - - ~libusb1_device_handle_impl() - { - /* NOP */ - } - - std::string get_serial() const - { - return _serial; - } - - boost::uint16_t get_vendor_id() const - { - return _vendor_id; - } - - - boost::uint16_t get_product_id() const - { - return _product_id; - } - - boost::uint16_t get_device_addr() const - { - return _device_addr; - } - -private: - std::string _serial; - boost::uint16_t _product_id; - boost::uint16_t _vendor_id; - boost::uint16_t _device_addr; -}; - - -usb_device_handle::sptr make_usb_device_handle(libusb_device *dev) -{ - libusb_device_descriptor desc; - - if (libusb_get_device_descriptor(dev, &desc) < 0) { - throw std::runtime_error("USB: failed to get device descriptor"); - } - - std::string serial = libusb::get_serial(dev); - boost::uint16_t product_id = desc.idProduct; - boost::uint16_t vendor_id = desc.idVendor; - boost::uint16_t device_addr = libusb_get_device_address(dev); - - return usb_device_handle::sptr(new libusb1_device_handle_impl( - serial, - product_id, - vendor_id, - device_addr)); -} - -std::vector usb_device_handle::get_device_list(boost::uint16_t vid, boost::uint16_t pid) -{ - libusb_context *ctx = NULL; - libusb_device** libusb_device_list; - std::vector device_handle_list; - libusb_device_descriptor desc; - - libusb::init(&ctx, libusb_debug_level); - - size_t dev_size = libusb_get_device_list(ctx, &libusb_device_list); - for (size_t i = 0; i < dev_size; i++) { - libusb_device *dev = libusb_device_list[i]; - if(libusb_get_device_descriptor(dev, &desc) != 0) { - continue; //just try the next device, do not throw - } - if(desc.idVendor == vid && desc.idProduct == pid) { - device_handle_list.push_back(make_usb_device_handle(dev)); - } - } - - libusb_free_device_list(libusb_device_list, true); - libusb_exit(ctx); - return device_handle_list; -} diff --git a/host/lib/transport/libusb1_zero_copy.cpp b/host/lib/transport/libusb1_zero_copy.cpp index b3a160462..41cd4b3fc 100644 --- a/host/lib/transport/libusb1_zero_copy.cpp +++ b/host/lib/transport/libusb1_zero_copy.cpp @@ -24,7 +24,6 @@ using namespace uhd::transport; -const int libusb_debug_level = 0; const int libusb_timeout = 0; /*********************************************************************** @@ -57,8 +56,8 @@ void pp_transfer(libusb_transfer *lut) **********************************************************************/ class usb_endpoint { private: - libusb_device_handle *_dev_handle; - libusb_context *_ctx; + libusb::device_handle::sptr _handle; + libusb::session::sptr _session; int _endpoint; bool _input; @@ -90,9 +89,8 @@ private: 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(libusb::device_handle::sptr handle, libusb::session::sptr sess, + int endpoint, bool input, size_t transfer_size, size_t num_transfers); ~usb_endpoint(); @@ -143,11 +141,11 @@ void usb_endpoint::callback_handle_transfer(libusb_transfer *lut) * 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), +usb_endpoint::usb_endpoint( + libusb::device_handle::sptr handle, libusb::session::sptr sess, + int endpoint, bool input, size_t transfer_size, size_t num_transfers) + : _handle(handle), _session(sess), + _endpoint(endpoint), _input(input), _transfer_size(transfer_size), _num_transfers(num_transfers) { unsigned int i; @@ -203,7 +201,7 @@ libusb_transfer *usb_endpoint::allocate_transfer(int buff_len) unsigned int endpoint = ((_endpoint & 0x7f) | (_input ? 0x80 : 0)); libusb_fill_bulk_transfer(lut, // transfer - _dev_handle, // dev_handle + _handle->get(), // dev_handle endpoint, // endpoint buff, // buffer buff_len, // length @@ -356,7 +354,7 @@ bool usb_endpoint::reap_pending_list() { int retval; - if ((retval = libusb_handle_events(_ctx)) < 0) { + if ((retval = libusb_handle_events(_session->get_context())) < 0) { std::cerr << "error: libusb_handle_events: " << retval << std::endl; return false; } @@ -383,7 +381,7 @@ bool usb_endpoint::reap_pending_list_timeout() size_t pending_list_size = _pending_list.size(); - if ((retval = libusb_handle_events_timeout(_ctx, &tv)) < 0) { + if ((retval = libusb_handle_events_timeout(_session->get_context(), &tv)) < 0) { std::cerr << "error: libusb_handle_events: " << retval << std::endl; return false; } @@ -613,11 +611,8 @@ private: usb_endpoint *_rx_ep; usb_endpoint *_tx_ep; - // Maintain libusb values - libusb_context *_rx_ctx; - libusb_context *_tx_ctx; - libusb_device_handle *_rx_dev_handle; - libusb_device_handle *_tx_dev_handle; + libusb::device_handle::sptr _handle; + libusb::session::sptr _session; size_t _recv_buff_size; size_t _send_buff_size; @@ -626,7 +621,7 @@ private: public: typedef boost::shared_ptr sptr; - libusb_zero_copy_impl(usb_device_handle::sptr handle, + libusb_zero_copy_impl(libusb::device_handle::sptr handle, unsigned int rx_endpoint, unsigned int tx_endpoint, size_t recv_buff_size, @@ -646,44 +641,27 @@ public: * Initializes libusb, opens devices, and sets up interfaces for I/O. * Finally, creates endpoints for asynchronous I/O. */ -libusb_zero_copy_impl::libusb_zero_copy_impl(usb_device_handle::sptr handle, +libusb_zero_copy_impl::libusb_zero_copy_impl(libusb::device_handle::sptr handle, 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), + : _handle(handle), _session(libusb::session::get_global_session()), _recv_buff_size(block_size), _send_buff_size(block_size), _num_frames(buff_size / block_size) { - // Initialize libusb with separate contexts to allow - // thread safe operation of transmit and receive - libusb::init(&_rx_ctx, libusb_debug_level); - libusb::init(&_tx_ctx, libusb_debug_level); - - UHD_ASSERT_THROW((_rx_ctx != NULL) && (_tx_ctx != NULL)); - - // Find and open the libusb_device corresponding to the - // given handle and return the libusb_device_handle - // that can be used for I/O purposes. - _rx_dev_handle = libusb::open_device(_rx_ctx, handle); - _tx_dev_handle = libusb::open_device(_tx_ctx, handle); - - // Open USB interfaces for tx/rx using magic values. - // IN interface: 2 - // OUT interface: 1 - // Control interface: 0 - libusb::open_interface(_rx_dev_handle, 2); - libusb::open_interface(_tx_dev_handle, 1); - - _rx_ep = new usb_endpoint(_rx_dev_handle, // libusb device_handle - _rx_ctx, // libusb context + _handle->claim_interface(2 /*in interface*/); + _handle->claim_interface(1 /*out interface*/); + + _rx_ep = new usb_endpoint(_handle, // libusb device_handle + _session, // libusb session w/ context rx_endpoint, // USB endpoint number true, // IN endpoint _recv_buff_size, // buffer size per transfer _num_frames); // number of libusb transfers - _tx_ep = new usb_endpoint(_tx_dev_handle, // libusb device_handle - _tx_ctx, // libusb context + _tx_ep = new usb_endpoint(_handle, // libusb device_handle + _session, // libusb session w/ context tx_endpoint, // USB endpoint number false, // OUT endpoint _send_buff_size, // buffer size per transfer @@ -694,13 +672,7 @@ libusb_zero_copy_impl::libusb_zero_copy_impl(usb_device_handle::sptr handle, 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); + delete _tx_ep; } @@ -755,7 +727,10 @@ usb_zero_copy::sptr usb_zero_copy::make(usb_device_handle::sptr handle, size_t block_size) { - return sptr(new libusb_zero_copy_impl(handle, + libusb::device_handle::sptr dev_handle(libusb::device_handle::get_cached_handle( + boost::static_pointer_cast(handle)->get_device() + )); + return sptr(new libusb_zero_copy_impl(dev_handle, rx_endpoint, tx_endpoint, buff_size, -- cgit v1.2.3 From 0816925695aa69a1ae344863fef47d239b3ec8af Mon Sep 17 00:00:00 2001 From: Josh Blum Date: Sun, 26 Sep 2010 20:23:23 -0700 Subject: usb: zero copy work, multiple endpoints with single context, async io Heavy work on the zero copy interface and endpoint wrappers to properly use the async io. The global libusb session starts a thread to run the event handler, the async callbacks push completed transfers onto a thread-safe bounded buffer. The managed buffer creation routines use the bounded buffer to efficiently pop off completed transfers. works on linux, throws a weird exception on cleanup --- host/lib/transport/libusb1_base.cpp | 16 ++ host/lib/transport/libusb1_zero_copy.cpp | 474 ++++++++----------------------- 2 files changed, 134 insertions(+), 356 deletions(-) (limited to 'host/lib/transport/libusb1_base.cpp') diff --git a/host/lib/transport/libusb1_base.cpp b/host/lib/transport/libusb1_base.cpp index 5ff996642..26e864459 100644 --- a/host/lib/transport/libusb1_base.cpp +++ b/host/lib/transport/libusb1_base.cpp @@ -20,6 +20,7 @@ #include #include #include +#include #include using namespace uhd::transport; @@ -32,9 +33,12 @@ public: libusb_session_impl(void){ UHD_ASSERT_THROW(libusb_init(&_context) == 0); libusb_set_debug(_context, debug_level); + _thread_group.create_thread(boost::bind(&libusb_session_impl::run_event_loop, this)); } ~libusb_session_impl(void){ + _running = false; + _thread_group.join_all(); libusb_exit(_context); } @@ -44,6 +48,18 @@ public: private: libusb_context *_context; + boost::thread_group _thread_group; + bool _running; + + void run_event_loop(void){ + _running = true; + timeval tv; + while(_running){ + tv.tv_sec = 0; + tv.tv_usec = 100000; //100ms + libusb_handle_events_timeout(this->get_context(), &tv); + } + } }; libusb::session::sptr libusb::session::get_global_session(void){ diff --git a/host/lib/transport/libusb1_zero_copy.cpp b/host/lib/transport/libusb1_zero_copy.cpp index 41cd4b3fc..e84793e88 100644 --- a/host/lib/transport/libusb1_zero_copy.cpp +++ b/host/lib/transport/libusb1_zero_copy.cpp @@ -17,8 +17,11 @@ #include "libusb1_base.hpp" #include +#include #include -#include +#include +#include +#include #include #include @@ -55,52 +58,57 @@ void pp_transfer(libusb_transfer *lut) * interface provided by the kernel. **********************************************************************/ class usb_endpoint { +public: + typedef boost::shared_ptr sptr; + + usb_endpoint( + libusb::device_handle::sptr handle, + int endpoint, + bool input, + size_t transfer_size, + size_t num_transfers + ); + + ~usb_endpoint(void); + + // Exposed interface for submitting / retrieving transfer buffers + + //! Submit a new transfer that was presumably just filled or emptied. + void submit(libusb_transfer *lut); + + /*! + * Get an available transfer: + * For inputs, this is a just filled transfer. + * For outputs, this is a just emptied transfer. + * \param timeout_ms the timeout to wait for a lut + * \return the transfer pointer or NULL if timeout + */ + libusb_transfer *get_lut_with_wait(size_t timeout_ms = 100); + + //Callback use only + void callback_handle_transfer(libusb_transfer *lut); + private: libusb::device_handle::sptr _handle; - libusb::session::sptr _session; int _endpoint; bool _input; size_t _transfer_size; size_t _num_transfers; - // Transfer state lists (transfers are free, pending, or completed) - std::list _free_list; - std::list _pending_list; - std::list _completed_list; + //! hold a bounded buffer of completed transfers + typedef bounded_buffer lut_buff_type; + lut_buff_type::sptr _completed_list; + + //! a list of all transfer structs we allocated + std::vector _all_luts; + + //! a list of shared arrays for the transfer buffers + std::vector > _buffers; // 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); - - // Debug use void print_transfer_status(libusb_transfer *lut); - -public: - usb_endpoint(libusb::device_handle::sptr handle, libusb::session::sptr sess, - int endpoint, bool input, size_t transfer_size, size_t num_transfers); - - ~usb_endpoint(); - - // Exposed interface for submitting / retrieving transfer buffers - 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); }; @@ -113,9 +121,8 @@ public: * it from the pending to completed status list. * \param lut pointer to libusb_transfer */ -static void callback(libusb_transfer *lut) -{ - usb_endpoint *endpoint = (usb_endpoint *) lut->user_data; +static void callback(libusb_transfer *lut){ + usb_endpoint *endpoint = (usb_endpoint *) lut->user_data; endpoint->callback_handle_transfer(lut); } @@ -124,14 +131,8 @@ static void callback(libusb_transfer *lut) * Accessor call to allow list access from callback space * \param pointer to libusb_transfer */ -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); +void usb_endpoint::callback_handle_transfer(libusb_transfer *lut){ + _completed_list->push_with_wait(lut); } @@ -142,18 +143,27 @@ void usb_endpoint::callback_handle_transfer(libusb_transfer *lut) * data is available. */ usb_endpoint::usb_endpoint( - libusb::device_handle::sptr handle, libusb::session::sptr sess, - int endpoint, bool input, size_t transfer_size, size_t num_transfers) - : _handle(handle), _session(sess), - _endpoint(endpoint), _input(input), - _transfer_size(transfer_size), _num_transfers(num_transfers) + libusb::device_handle::sptr handle, + int endpoint, + bool input, + size_t transfer_size, + size_t num_transfers +): + _handle(handle), + _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)); + _completed_list = lut_buff_type::make(num_transfers); - if (_input) - submit(free_list_get()); + for (size_t i = 0; i < _num_transfers; i++){ + _all_luts.push_back(allocate_transfer(_transfer_size)); + + //input luts are immediately submitted to be filled + //output luts go into the completed list as free buffers + if (_input) this->submit(_all_luts.back()); + else _completed_list->push_with_wait(_all_luts.back()); } } @@ -165,22 +175,21 @@ usb_endpoint::usb_endpoint( * the transfers. Libusb will deallocate the data buffer held by * each transfer. */ -usb_endpoint::~usb_endpoint() -{ - cancel_all(); - - while (!_pending_list.empty()) { - if (!reap_pending_list()) - std::cerr << "error: destructor failed to reap" << std::endl; +usb_endpoint::~usb_endpoint(void){ + //cancel all transfers + BOOST_FOREACH(libusb_transfer *lut, _all_luts){ + libusb_cancel_transfer(lut); } - while (!_completed_list.empty()) { - if (!reap_completed_list()) - std::cerr << "error: destructor failed to reap" << std::endl; - } + //collect canceled transfers (drain the queue) + libusb_transfer *lut; + while(_completed_list->pop_with_timed_wait( + lut, boost::posix_time::milliseconds(100) + )); - while (!_free_list.empty()) { - libusb_free_transfer(free_list_get()); + //free all transfers + BOOST_FOREACH(libusb_transfer *lut, _all_luts){ + libusb_free_transfer(lut); } } @@ -192,20 +201,21 @@ usb_endpoint::~usb_endpoint() * \param buff_len size of the individual buffer held by each transfer * \return pointer to an allocated libusb_transfer */ -libusb_transfer *usb_endpoint::allocate_transfer(int buff_len) -{ +libusb_transfer *usb_endpoint::allocate_transfer(int buff_len){ libusb_transfer *lut = libusb_alloc_transfer(0); - unsigned char *buff = new unsigned char[buff_len]; + boost::shared_array buff(new boost::uint8_t[buff_len]); + _buffers.push_back(buff); //store a reference to this shared array unsigned int endpoint = ((_endpoint & 0x7f) | (_input ? 0x80 : 0)); + libusb_transfer_cb_fn lut_callback = libusb_transfer_cb_fn(&callback); libusb_fill_bulk_transfer(lut, // transfer _handle->get(), // dev_handle endpoint, // endpoint - buff, // buffer + buff.get(), // buffer buff_len, // length - libusb_transfer_cb_fn(callback), // callback + lut_callback, // callback this, // user_data 0); // timeout return lut; @@ -218,95 +228,15 @@ libusb_transfer *usb_endpoint::allocate_transfer(int buff_len) * \param lut pointer to libusb_transfer * \return true on success or false on error */ -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. - * \param lut pointer to libusb_transfer to cancel - * \return true on success or false if transfer is not found - * - * 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::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 - * \return bool true if cancelation request is submitted - * - * Note: success only indicates submission of cancelation request. - * Sucessful cancelation is not known until the callback occurs. - */ -bool usb_endpoint::cancel_all() -{ - std::list::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. - * \return bool true if a libusb transfer is reaped, false otherwise - */ -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; +void usb_endpoint::submit(libusb_transfer *lut){ + UHD_ASSERT_THROW(libusb_submit_transfer(lut) == 0); } - /* * Print status errors of a completed transfer * \param lut pointer to an libusb_transfer */ -void usb_endpoint::print_transfer_status(libusb_transfer *lut) -{ +void usb_endpoint::print_transfer_status(libusb_transfer *lut){ switch (lut->status) { case LIBUSB_TRANSFER_COMPLETED: if (lut->actual_length < lut->length) { @@ -342,166 +272,14 @@ void usb_endpoint::print_transfer_status(libusb_transfer *lut) } } - -/* - * Reap pending transfers without timeout - * 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. - * \return true true if a transfer was reaped or false otherwise - */ -bool usb_endpoint::reap_pending_list() -{ - int retval; - - if ((retval = libusb_handle_events(_session->get_context())) < 0) { - std::cerr << "error: libusb_handle_events: " << retval << std::endl; - return false; - } - - return true; -} - - -/* - * Reap pending transfers with timeout - * 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. - * \return true if a transfer was reaped or false otherwise - */ -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(_session->get_context(), &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; - } -} - - -/* - * Get a free transfer - * The transfer has an empty data bufer for OUT requests - * \return pointer to a libusb_transfer - */ -libusb_transfer *usb_endpoint::get_free_transfer() -{ - if (_free_list.empty()) { - if (!reap_completed_list()) - return NULL; - } - - return free_list_get(); -} - - -/* - * Get a completed transfer - * The transfer has a full data buffer for IN requests - * \return pointer to libusb_transfer - */ -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 *usb_endpoint::get_lut_with_wait(size_t timeout_ms){ libusb_transfer *lut; - - if (_completed_list.empty()) { - return NULL; - } - else { - lut = _completed_list.front(); - _completed_list.pop_front(); - return lut; - } + if (_completed_list->pop_with_timed_wait( + lut, boost::posix_time::milliseconds(timeout_ms) + )) return lut; + return NULL; } - -/* - * 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::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 **********************************************************************/ @@ -515,17 +293,15 @@ bool usb_endpoint::pending_list_remove(libusb_transfer *lut) class libusb_managed_recv_buffer_impl : public managed_recv_buffer { public: libusb_managed_recv_buffer_impl(libusb_transfer *lut, - usb_endpoint *endpoint) + usb_endpoint::sptr 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; + ~libusb_managed_recv_buffer_impl(void){ + _endpoint->submit(_lut); } private: @@ -535,7 +311,7 @@ private: } libusb_transfer *_lut; - usb_endpoint *_endpoint; + usb_endpoint::sptr _endpoint; const boost::asio::const_buffer _buff; }; @@ -551,7 +327,7 @@ private: class libusb_managed_send_buffer_impl : public managed_send_buffer { public: libusb_managed_send_buffer_impl(libusb_transfer *lut, - usb_endpoint *endpoint, + usb_endpoint::sptr endpoint, size_t buff_size) : _buff(lut->buffer, buff_size), _committed(false) { @@ -559,8 +335,7 @@ public: _endpoint = endpoint; } - ~libusb_managed_send_buffer_impl() - { + ~libusb_managed_send_buffer_impl(void){ if (!_committed) { _lut->length = 0; _lut->actual_length = 0; @@ -580,12 +355,14 @@ public: _lut->length = num_bytes; _lut->actual_length = 0; - if (_endpoint->submit(_lut)) { + try{ + _endpoint->submit(_lut); _committed = true; return num_bytes; } - else { - return 0; + catch(const std::exception &e){ + std::cerr << "Error in commit: " << e.what() << std::endl; + return -1; } } @@ -596,7 +373,7 @@ private: } libusb_transfer *_lut; - usb_endpoint *_endpoint; + usb_endpoint::sptr _endpoint; const boost::asio::mutable_buffer _buff; bool _committed; }; @@ -608,11 +385,9 @@ private: class libusb_zero_copy_impl : public usb_zero_copy { private: - usb_endpoint *_rx_ep; - usb_endpoint *_tx_ep; + usb_endpoint::sptr _rx_ep, _tx_ep; libusb::device_handle::sptr _handle; - libusb::session::sptr _session; size_t _recv_buff_size; size_t _send_buff_size; @@ -626,8 +401,6 @@ public: 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); @@ -646,45 +419,38 @@ libusb_zero_copy_impl::libusb_zero_copy_impl(libusb::device_handle::sptr handle, unsigned int tx_endpoint, size_t buff_size, size_t block_size) - : _handle(handle), _session(libusb::session::get_global_session()), + : _handle(handle), _recv_buff_size(block_size), _send_buff_size(block_size), _num_frames(buff_size / block_size) { _handle->claim_interface(2 /*in interface*/); _handle->claim_interface(1 /*out interface*/); - _rx_ep = new usb_endpoint(_handle, // libusb device_handle - _session, // libusb session w/ context + _rx_ep = usb_endpoint::sptr(new usb_endpoint( + _handle, // libusb device_handle rx_endpoint, // USB endpoint number true, // IN endpoint _recv_buff_size, // buffer size per transfer - _num_frames); // number of libusb transfers + _num_frames // number of libusb transfers + )); - _tx_ep = new usb_endpoint(_handle, // libusb device_handle - _session, // libusb session w/ context + _tx_ep = usb_endpoint::sptr(new usb_endpoint( + _handle, // libusb device_handle tx_endpoint, // USB endpoint number false, // OUT endpoint _send_buff_size, // buffer size per transfer - _num_frames); // number of libusb transfers -} - - -libusb_zero_copy_impl::~libusb_zero_copy_impl() -{ - delete _rx_ep; - delete _tx_ep; + _num_frames // number of libusb transfers + )); } - /* * Construct a managed receive buffer from a completed libusb transfer * (happy with buffer full of data) obtained from the receive endpoint. * Return empty pointer if no transfer is available (timeout or error). * \return pointer to a managed receive buffer */ -managed_recv_buffer::sptr libusb_zero_copy_impl::get_recv_buff() -{ - libusb_transfer *lut = _rx_ep->get_completed_transfer(); +managed_recv_buffer::sptr libusb_zero_copy_impl::get_recv_buff(void){ + libusb_transfer *lut = _rx_ep->get_lut_with_wait(/* TODO timeout API */); if (lut == NULL) { return managed_recv_buffer::sptr(); } @@ -702,9 +468,8 @@ managed_recv_buffer::sptr libusb_zero_copy_impl::get_recv_buff() * (timeout or error). * \return pointer to a managed send buffer */ -managed_send_buffer::sptr libusb_zero_copy_impl::get_send_buff() -{ - libusb_transfer *lut = _tx_ep->get_free_transfer(); +managed_send_buffer::sptr libusb_zero_copy_impl::get_send_buff(void){ + libusb_transfer *lut = _tx_ep->get_lut_with_wait(/* TODO timeout API */); if (lut == NULL) { return managed_send_buffer::sptr(); } @@ -736,6 +501,3 @@ usb_zero_copy::sptr usb_zero_copy::make(usb_device_handle::sptr handle, buff_size, block_size)); } - - - -- cgit v1.2.3 From e4fffed05dda57bb37d693a3a26ea6a903c925f7 Mon Sep 17 00:00:00 2001 From: Josh Blum Date: Thu, 30 Sep 2010 11:32:21 -0700 Subject: usb: set rt thread priority for the libusb event loop --- host/lib/transport/libusb1_base.cpp | 3 +++ 1 file changed, 3 insertions(+) (limited to 'host/lib/transport/libusb1_base.cpp') diff --git a/host/lib/transport/libusb1_base.cpp b/host/lib/transport/libusb1_base.cpp index 26e864459..49f524a32 100644 --- a/host/lib/transport/libusb1_base.cpp +++ b/host/lib/transport/libusb1_base.cpp @@ -16,6 +16,7 @@ // #include "libusb1_base.hpp" +#include #include #include #include @@ -23,6 +24,7 @@ #include #include +using namespace uhd; using namespace uhd::transport; /*********************************************************************** @@ -52,6 +54,7 @@ private: bool _running; void run_event_loop(void){ + set_thread_priority_safe(); _running = true; timeval tv; while(_running){ -- cgit v1.2.3 From 348b5b12ad9ec916c92d642bfb28e47264473535 Mon Sep 17 00:00:00 2001 From: Josh Blum Date: Fri, 1 Oct 2010 11:54:22 -0700 Subject: usb: catch open errors and print message, device: catch exceptions at discovery time --- host/lib/device.cpp | 18 ++++++++++++------ host/lib/transport/libusb1_base.cpp | 16 +++++++++++----- host/lib/usrp/usrp1/usrp1_impl.cpp | 28 +++++++++++++--------------- 3 files changed, 36 insertions(+), 26 deletions(-) (limited to 'host/lib/transport/libusb1_base.cpp') diff --git a/host/lib/device.cpp b/host/lib/device.cpp index d575ebaab..386588a08 100644 --- a/host/lib/device.cpp +++ b/host/lib/device.cpp @@ -26,6 +26,7 @@ #include #include #include +#include using namespace uhd; @@ -73,12 +74,17 @@ device_addrs_t device::find(const device_addr_t &hint){ device_addrs_t device_addrs; BOOST_FOREACH(const dev_fcn_reg_t &fcn, get_dev_fcn_regs()){ - device_addrs_t discovered_addrs = fcn.get<0>()(hint); - device_addrs.insert( - device_addrs.begin(), - discovered_addrs.begin(), - discovered_addrs.end() - ); + try{ + device_addrs_t discovered_addrs = fcn.get<0>()(hint); + device_addrs.insert( + device_addrs.begin(), + discovered_addrs.begin(), + discovered_addrs.end() + ); + } + catch(const std::exception &e){ + std::cerr << "Device discovery error: " << e.what() << std::endl; + } } return device_addrs; diff --git a/host/lib/transport/libusb1_base.cpp b/host/lib/transport/libusb1_base.cpp index 49f524a32..910b04fc8 100644 --- a/host/lib/transport/libusb1_base.cpp +++ b/host/lib/transport/libusb1_base.cpp @@ -213,15 +213,21 @@ private: libusb::device_handle::sptr libusb::device_handle::get_cached_handle(device::sptr dev){ static uhd::dict > handles; - //not expired -> get existing session + //not expired -> get existing handle if (handles.has_key(dev->get()) and not handles[dev->get()].expired()){ return handles[dev->get()].lock(); } - //create a new global session - sptr new_handle(new libusb_device_handle_impl(dev)); - handles[dev->get()] = new_handle; - return new_handle; + //create a new cached handle + try{ + sptr new_handle(new libusb_device_handle_impl(dev)); + handles[dev->get()] = new_handle; + return new_handle; + } + catch(const std::exception &e){ + std::cerr << "USB open failed: see the application notes for your device." << std::endl; + throw std::runtime_error(e.what()); + } } /*********************************************************************** diff --git a/host/lib/usrp/usrp1/usrp1_impl.cpp b/host/lib/usrp/usrp1/usrp1_impl.cpp index 793e3027d..156fc119f 100644 --- a/host/lib/usrp/usrp1/usrp1_impl.cpp +++ b/host/lib/usrp/usrp1/usrp1_impl.cpp @@ -83,9 +83,7 @@ static device_addrs_t usrp1_find(const device_addr_t &hint) //find the usrps and load firmware BOOST_FOREACH(usb_device_handle::sptr handle, usb_device_handle::get_device_list(vid, pid)) { - usb_control::sptr ctrl_transport = usb_control::make(handle); - usrp_ctrl::sptr usrp_ctrl = usrp_ctrl::make(ctrl_transport); - usrp_ctrl->usrp_load_firmware(usrp1_fw_image); + usrp_ctrl::make(usb_control::make(handle))->usrp_load_firmware(usrp1_fw_image); } //get descriptors again with serial number, but using the initialized VID/PID now since we have firmware @@ -93,13 +91,13 @@ static device_addrs_t usrp1_find(const device_addr_t &hint) pid = USRP1_PRODUCT_ID; BOOST_FOREACH(usb_device_handle::sptr handle, usb_device_handle::get_device_list(vid, pid)) { - device_addr_t new_addr; - new_addr["type"] = "usrp1"; - new_addr["serial"] = handle->get_serial(); - //this is a found usrp1 when a hint serial is not specified or it matches - if (not hint.has_key("serial") or hint["serial"] == new_addr["serial"]){ - usrp1_addrs.push_back(new_addr); - } + device_addr_t new_addr; + new_addr["type"] = "usrp1"; + new_addr["serial"] = handle->get_serial(); + //this is a found usrp1 when a hint serial is not specified or it matches + if (not hint.has_key("serial") or hint["serial"] == new_addr["serial"]){ + usrp1_addrs.push_back(new_addr); + } } return usrp1_addrs; @@ -109,12 +107,12 @@ static device_addrs_t usrp1_find(const device_addr_t &hint) * Make **********************************************************************/ template static output_type cast_from_dev_addr( - const device_addr_t &device_addr, - const std::string &key, - output_type def_val + const device_addr_t &device_addr, + const std::string &key, + output_type def_val ){ - return (device_addr.has_key(key))? - boost::lexical_cast(device_addr[key]) : def_val; + return (device_addr.has_key(key))? + boost::lexical_cast(device_addr[key]) : def_val; } static device::sptr usrp1_make(const device_addr_t &device_addr) -- cgit v1.2.3 From 0fc641042ef81731fa84f796fd983a5a602e260c Mon Sep 17 00:00:00 2001 From: Josh Blum Date: Tue, 5 Oct 2010 15:47:58 -0700 Subject: usb: moved event handler thread into the zero copy interface --- host/docs/transport.rst | 1 + host/lib/transport/libusb1_base.cpp | 18 ------------------ host/lib/transport/libusb1_zero_copy.cpp | 28 ++++++++++++++++++++++++++++ 3 files changed, 29 insertions(+), 18 deletions(-) (limited to 'host/lib/transport/libusb1_base.cpp') diff --git a/host/docs/transport.rst b/host/docs/transport.rst index d6a146c67..30fc1d78f 100644 --- a/host/docs/transport.rst +++ b/host/docs/transport.rst @@ -79,3 +79,4 @@ The following parameters can be used to alter the transport's default behavior: * **num_recv_frames:** The number of simultaneous receive transfers * **send_frame_size:** The size of a single send transfers in bytes * **num_send_frames:** The number of simultaneous send transfers +* **concurrency_hint:** The number of threads to run the event handler diff --git a/host/lib/transport/libusb1_base.cpp b/host/lib/transport/libusb1_base.cpp index 910b04fc8..cfa77d9ca 100644 --- a/host/lib/transport/libusb1_base.cpp +++ b/host/lib/transport/libusb1_base.cpp @@ -16,12 +16,10 @@ // #include "libusb1_base.hpp" -#include #include #include #include #include -#include #include using namespace uhd; @@ -35,12 +33,9 @@ public: libusb_session_impl(void){ UHD_ASSERT_THROW(libusb_init(&_context) == 0); libusb_set_debug(_context, debug_level); - _thread_group.create_thread(boost::bind(&libusb_session_impl::run_event_loop, this)); } ~libusb_session_impl(void){ - _running = false; - _thread_group.join_all(); libusb_exit(_context); } @@ -50,19 +45,6 @@ public: private: libusb_context *_context; - boost::thread_group _thread_group; - bool _running; - - void run_event_loop(void){ - set_thread_priority_safe(); - _running = true; - timeval tv; - while(_running){ - tv.tv_sec = 0; - tv.tv_usec = 100000; //100ms - libusb_handle_events_timeout(this->get_context(), &tv); - } - } }; libusb::session::sptr libusb::session::get_global_session(void){ diff --git a/host/lib/transport/libusb1_zero_copy.cpp b/host/lib/transport/libusb1_zero_copy.cpp index df6db1eb9..f589d7c77 100644 --- a/host/lib/transport/libusb1_zero_copy.cpp +++ b/host/lib/transport/libusb1_zero_copy.cpp @@ -18,6 +18,7 @@ #include "libusb1_base.hpp" #include #include +#include #include #include #include @@ -290,6 +291,11 @@ public: const device_addr_t &hints ); + ~libusb_zero_copy_impl(void){ + _threads_running = false; + _thread_group.join_all(); + } + managed_recv_buffer::sptr get_recv_buff(double); managed_send_buffer::sptr get_send_buff(double); @@ -318,6 +324,22 @@ private: const size_t _recv_frame_size, _num_recv_frames; const size_t _send_frame_size, _num_send_frames; usb_endpoint::sptr _recv_ep, _send_ep; + + //event handler threads + boost::thread_group _thread_group; + bool _threads_running; + + void run_event_loop(void){ + set_thread_priority_safe(); + libusb::session::sptr session = libusb::session::get_global_session(); + _threads_running = true; + while(_threads_running){ + timeval tv; + tv.tv_sec = 0; + tv.tv_usec = 100000; //100ms + libusb_handle_events_timeout(session->get_context(), &tv); + } + } }; /* @@ -355,6 +377,12 @@ libusb_zero_copy_impl::libusb_zero_copy_impl( this->get_send_frame_size(), // buffer size per transfer this->get_num_send_frames() // number of libusb transfers )); + + //spawn the event handler threads + size_t concurrency = hints.cast("concurrency_hint", 1); + for (size_t i = 0; i < concurrency; i++) _thread_group.create_thread( + boost::bind(&libusb_zero_copy_impl::run_event_loop, this) + ); } /* -- cgit v1.2.3