libusb1-1.7/0000755000175000017500000000000013404567420014065 5ustar vincentvincent00000000000000libusb1-1.7/PKG-INFO0000644000175000017500000003716313404567420015174 0ustar vincentvincent00000000000000Metadata-Version: 1.1 Name: libusb1 Version: 1.7 Summary: Pure-python wrapper for libusb-1.0 Home-page: http://github.com/vpelletier/python-libusb1 Author: Vincent Pelletier Author-email: plr.vincent@gmail.com License: LGPLv2.1+ Description: .. contents:: Pure-python wrapper for libusb-1.0 Supports all transfer types, both in synchronous and asynchronous mode. Home: http://github.com/vpelletier/python-libusb1 PyPI: http://pypi.python.org/pypi/libusb1 .. role:: c_code(code) :language: c .. role:: python_code(code) :language: python Dependencies ============ - CPython_ 2.7+ or 3.4+, pypy_ 2.0+. Older versions may work, but are not recommended as there is no automated regression testing set up for them. - libusb-1.0_ Supported OSes ============== python-libusb1 can be expected to work on: - GNU/Linux - Windows [#]_ native dll or via Cygwin_ - OSX [#]_ via MacPorts_, Fink_ or Homebrew_ - FreeBSD (including Debian GNU/kFreeBSD) - OpenBSD .. [#] Beware of libusb-win32, which implements 0.1 API, not 1.0 . .. [#] Beware of possible lack of select.poll if you want to use asynchronous API. Installation ============ Releases from PyPI, with name *libusb1*. Installing from command line:: $ pip install libusb1 or:: $ easy_install libusb1 Latest version from source tree:: $ git clone https://github.com/vpelletier/python-libusb1.git $ cd python-libusb1 $ python setup.py install Usage ===== Finding a device and gaining exclusive access: .. code:: python import usb1 with usb1.USBContext() as context: handle = context.openByVendorIDAndProductID( VENDOR_ID, PRODUCT_ID, skip_on_error=True, ) if handle is None: # Device not present, or user is not allowed to access device. with handle.claimInterface(INTERFACE): # Do stuff with endpoints on claimed interface. Synchronous I/O: .. code:: python while True: data = handle.bulkRead(ENDPOINT, BUFFER_SIZE) # Process data... Asynchronous I/O, with more error handling: .. code:: python def processReceivedData(transfer): if transfer.getStatus() != usb1.TRANSFER_COMPLETED: # Transfer did not complete successfully, there is no data to read. # This example does not resubmit transfers on errors. You may want # to resubmit in some cases (timeout, ...). return data = handle.getBuffer()[:transfer.getActualLength()] # Process data... # Resubmit transfer once data is processed. transfer.submit() # Build a list of transfer objects and submit them to prime the pump. transfer_list = [] for _ in xrange(TRANSFER_COUNT): transfer = handle.getTransfer() transfer.setBulk( usb1.ENDPOINT_IN | ENDPOINT, BUFFER_SIZE, callback=processReceivedData, ) transfer.submit() transfer_list.append(transfer) # Loop as long as there is at least one submitted transfer. while any(x.isSubmitted() for x in reader_list): try: context.handleEvents() except usb1.USBErrorInterrupted: pass For more, see the ``example`` directory. Documentation ============= python-libusb1 main documentation is accessible with python's standard ``pydoc`` command. python-libusb1 follows libusb-1.0 documentation as closely as possible, without taking decisions for you. Thanks to this, python-libusb1 does not need to duplicate the nice existing `libusb1.0 documentation`_. Some description is needed though on how to jump from libusb-1.0 documentation to python-libusb1, and vice-versa: ``usb1`` module groups libusb-1.0 functions as class methods. The first parameter (when it's a ``libusb_...`` pointer) defined the class the fonction belongs to. For example: - :c_code:`int libusb_init (libusb_context **context)` becomes USBContext class constructor, :python_code:`USBContext.__init__(self)` - :c_code:`ssize_t libusb_get_device_list (libusb_context *ctx, libusb_device ***list)` becomes an USBContext method, returning a list of USBDevice instances, :python_code:`USBDevice.getDeviceList(self)` - :c_code:`uint8_t libusb_get_bus_number (libusb_device *dev)` becomes an USBDevice method, :python_code:`USBDevice.getBusNumber(self)` Error statuses are converted into :python_code:`usb1.USBError` exceptions, with status as ``value`` instance property. ``usb1`` module also defines a few more functions and classes, which are otherwise not so convenient to call from Python: the event handling API needed by async API. History ======= 0.0.1 ----- Initial release 0.1.1 ----- Massive rework of usb1.py, making it more python-ish and fixing some memory leaks. 0.1.2 ----- Deprecate "transfer" constructor parameter to allow instance reuse. 0.1.3 ----- Some work on isochronous "in" transfers. They don't raise exceptions anymore, but data validity and python-induced latency impact weren't properly checked. 0.2.0 ----- Fix asynchronous configuration transfers. Stand-alone polling thread for multi-threaded apps. More libusb methods exposed on objects, including ones not yet part of released libusb versions (up to their commit 4630fc2). 2to3 friendly. Drop deprecated USBDevice.reprConfigurations method. 0.2.1 ----- Add FreeBSD support. 0.2.2 ----- Add Cygwin support. OpenBSD support checked (no change). 0.2.3 ----- Add fink and homebrew support on OSX. Drop PATH_MAX definition. Try harder when looking for libusb. 1.0.0 ----- Fix FreeBSD ABI compatibility. Easier to list connected devices. Easier to terminate all async transfers for clean exit. Fix few segfault causes. pypy support. 1.1.0 ----- Descriptor walk API documented. Version and capability APIs exposed. Some portability fixes (OSes, python versions). Isochronous transfer refuses to round transfer size. Better exception handling in enumeration. Add examples. Better documentation. 1.2.0 ----- Wrap hotplug API. Wrap port number API. Wrap kernel auto-detach API. Drop wrapper for libusb_strerror, with compatibility place-holder. Add a few new upstream enum values. 1.3.0 ----- **Backward-incompatible change**: Enum class now affects caller's local scope, not its global scope. This should not be of much importance, as: - This class is probably very little used outside libusb1.py - This class is probably mostly used at module level, where locals == globals. It is possible to get former behaviour by providing the new ``scope_dict`` parameter to ``Enum`` constructor:: SOME_ENUM = libusb1.Enum({...}, scope_dict=globals()) Improve start-up time on CPython by not importing standard ``inspect`` module. Fix some more USBTransfer memory leaks. Add Transfer.iterISO for more efficient isochronous reception. 1.3.1 ----- Fixed USBContext.waitForEvent. Fix typo in USBInterfaceSetting.getClassTuple method name. Backward compatibility preserved. Remove globals accesses from USBDeviceHandle destructor. Assorted documentation improvements. 1.3.2 ----- Made USBDevice instances hashable. Relaxed licensing by moving from GPL v2+ to LGPL v2.1+, for consistency with libusb1. 1.4.0 ----- Reduce (remove ?) the need to import libusb1 module by exposing USBError and constants in usb1 module. Fix libusb1.LIBUSB_ENDPOINT_ENDPOINT_MASK and libusb1.LIBUSB_ENDPOINT_DIR_MASK naming. Fix pydoc appearance of several USBContext methods. Define exception classes for each error values. 1.4.1 ----- Fix wheel generation (``python3 setup.py bdist_wheel``). 1.5.0 ----- controlWrite, bulkWrite and interruptWrite now reject (with TypeError) numeric values for ``data`` parameter. Fix libusb1.REQUEST_TYPE_* names (were TYPE_*). Preserve backward compatibility. Add USBContext.getDeviceIterator method. Rename USBContext.exit as USBContext.close for consistency with other USB* classes. Preserve backward compatibility. Make USBDeviceHandle.claimInterface a context manager, for easier interface releasing. 1.5.1 ----- Introduce USBPollerThread.stop . Fix USBDeviceHandle.getSupportedLanguageList bug when running under python 3. While fixing this bug it was realised that this method returned ctypes objects. This was not intended, and it now returns regular integers. 1.5.2 ----- Make USBTransfer.cancel raise specific error instances. 1.5.3 ----- Fix USBTransfer.cancel exception raising introduced in 1.5.2: it was accidentally becomming a bound method, preventing the raise to actually happen (in at least CPython 2.x) or raising type conversion errors (in at least CPython 3.5.2). 1.6 --- Improve asynchronous transfer performance: (very) suboptimal code was used to initialise asynchronous transfer buffer. As a consequence, usb1 now exposes ``bytearrays`` where it used to expose ``bytes`` or ``str`` objects. Deprecate libusb1 module import, which should not be needed since all (?) needed constants were re-bound to usb1 module. Move testUSB1 module inside usb1, to eventually only expose usb1 as top-level module. 1.6.1 ----- Fix getSupportedLanguageList. Fix and extend get{,ASCII}StringDescriptor . Fix iterISO and getISOBufferList. 1.6.2 ----- Fix getASCIIStringDescriptor: unlike getStringDescriptor, this returns only the payload of the string descriptor, without its header. 1.6.3 ----- Deprecate USBPollerThread . It is mileading users for which the simple version (a thread calling ``USBContext.handleEvents``) would be enough. And for more advanced uses (ie, actually needing to poll non-libusb file descriptors), this class only works reliably with epoll: kqueue (which should tehcnically work) has a different API on python level, and poll (which has the same API as epoll on python level) lacks the critical ability to change the set of monitored file descriptors while a poll is already running, causing long pauses - if not deadlocks. 1.6.4 ----- Fix asynchronous control transfers. 1.6.5 ----- Document hotplug handler limitations. Run 2to3 when running setup.py with python3, and reduce differences with python3. Properly cast libusb_set_pollfd_notifiers arguments. Fix null pointer value: POINTER(None) is the type of a pointer which may be a null pointer, which falls back to c_void_p. But c_void_p() is an actual null pointer. 1.6.6 ----- Expose bare string descriptors (aka string indexes) on USBDevice. 1.6.7 ----- get{,ASCII}StringDescriptor now return None for descriptor 0 instead of raising UnicodeDecodeError. Use getSupportedLanguageList to access it. Moved getManufacturer, getProduct and getSerialNumber to USBDeviceHandle. Kept shortcuts for these on USBDevice. 1.7 --- get{,ASCII}StringDescriptor now return None for descriptor 0, use getSupportedLanguageList to get its content. getManufacturer, getProduct and getSerialNumber are now on USBDeviceHandle, with backward-compatibility aliases on their original location. Synchronous bulk and interrupt API exposes number of bytes sent and received bytes even when a timeout occurs. .. _CPython: http://www.python.org/ .. _pypy: http://pypy.org/ .. _Cygwin: https://www.cygwin.com/ .. _MacPorts: https://www.macports.org/ .. _Fink: http://www.finkproject.org/ .. _Homebrew: http://brew.sh/ .. _libusb-1.0: https://github.com/libusb/libusb/wiki/ .. _libusb1.0 documentation: http://libusb.sourceforge.net/api-1.0/ Keywords: usb libusb Platform: any Classifier: Intended Audience :: Developers Classifier: License :: OSI Approved :: GNU Lesser General Public License v2 or later (LGPLv2+) Classifier: Operating System :: OS Independent Classifier: Programming Language :: Python :: 2.4 Classifier: Programming Language :: Python :: 2.5 Classifier: Programming Language :: Python :: 2.6 Classifier: Programming Language :: Python :: 2.7 Classifier: Programming Language :: Python :: 3 Classifier: Programming Language :: Python :: 3.4 Classifier: Programming Language :: Python :: 3.5 Classifier: Programming Language :: Python :: 3.6 Classifier: Programming Language :: Python :: 3.7 Classifier: Programming Language :: Python :: Implementation :: CPython Classifier: Programming Language :: Python :: Implementation :: PyPy Classifier: Topic :: Software Development :: Libraries Classifier: Topic :: System :: Hardware :: Hardware Drivers libusb1-1.7/README.rst0000644000175000017500000002564413404567324015572 0ustar vincentvincent00000000000000Pure-python wrapper for libusb-1.0 Supports all transfer types, both in synchronous and asynchronous mode. Home: http://github.com/vpelletier/python-libusb1 PyPI: http://pypi.python.org/pypi/libusb1 .. role:: c_code(code) :language: c .. role:: python_code(code) :language: python Dependencies ============ - CPython_ 2.7+ or 3.4+, pypy_ 2.0+. Older versions may work, but are not recommended as there is no automated regression testing set up for them. - libusb-1.0_ Supported OSes ============== python-libusb1 can be expected to work on: - GNU/Linux - Windows [#]_ native dll or via Cygwin_ - OSX [#]_ via MacPorts_, Fink_ or Homebrew_ - FreeBSD (including Debian GNU/kFreeBSD) - OpenBSD .. [#] Beware of libusb-win32, which implements 0.1 API, not 1.0 . .. [#] Beware of possible lack of select.poll if you want to use asynchronous API. Installation ============ Releases from PyPI, with name *libusb1*. Installing from command line:: $ pip install libusb1 or:: $ easy_install libusb1 Latest version from source tree:: $ git clone https://github.com/vpelletier/python-libusb1.git $ cd python-libusb1 $ python setup.py install Usage ===== Finding a device and gaining exclusive access: .. code:: python import usb1 with usb1.USBContext() as context: handle = context.openByVendorIDAndProductID( VENDOR_ID, PRODUCT_ID, skip_on_error=True, ) if handle is None: # Device not present, or user is not allowed to access device. with handle.claimInterface(INTERFACE): # Do stuff with endpoints on claimed interface. Synchronous I/O: .. code:: python while True: data = handle.bulkRead(ENDPOINT, BUFFER_SIZE) # Process data... Asynchronous I/O, with more error handling: .. code:: python def processReceivedData(transfer): if transfer.getStatus() != usb1.TRANSFER_COMPLETED: # Transfer did not complete successfully, there is no data to read. # This example does not resubmit transfers on errors. You may want # to resubmit in some cases (timeout, ...). return data = handle.getBuffer()[:transfer.getActualLength()] # Process data... # Resubmit transfer once data is processed. transfer.submit() # Build a list of transfer objects and submit them to prime the pump. transfer_list = [] for _ in xrange(TRANSFER_COUNT): transfer = handle.getTransfer() transfer.setBulk( usb1.ENDPOINT_IN | ENDPOINT, BUFFER_SIZE, callback=processReceivedData, ) transfer.submit() transfer_list.append(transfer) # Loop as long as there is at least one submitted transfer. while any(x.isSubmitted() for x in reader_list): try: context.handleEvents() except usb1.USBErrorInterrupted: pass For more, see the ``example`` directory. Documentation ============= python-libusb1 main documentation is accessible with python's standard ``pydoc`` command. python-libusb1 follows libusb-1.0 documentation as closely as possible, without taking decisions for you. Thanks to this, python-libusb1 does not need to duplicate the nice existing `libusb1.0 documentation`_. Some description is needed though on how to jump from libusb-1.0 documentation to python-libusb1, and vice-versa: ``usb1`` module groups libusb-1.0 functions as class methods. The first parameter (when it's a ``libusb_...`` pointer) defined the class the fonction belongs to. For example: - :c_code:`int libusb_init (libusb_context **context)` becomes USBContext class constructor, :python_code:`USBContext.__init__(self)` - :c_code:`ssize_t libusb_get_device_list (libusb_context *ctx, libusb_device ***list)` becomes an USBContext method, returning a list of USBDevice instances, :python_code:`USBDevice.getDeviceList(self)` - :c_code:`uint8_t libusb_get_bus_number (libusb_device *dev)` becomes an USBDevice method, :python_code:`USBDevice.getBusNumber(self)` Error statuses are converted into :python_code:`usb1.USBError` exceptions, with status as ``value`` instance property. ``usb1`` module also defines a few more functions and classes, which are otherwise not so convenient to call from Python: the event handling API needed by async API. History ======= 0.0.1 ----- Initial release 0.1.1 ----- Massive rework of usb1.py, making it more python-ish and fixing some memory leaks. 0.1.2 ----- Deprecate "transfer" constructor parameter to allow instance reuse. 0.1.3 ----- Some work on isochronous "in" transfers. They don't raise exceptions anymore, but data validity and python-induced latency impact weren't properly checked. 0.2.0 ----- Fix asynchronous configuration transfers. Stand-alone polling thread for multi-threaded apps. More libusb methods exposed on objects, including ones not yet part of released libusb versions (up to their commit 4630fc2). 2to3 friendly. Drop deprecated USBDevice.reprConfigurations method. 0.2.1 ----- Add FreeBSD support. 0.2.2 ----- Add Cygwin support. OpenBSD support checked (no change). 0.2.3 ----- Add fink and homebrew support on OSX. Drop PATH_MAX definition. Try harder when looking for libusb. 1.0.0 ----- Fix FreeBSD ABI compatibility. Easier to list connected devices. Easier to terminate all async transfers for clean exit. Fix few segfault causes. pypy support. 1.1.0 ----- Descriptor walk API documented. Version and capability APIs exposed. Some portability fixes (OSes, python versions). Isochronous transfer refuses to round transfer size. Better exception handling in enumeration. Add examples. Better documentation. 1.2.0 ----- Wrap hotplug API. Wrap port number API. Wrap kernel auto-detach API. Drop wrapper for libusb_strerror, with compatibility place-holder. Add a few new upstream enum values. 1.3.0 ----- **Backward-incompatible change**: Enum class now affects caller's local scope, not its global scope. This should not be of much importance, as: - This class is probably very little used outside libusb1.py - This class is probably mostly used at module level, where locals == globals. It is possible to get former behaviour by providing the new ``scope_dict`` parameter to ``Enum`` constructor:: SOME_ENUM = libusb1.Enum({...}, scope_dict=globals()) Improve start-up time on CPython by not importing standard ``inspect`` module. Fix some more USBTransfer memory leaks. Add Transfer.iterISO for more efficient isochronous reception. 1.3.1 ----- Fixed USBContext.waitForEvent. Fix typo in USBInterfaceSetting.getClassTuple method name. Backward compatibility preserved. Remove globals accesses from USBDeviceHandle destructor. Assorted documentation improvements. 1.3.2 ----- Made USBDevice instances hashable. Relaxed licensing by moving from GPL v2+ to LGPL v2.1+, for consistency with libusb1. 1.4.0 ----- Reduce (remove ?) the need to import libusb1 module by exposing USBError and constants in usb1 module. Fix libusb1.LIBUSB_ENDPOINT_ENDPOINT_MASK and libusb1.LIBUSB_ENDPOINT_DIR_MASK naming. Fix pydoc appearance of several USBContext methods. Define exception classes for each error values. 1.4.1 ----- Fix wheel generation (``python3 setup.py bdist_wheel``). 1.5.0 ----- controlWrite, bulkWrite and interruptWrite now reject (with TypeError) numeric values for ``data`` parameter. Fix libusb1.REQUEST_TYPE_* names (were TYPE_*). Preserve backward compatibility. Add USBContext.getDeviceIterator method. Rename USBContext.exit as USBContext.close for consistency with other USB* classes. Preserve backward compatibility. Make USBDeviceHandle.claimInterface a context manager, for easier interface releasing. 1.5.1 ----- Introduce USBPollerThread.stop . Fix USBDeviceHandle.getSupportedLanguageList bug when running under python 3. While fixing this bug it was realised that this method returned ctypes objects. This was not intended, and it now returns regular integers. 1.5.2 ----- Make USBTransfer.cancel raise specific error instances. 1.5.3 ----- Fix USBTransfer.cancel exception raising introduced in 1.5.2: it was accidentally becomming a bound method, preventing the raise to actually happen (in at least CPython 2.x) or raising type conversion errors (in at least CPython 3.5.2). 1.6 --- Improve asynchronous transfer performance: (very) suboptimal code was used to initialise asynchronous transfer buffer. As a consequence, usb1 now exposes ``bytearrays`` where it used to expose ``bytes`` or ``str`` objects. Deprecate libusb1 module import, which should not be needed since all (?) needed constants were re-bound to usb1 module. Move testUSB1 module inside usb1, to eventually only expose usb1 as top-level module. 1.6.1 ----- Fix getSupportedLanguageList. Fix and extend get{,ASCII}StringDescriptor . Fix iterISO and getISOBufferList. 1.6.2 ----- Fix getASCIIStringDescriptor: unlike getStringDescriptor, this returns only the payload of the string descriptor, without its header. 1.6.3 ----- Deprecate USBPollerThread . It is mileading users for which the simple version (a thread calling ``USBContext.handleEvents``) would be enough. And for more advanced uses (ie, actually needing to poll non-libusb file descriptors), this class only works reliably with epoll: kqueue (which should tehcnically work) has a different API on python level, and poll (which has the same API as epoll on python level) lacks the critical ability to change the set of monitored file descriptors while a poll is already running, causing long pauses - if not deadlocks. 1.6.4 ----- Fix asynchronous control transfers. 1.6.5 ----- Document hotplug handler limitations. Run 2to3 when running setup.py with python3, and reduce differences with python3. Properly cast libusb_set_pollfd_notifiers arguments. Fix null pointer value: POINTER(None) is the type of a pointer which may be a null pointer, which falls back to c_void_p. But c_void_p() is an actual null pointer. 1.6.6 ----- Expose bare string descriptors (aka string indexes) on USBDevice. 1.6.7 ----- get{,ASCII}StringDescriptor now return None for descriptor 0 instead of raising UnicodeDecodeError. Use getSupportedLanguageList to access it. Moved getManufacturer, getProduct and getSerialNumber to USBDeviceHandle. Kept shortcuts for these on USBDevice. 1.7 --- get{,ASCII}StringDescriptor now return None for descriptor 0, use getSupportedLanguageList to get its content. getManufacturer, getProduct and getSerialNumber are now on USBDeviceHandle, with backward-compatibility aliases on their original location. Synchronous bulk and interrupt API exposes number of bytes sent and received bytes even when a timeout occurs. .. _CPython: http://www.python.org/ .. _pypy: http://pypy.org/ .. _Cygwin: https://www.cygwin.com/ .. _MacPorts: https://www.macports.org/ .. _Fink: http://www.finkproject.org/ .. _Homebrew: http://brew.sh/ .. _libusb-1.0: https://github.com/libusb/libusb/wiki/ .. _libusb1.0 documentation: http://libusb.sourceforge.net/api-1.0/ libusb1-1.7/libusb1.egg-info/0000755000175000017500000000000013404567420017120 5ustar vincentvincent00000000000000libusb1-1.7/libusb1.egg-info/PKG-INFO0000644000175000017500000003716313404567420020227 0ustar vincentvincent00000000000000Metadata-Version: 1.1 Name: libusb1 Version: 1.7 Summary: Pure-python wrapper for libusb-1.0 Home-page: http://github.com/vpelletier/python-libusb1 Author: Vincent Pelletier Author-email: plr.vincent@gmail.com License: LGPLv2.1+ Description: .. contents:: Pure-python wrapper for libusb-1.0 Supports all transfer types, both in synchronous and asynchronous mode. Home: http://github.com/vpelletier/python-libusb1 PyPI: http://pypi.python.org/pypi/libusb1 .. role:: c_code(code) :language: c .. role:: python_code(code) :language: python Dependencies ============ - CPython_ 2.7+ or 3.4+, pypy_ 2.0+. Older versions may work, but are not recommended as there is no automated regression testing set up for them. - libusb-1.0_ Supported OSes ============== python-libusb1 can be expected to work on: - GNU/Linux - Windows [#]_ native dll or via Cygwin_ - OSX [#]_ via MacPorts_, Fink_ or Homebrew_ - FreeBSD (including Debian GNU/kFreeBSD) - OpenBSD .. [#] Beware of libusb-win32, which implements 0.1 API, not 1.0 . .. [#] Beware of possible lack of select.poll if you want to use asynchronous API. Installation ============ Releases from PyPI, with name *libusb1*. Installing from command line:: $ pip install libusb1 or:: $ easy_install libusb1 Latest version from source tree:: $ git clone https://github.com/vpelletier/python-libusb1.git $ cd python-libusb1 $ python setup.py install Usage ===== Finding a device and gaining exclusive access: .. code:: python import usb1 with usb1.USBContext() as context: handle = context.openByVendorIDAndProductID( VENDOR_ID, PRODUCT_ID, skip_on_error=True, ) if handle is None: # Device not present, or user is not allowed to access device. with handle.claimInterface(INTERFACE): # Do stuff with endpoints on claimed interface. Synchronous I/O: .. code:: python while True: data = handle.bulkRead(ENDPOINT, BUFFER_SIZE) # Process data... Asynchronous I/O, with more error handling: .. code:: python def processReceivedData(transfer): if transfer.getStatus() != usb1.TRANSFER_COMPLETED: # Transfer did not complete successfully, there is no data to read. # This example does not resubmit transfers on errors. You may want # to resubmit in some cases (timeout, ...). return data = handle.getBuffer()[:transfer.getActualLength()] # Process data... # Resubmit transfer once data is processed. transfer.submit() # Build a list of transfer objects and submit them to prime the pump. transfer_list = [] for _ in xrange(TRANSFER_COUNT): transfer = handle.getTransfer() transfer.setBulk( usb1.ENDPOINT_IN | ENDPOINT, BUFFER_SIZE, callback=processReceivedData, ) transfer.submit() transfer_list.append(transfer) # Loop as long as there is at least one submitted transfer. while any(x.isSubmitted() for x in reader_list): try: context.handleEvents() except usb1.USBErrorInterrupted: pass For more, see the ``example`` directory. Documentation ============= python-libusb1 main documentation is accessible with python's standard ``pydoc`` command. python-libusb1 follows libusb-1.0 documentation as closely as possible, without taking decisions for you. Thanks to this, python-libusb1 does not need to duplicate the nice existing `libusb1.0 documentation`_. Some description is needed though on how to jump from libusb-1.0 documentation to python-libusb1, and vice-versa: ``usb1`` module groups libusb-1.0 functions as class methods. The first parameter (when it's a ``libusb_...`` pointer) defined the class the fonction belongs to. For example: - :c_code:`int libusb_init (libusb_context **context)` becomes USBContext class constructor, :python_code:`USBContext.__init__(self)` - :c_code:`ssize_t libusb_get_device_list (libusb_context *ctx, libusb_device ***list)` becomes an USBContext method, returning a list of USBDevice instances, :python_code:`USBDevice.getDeviceList(self)` - :c_code:`uint8_t libusb_get_bus_number (libusb_device *dev)` becomes an USBDevice method, :python_code:`USBDevice.getBusNumber(self)` Error statuses are converted into :python_code:`usb1.USBError` exceptions, with status as ``value`` instance property. ``usb1`` module also defines a few more functions and classes, which are otherwise not so convenient to call from Python: the event handling API needed by async API. History ======= 0.0.1 ----- Initial release 0.1.1 ----- Massive rework of usb1.py, making it more python-ish and fixing some memory leaks. 0.1.2 ----- Deprecate "transfer" constructor parameter to allow instance reuse. 0.1.3 ----- Some work on isochronous "in" transfers. They don't raise exceptions anymore, but data validity and python-induced latency impact weren't properly checked. 0.2.0 ----- Fix asynchronous configuration transfers. Stand-alone polling thread for multi-threaded apps. More libusb methods exposed on objects, including ones not yet part of released libusb versions (up to their commit 4630fc2). 2to3 friendly. Drop deprecated USBDevice.reprConfigurations method. 0.2.1 ----- Add FreeBSD support. 0.2.2 ----- Add Cygwin support. OpenBSD support checked (no change). 0.2.3 ----- Add fink and homebrew support on OSX. Drop PATH_MAX definition. Try harder when looking for libusb. 1.0.0 ----- Fix FreeBSD ABI compatibility. Easier to list connected devices. Easier to terminate all async transfers for clean exit. Fix few segfault causes. pypy support. 1.1.0 ----- Descriptor walk API documented. Version and capability APIs exposed. Some portability fixes (OSes, python versions). Isochronous transfer refuses to round transfer size. Better exception handling in enumeration. Add examples. Better documentation. 1.2.0 ----- Wrap hotplug API. Wrap port number API. Wrap kernel auto-detach API. Drop wrapper for libusb_strerror, with compatibility place-holder. Add a few new upstream enum values. 1.3.0 ----- **Backward-incompatible change**: Enum class now affects caller's local scope, not its global scope. This should not be of much importance, as: - This class is probably very little used outside libusb1.py - This class is probably mostly used at module level, where locals == globals. It is possible to get former behaviour by providing the new ``scope_dict`` parameter to ``Enum`` constructor:: SOME_ENUM = libusb1.Enum({...}, scope_dict=globals()) Improve start-up time on CPython by not importing standard ``inspect`` module. Fix some more USBTransfer memory leaks. Add Transfer.iterISO for more efficient isochronous reception. 1.3.1 ----- Fixed USBContext.waitForEvent. Fix typo in USBInterfaceSetting.getClassTuple method name. Backward compatibility preserved. Remove globals accesses from USBDeviceHandle destructor. Assorted documentation improvements. 1.3.2 ----- Made USBDevice instances hashable. Relaxed licensing by moving from GPL v2+ to LGPL v2.1+, for consistency with libusb1. 1.4.0 ----- Reduce (remove ?) the need to import libusb1 module by exposing USBError and constants in usb1 module. Fix libusb1.LIBUSB_ENDPOINT_ENDPOINT_MASK and libusb1.LIBUSB_ENDPOINT_DIR_MASK naming. Fix pydoc appearance of several USBContext methods. Define exception classes for each error values. 1.4.1 ----- Fix wheel generation (``python3 setup.py bdist_wheel``). 1.5.0 ----- controlWrite, bulkWrite and interruptWrite now reject (with TypeError) numeric values for ``data`` parameter. Fix libusb1.REQUEST_TYPE_* names (were TYPE_*). Preserve backward compatibility. Add USBContext.getDeviceIterator method. Rename USBContext.exit as USBContext.close for consistency with other USB* classes. Preserve backward compatibility. Make USBDeviceHandle.claimInterface a context manager, for easier interface releasing. 1.5.1 ----- Introduce USBPollerThread.stop . Fix USBDeviceHandle.getSupportedLanguageList bug when running under python 3. While fixing this bug it was realised that this method returned ctypes objects. This was not intended, and it now returns regular integers. 1.5.2 ----- Make USBTransfer.cancel raise specific error instances. 1.5.3 ----- Fix USBTransfer.cancel exception raising introduced in 1.5.2: it was accidentally becomming a bound method, preventing the raise to actually happen (in at least CPython 2.x) or raising type conversion errors (in at least CPython 3.5.2). 1.6 --- Improve asynchronous transfer performance: (very) suboptimal code was used to initialise asynchronous transfer buffer. As a consequence, usb1 now exposes ``bytearrays`` where it used to expose ``bytes`` or ``str`` objects. Deprecate libusb1 module import, which should not be needed since all (?) needed constants were re-bound to usb1 module. Move testUSB1 module inside usb1, to eventually only expose usb1 as top-level module. 1.6.1 ----- Fix getSupportedLanguageList. Fix and extend get{,ASCII}StringDescriptor . Fix iterISO and getISOBufferList. 1.6.2 ----- Fix getASCIIStringDescriptor: unlike getStringDescriptor, this returns only the payload of the string descriptor, without its header. 1.6.3 ----- Deprecate USBPollerThread . It is mileading users for which the simple version (a thread calling ``USBContext.handleEvents``) would be enough. And for more advanced uses (ie, actually needing to poll non-libusb file descriptors), this class only works reliably with epoll: kqueue (which should tehcnically work) has a different API on python level, and poll (which has the same API as epoll on python level) lacks the critical ability to change the set of monitored file descriptors while a poll is already running, causing long pauses - if not deadlocks. 1.6.4 ----- Fix asynchronous control transfers. 1.6.5 ----- Document hotplug handler limitations. Run 2to3 when running setup.py with python3, and reduce differences with python3. Properly cast libusb_set_pollfd_notifiers arguments. Fix null pointer value: POINTER(None) is the type of a pointer which may be a null pointer, which falls back to c_void_p. But c_void_p() is an actual null pointer. 1.6.6 ----- Expose bare string descriptors (aka string indexes) on USBDevice. 1.6.7 ----- get{,ASCII}StringDescriptor now return None for descriptor 0 instead of raising UnicodeDecodeError. Use getSupportedLanguageList to access it. Moved getManufacturer, getProduct and getSerialNumber to USBDeviceHandle. Kept shortcuts for these on USBDevice. 1.7 --- get{,ASCII}StringDescriptor now return None for descriptor 0, use getSupportedLanguageList to get its content. getManufacturer, getProduct and getSerialNumber are now on USBDeviceHandle, with backward-compatibility aliases on their original location. Synchronous bulk and interrupt API exposes number of bytes sent and received bytes even when a timeout occurs. .. _CPython: http://www.python.org/ .. _pypy: http://pypy.org/ .. _Cygwin: https://www.cygwin.com/ .. _MacPorts: https://www.macports.org/ .. _Fink: http://www.finkproject.org/ .. _Homebrew: http://brew.sh/ .. _libusb-1.0: https://github.com/libusb/libusb/wiki/ .. _libusb1.0 documentation: http://libusb.sourceforge.net/api-1.0/ Keywords: usb libusb Platform: any Classifier: Intended Audience :: Developers Classifier: License :: OSI Approved :: GNU Lesser General Public License v2 or later (LGPLv2+) Classifier: Operating System :: OS Independent Classifier: Programming Language :: Python :: 2.4 Classifier: Programming Language :: Python :: 2.5 Classifier: Programming Language :: Python :: 2.6 Classifier: Programming Language :: Python :: 2.7 Classifier: Programming Language :: Python :: 3 Classifier: Programming Language :: Python :: 3.4 Classifier: Programming Language :: Python :: 3.5 Classifier: Programming Language :: Python :: 3.6 Classifier: Programming Language :: Python :: 3.7 Classifier: Programming Language :: Python :: Implementation :: CPython Classifier: Programming Language :: Python :: Implementation :: PyPy Classifier: Topic :: Software Development :: Libraries Classifier: Topic :: System :: Hardware :: Hardware Drivers libusb1-1.7/libusb1.egg-info/dependency_links.txt0000644000175000017500000000000113404567420023166 0ustar vincentvincent00000000000000 libusb1-1.7/libusb1.egg-info/SOURCES.txt0000644000175000017500000000051713404567420021007 0ustar vincentvincent00000000000000COPYING COPYING.LESSER MANIFEST.in README.rst libusb1.py setup.py stdeb.cfg examples/README examples/hotplug.py examples/hotplug_advanced.py examples/listdevs.py libusb1.egg-info/PKG-INFO libusb1.egg-info/SOURCES.txt libusb1.egg-info/dependency_links.txt libusb1.egg-info/top_level.txt usb1/__init__.py usb1/libusb1.py usb1/testUSB1.pylibusb1-1.7/libusb1.egg-info/top_level.txt0000644000175000017500000000001513404567420021646 0ustar vincentvincent00000000000000libusb1 usb1 libusb1-1.7/setup.cfg0000644000175000017500000000004613404567420015706 0ustar vincentvincent00000000000000[egg_info] tag_build = tag_date = 0 libusb1-1.7/setup.py0000644000175000017500000000464313404567324015611 0ustar vincentvincent00000000000000# Copyright (C) 2010-2018 Vincent Pelletier # # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public # License as published by the Free Software Foundation; either # version 2.1 of the License, or (at your option) any later version. # # This library 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 # Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public # License along with this library; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA from setuptools import setup from codecs import open import os import sys long_description = open( os.path.join(os.path.dirname(__file__), 'README.rst'), encoding='utf8', ).read() try: next except NameError: # "next" builtin missing < 2.6 next = lambda x: x.next() setup( name='libusb1', description=next(x for x in long_description.splitlines() if x.strip()), long_description='.. contents::\n\n' + long_description, keywords='usb libusb', version='1.7', author='Vincent Pelletier', author_email='plr.vincent@gmail.com', url='http://github.com/vpelletier/python-libusb1', license='LGPLv2.1+', platforms=['any'], py_modules=['libusb1'], packages=['usb1'], classifiers=[ 'Intended Audience :: Developers', 'License :: OSI Approved :: GNU Lesser General Public License v2 or later (LGPLv2+)', 'Operating System :: OS Independent', 'Programming Language :: Python :: 2.4', 'Programming Language :: Python :: 2.5', 'Programming Language :: Python :: 2.6', 'Programming Language :: Python :: 2.7', 'Programming Language :: Python :: 3', 'Programming Language :: Python :: 3.4', 'Programming Language :: Python :: 3.5', 'Programming Language :: Python :: 3.6', 'Programming Language :: Python :: 3.7', 'Programming Language :: Python :: Implementation :: CPython', 'Programming Language :: Python :: Implementation :: PyPy', 'Topic :: Software Development :: Libraries', 'Topic :: System :: Hardware :: Hardware Drivers', ], use_2to3=sys.version_info >= (3, ), ) libusb1-1.7/stdeb.cfg0000644000175000017500000000013412756433522015651 0ustar vincentvincent00000000000000[DEFAULT] Source=python-libusb1 Package=python-libusb1 Depends=libusb-1.0-0 | libusb2debian libusb1-1.7/examples/0000755000175000017500000000000013404567420015703 5ustar vincentvincent00000000000000libusb1-1.7/examples/hotplug.py0000755000175000017500000000333613404567324017752 0ustar vincentvincent00000000000000#!/usr/bin/env python # Copyright (C) 2013-2018 Vincent Pelletier # # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public # License as published by the Free Software Foundation; either # version 2.1 of the License, or (at your option) any later version. # # This library 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 # Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public # License along with this library; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA from __future__ import print_function import usb1 def hotplug_callback(context, device, event): print("Device %s: %s" % ( { usb1.HOTPLUG_EVENT_DEVICE_ARRIVED: 'arrived', usb1.HOTPLUG_EVENT_DEVICE_LEFT: 'left', }[event], device, )) # Note: cannot call synchronous API in this function. def main(): with usb1.USBContext() as context: if not context.hasCapability(usb1.CAP_HAS_HOTPLUG): print('Hotplug support is missing. Please update your libusb version.') return print('Registering hotplug callback...') opaque = context.hotplugRegisterCallback(hotplug_callback) print('Callback registered. Monitoring events, ^C to exit') try: while True: context.handleEvents() except (KeyboardInterrupt, SystemExit): print('Exiting') if __name__ == '__main__': main() libusb1-1.7/examples/README0000644000175000017500000000105312756433522016566 0ustar vincentvincent00000000000000More concrete uses: - USB protocol analyser driver in userland (the protocol analyser is itself an USB device). Rather simple code, uses synchronous and asynchronous bulk transfers. https://github.com/vpelletier/ITI1480A-linux/blob/master/iti1480a/capture.py - Tinkerforge's brickd They since switched to all-C to fit into embedded devices. https://github.com/Tinkerforge/brickd/tree/v1.x.y/src/brickd - NetMDPython Tools to control NetMD Mindisk players by USB. https://wiki.physik.fu-berlin.de/linux-minidisc/doku.php?id=netmdpython libusb1-1.7/examples/listdevs.py0000755000175000017500000000234013404567324020117 0ustar vincentvincent00000000000000#!/usr/bin/env python # Copyright (C) 2013-2018 Vincent Pelletier # # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public # License as published by the Free Software Foundation; either # version 2.1 of the License, or (at your option) any later version. # # This library 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 # Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public # License along with this library; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA from __future__ import print_function import usb1 def main(): with usb1.USBContext() as context: for device in context.getDeviceIterator(skip_on_error=True): print('ID %04x:%04x' % (device.getVendorID(), device.getProductID()), '->'.join(str(x) for x in ['Bus %03i' % (device.getBusNumber(), )] + device.getPortNumberList()), 'Device', device.getDeviceAddress()) if __name__ == '__main__': main() libusb1-1.7/examples/hotplug_advanced.py0000755000175000017500000002017013404567324021572 0ustar vincentvincent00000000000000#!/usr/bin/env python # Copyright (C) 2013-2018 Vincent Pelletier # # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public # License as published by the Free Software Foundation; either # version 2.1 of the License, or (at your option) any later version. # # This library 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 # Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public # License along with this library; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA """ Advanced hotplug examples. Presents ways of integrating hotplug into your userland USB driver. """ from __future__ import print_function import select import sys import usb1 # A few helpers for demonstration... mode_dict = {} class NoHotplugSupport(Exception): pass def onAwesomeDeviceLeft(awesome_device): print('Device left:', str(awesome_device)) def onAwesomeDeviceArrived(awesome_device): awesome_device.onClose = onAwesomeDeviceLeft print('Device arrived:', str(awesome_device)) class SelectPoller(object): """ Dummy poller based on select, because it exists on all platforms. WARNING: this class is just for a trivial demonstration, and inherits select() limitations. The most important limitation is that regitering descriptors does not wake/affect a running poll. """ def __init__(self): self._fd_dict = {} def register(self, fd, events): self._fd_dict[fd] = events def unregister(self, fd): self._fd_dict.pop(fd) def poll(self, timeout=None): flag_list = (select.POLLIN, select.POLLOUT, select.POLLPRI) result = {} for fd_list, happened_flag in zip( select.select(*([[ fd for fd, events in self._fd_dict.iteritems() if events & flag ] for flag in flag_list] + [timeout])), flag_list, ): result[fd] = result.get(fd, 0) | happened_flag return result.items() # (end of demonstration helpers) class AwesomeDevice(object): # Application can set this property to do cleanup when device gets closed, # for example when device has left. onClose = lambda device: None def __init__(self, handle): self._handle = handle def __str__(self): # For demonstration purposes only. return 'Awesome Device at ' + str(self._handle.getDevice()) def close(self): # Note: device may have already left when this method is called, # so catch USBErrorNoDevice around cleanup steps involving the device. try: self.onClose(self) # Put device in low-power mode, release claimed interfaces... pass except usb1.USBErrorNoDevice: pass self._handle.close() class AwesomeDeviceHoarderBase(object): """ Manages the horde of connected devices. """ def __init__( self, onDeviceArrived=(lambda awesome_device: False), ): """ onDeviceArrived (callable) Allows further actions by the application when a relevant device arrives, so that it integrates with other devices (ex: send different key events when the same button is pressed on different devices). Returns whether this device should be ignored. Cannot call synchronous API. """ self.context = usb1.USBContext() if not self.context.hasCapability(usb1.CAP_HAS_HOTPLUG): raise NoHotplugSupport( 'Hotplug support is missing. Please update your libusb version.' ) self._device_dict = {} self._onDeviceArrived = onDeviceArrived def _registerCallback(self): self.context.hotplugRegisterCallback( self._onHotplugEvent, # Just in case more events are added in the future. events=usb1.HOTPLUG_EVENT_DEVICE_ARRIVED | usb1.HOTPLUG_EVENT_DEVICE_LEFT, # Edit these if you handle devices from a single vendor, of a # single product type or of a single device class; and simplify # device filtering accordingly in _onHotplugEvent. #vendor_id=, #product_id=, #dev_class=, ) @staticmethod def isDeviceSupported(vendor_id, device_id): """ Check if we should drive the device which arrived. Simplify this if libusb hotplug API filter is enough (ex: handling a single device type). """ # This example handles all devices. return True def _onHotplugEvent(self, context, device, event): if event == usb1.HOTPLUG_EVENT_DEVICE_LEFT: awesome_device = self._device_dict.pop(device, None) if awesome_device is not None: awesome_device.close() return # Remove next branch if libusb hotplug API filtering is enough (ex: # handling a single device type). if not self.isDeviceSupported( device.getVendorID(), device.getProductID(), ): return try: handle = device.open() except usb1.USBError: return awesome_device = AwesomeDevice(handle) if self._onDeviceArrived(awesome_device): awesome_device.close() return self._device_dict[device] = awesome_device # Below are alternative APIs. Choose the one most suitable to your needs, # and ignore the others. This is of course not an exhaustive list. class AwesomeDeviceHoarderSimple(AwesomeDeviceHoarderBase): """ API 1: USB-event-centric application. Simplest API, for userland drivers which only react to USB events. """ def run(self): with self.context: print('Registering hotplug callback...') self._registerCallback() print('Callback registered. Monitoring events, ^C to exit') while True: self.context.handleEvents() def simple(): AwesomeDeviceHoarderSimple(onAwesomeDeviceArrived).run() mode_dict['simple'] = simple class AwesomeDeviceHoarderEventLoop(AwesomeDeviceHoarderBase): """ API 2: More complex, for userland drivers which need to react to other events (sockets, user input, ...). Application must then integrate libusb polling in its event loop (see usb1.USBPollerThread and usb1.USBPoller). """ def __enter__(self): self.context.open() print('Registering hotplug callback...') self._registerCallback() print('Callback registered.') return self def __exit__(self, exc_type, exc_val, exc_tb): self.context.close() def eventloop(): with AwesomeDeviceHoarderEventLoop(onAwesomeDeviceArrived) as awesome_device_hoarder: base_poller = SelectPoller() # In real code, file descriptor would be independently registered # to base_poller. # The event loop would be something like: poller = usb1.USBPoller(awesome_device_hoarder.context, base_poller) print('Monitoring events, ^C to exit') while True: poller.poll() mode_dict['eventloop'] = eventloop def main(): try: mode = mode_dict[sys.argv[1]] except (KeyError, IndexError): print('Usage: %s [%s]' % ( sys.argv[0], '|'.join(mode_dict), )) sys.exit(1) print( 'NOTE: this example needs sufficient permissions to be able to ' 'open USB devices to produce any interesting output. If you see ' 'nothing below, check you have USB devices plugged *and* that you ' 'have sufficient permissions to open them.' ) try: mode() except NoHotplugSupport, exc: print(exc.value) sys.exit(1) except (KeyboardInterrupt, SystemExit): print('Exiting') if __name__ == '__main__': main() libusb1-1.7/MANIFEST.in0000644000175000017500000000021612756433522015626 0ustar vincentvincent00000000000000include README.rst include COPYING include COPYING.LESSER include examples/README include examples/*.py include testUSB1.py include stdeb.cfg libusb1-1.7/COPYING.LESSER0000644000175000017500000006364212756433522016133 0ustar vincentvincent00000000000000 GNU LESSER GENERAL PUBLIC LICENSE Version 2.1, February 1999 Copyright (C) 1991, 1999 Free Software Foundation, Inc. 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. [This is the first released version of the Lesser GPL. It also counts as the successor of the GNU Library Public License, version 2, hence the version number 2.1.] Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public Licenses are intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This license, the Lesser General Public License, applies to some specially designated software packages--typically libraries--of the Free Software Foundation and other authors who decide to use it. You can use it too, but we suggest you first think carefully about whether this license or the ordinary General Public License is the better strategy to use in any particular case, based on the explanations below. When we speak of free software, we are referring to freedom of use, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish); that you receive source code or can get it if you want it; that you can change the software and use pieces of it in new free programs; and that you are informed that you can do these things. To protect your rights, we need to make restrictions that forbid distributors to deny you these rights or to ask you to surrender these rights. These restrictions translate to certain responsibilities for you if you distribute copies of the library or if you modify it. For example, if you distribute copies of the library, whether gratis or for a fee, you must give the recipients all the rights that we gave you. You must make sure that they, too, receive or can get the source code. If you link other code with the library, you must provide complete object files to the recipients, so that they can relink them with the library after making changes to the library and recompiling it. And you must show them these terms so they know their rights. We protect your rights with a two-step method: (1) we copyright the library, and (2) we offer you this license, which gives you legal permission to copy, distribute and/or modify the library. To protect each distributor, we want to make it very clear that there is no warranty for the free library. Also, if the library is modified by someone else and passed on, the recipients should know that what they have is not the original version, so that the original author's reputation will not be affected by problems that might be introduced by others. Finally, software patents pose a constant threat to the existence of any free program. We wish to make sure that a company cannot effectively restrict the users of a free program by obtaining a restrictive license from a patent holder. Therefore, we insist that any patent license obtained for a version of the library must be consistent with the full freedom of use specified in this license. Most GNU software, including some libraries, is covered by the ordinary GNU General Public License. This license, the GNU Lesser General Public License, applies to certain designated libraries, and is quite different from the ordinary General Public License. We use this license for certain libraries in order to permit linking those libraries into non-free programs. When a program is linked with a library, whether statically or using a shared library, the combination of the two is legally speaking a combined work, a derivative of the original library. The ordinary General Public License therefore permits such linking only if the entire combination fits its criteria of freedom. The Lesser General Public License permits more lax criteria for linking other code with the library. We call this license the "Lesser" General Public License because it does Less to protect the user's freedom than the ordinary General Public License. It also provides other free software developers Less of an advantage over competing non-free programs. These disadvantages are the reason we use the ordinary General Public License for many libraries. However, the Lesser license provides advantages in certain special circumstances. For example, on rare occasions, there may be a special need to encourage the widest possible use of a certain library, so that it becomes a de-facto standard. To achieve this, non-free programs must be allowed to use the library. A more frequent case is that a free library does the same job as widely used non-free libraries. In this case, there is little to gain by limiting the free library to free software only, so we use the Lesser General Public License. In other cases, permission to use a particular library in non-free programs enables a greater number of people to use a large body of free software. For example, permission to use the GNU C Library in non-free programs enables many more people to use the whole GNU operating system, as well as its variant, the GNU/Linux operating system. Although the Lesser General Public License is Less protective of the users' freedom, it does ensure that the user of a program that is linked with the Library has the freedom and the wherewithal to run that program using a modified version of the Library. The precise terms and conditions for copying, distribution and modification follow. Pay close attention to the difference between a "work based on the library" and a "work that uses the library". The former contains code derived from the library, whereas the latter must be combined with the library in order to run. GNU LESSER GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License Agreement applies to any software library or other program which contains a notice placed by the copyright holder or other authorized party saying it may be distributed under the terms of this Lesser General Public License (also called "this License"). Each licensee is addressed as "you". A "library" means a collection of software functions and/or data prepared so as to be conveniently linked with application programs (which use some of those functions and data) to form executables. The "Library", below, refers to any such software library or work which has been distributed under these terms. A "work based on the Library" means either the Library or any derivative work under copyright law: that is to say, a work containing the Library or a portion of it, either verbatim or with modifications and/or translated straightforwardly into another language. (Hereinafter, translation is included without limitation in the term "modification".) "Source code" for a work means the preferred form of the work for making modifications to it. For a library, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the library. Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running a program using the Library is not restricted, and output from such a program is covered only if its contents constitute a work based on the Library (independent of the use of the Library in a tool for writing it). Whether that is true depends on what the Library does and what the program that uses the Library does. 1. You may copy and distribute verbatim copies of the Library's complete source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and distribute a copy of this License along with the Library. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Library or any portion of it, thus forming a work based on the Library, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) The modified work must itself be a software library. b) You must cause the files modified to carry prominent notices stating that you changed the files and the date of any change. c) You must cause the whole of the work to be licensed at no charge to all third parties under the terms of this License. d) If a facility in the modified Library refers to a function or a table of data to be supplied by an application program that uses the facility, other than as an argument passed when the facility is invoked, then you must make a good faith effort to ensure that, in the event an application does not supply such function or table, the facility still operates, and performs whatever part of its purpose remains meaningful. (For example, a function in a library to compute square roots has a purpose that is entirely well-defined independent of the application. Therefore, Subsection 2d requires that any application-supplied function or table used by this function must be optional: if the application does not supply it, the square root function must still compute square roots.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Library, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Library, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Library. In addition, mere aggregation of another work not based on the Library with the Library (or with a work based on the Library) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may opt to apply the terms of the ordinary GNU General Public License instead of this License to a given copy of the Library. To do this, you must alter all the notices that refer to this License, so that they refer to the ordinary GNU General Public License, version 2, instead of to this License. (If a newer version than version 2 of the ordinary GNU General Public License has appeared, then you can specify that version instead if you wish.) Do not make any other change in these notices. Once this change is made in a given copy, it is irreversible for that copy, so the ordinary GNU General Public License applies to all subsequent copies and derivative works made from that copy. This option is useful when you wish to copy part of the code of the Library into a program that is not a library. 4. You may copy and distribute the Library (or a portion or derivative of it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange. If distribution of object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place satisfies the requirement to distribute the source code, even though third parties are not compelled to copy the source along with the object code. 5. A program that contains no derivative of any portion of the Library, but is designed to work with the Library by being compiled or linked with it, is called a "work that uses the Library". Such a work, in isolation, is not a derivative work of the Library, and therefore falls outside the scope of this License. However, linking a "work that uses the Library" with the Library creates an executable that is a derivative of the Library (because it contains portions of the Library), rather than a "work that uses the library". The executable is therefore covered by this License. Section 6 states terms for distribution of such executables. When a "work that uses the Library" uses material from a header file that is part of the Library, the object code for the work may be a derivative work of the Library even though the source code is not. Whether this is true is especially significant if the work can be linked without the Library, or if the work is itself a library. The threshold for this to be true is not precisely defined by law. If such an object file uses only numerical parameters, data structure layouts and accessors, and small macros and small inline functions (ten lines or less in length), then the use of the object file is unrestricted, regardless of whether it is legally a derivative work. (Executables containing this object code plus portions of the Library will still fall under Section 6.) Otherwise, if the work is a derivative of the Library, you may distribute the object code for the work under the terms of Section 6. Any executables containing that work also fall under Section 6, whether or not they are linked directly with the Library itself. 6. As an exception to the Sections above, you may also combine or link a "work that uses the Library" with the Library to produce a work containing portions of the Library, and distribute that work under terms of your choice, provided that the terms permit modification of the work for the customer's own use and reverse engineering for debugging such modifications. You must give prominent notice with each copy of the work that the Library is used in it and that the Library and its use are covered by this License. You must supply a copy of this License. If the work during execution displays copyright notices, you must include the copyright notice for the Library among them, as well as a reference directing the user to the copy of this License. Also, you must do one of these things: a) Accompany the work with the complete corresponding machine-readable source code for the Library including whatever changes were used in the work (which must be distributed under Sections 1 and 2 above); and, if the work is an executable linked with the Library, with the complete machine-readable "work that uses the Library", as object code and/or source code, so that the user can modify the Library and then relink to produce a modified executable containing the modified Library. (It is understood that the user who changes the contents of definitions files in the Library will not necessarily be able to recompile the application to use the modified definitions.) b) Use a suitable shared library mechanism for linking with the Library. A suitable mechanism is one that (1) uses at run time a copy of the library already present on the user's computer system, rather than copying library functions into the executable, and (2) will operate properly with a modified version of the library, if the user installs one, as long as the modified version is interface-compatible with the version that the work was made with. c) Accompany the work with a written offer, valid for at least three years, to give the same user the materials specified in Subsection 6a, above, for a charge no more than the cost of performing this distribution. d) If distribution of the work is made by offering access to copy from a designated place, offer equivalent access to copy the above specified materials from the same place. e) Verify that the user has already received a copy of these materials or that you have already sent this user a copy. For an executable, the required form of the "work that uses the Library" must include any data and utility programs needed for reproducing the executable from it. However, as a special exception, the materials to be distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. It may happen that this requirement contradicts the license restrictions of other proprietary libraries that do not normally accompany the operating system. Such a contradiction means you cannot use both them and the Library together in an executable that you distribute. 7. You may place library facilities that are a work based on the Library side-by-side in a single library together with other library facilities not covered by this License, and distribute such a combined library, provided that the separate distribution of the work based on the Library and of the other library facilities is otherwise permitted, and provided that you do these two things: a) Accompany the combined library with a copy of the same work based on the Library, uncombined with any other library facilities. This must be distributed under the terms of the Sections above. b) Give prominent notice with the combined library of the fact that part of it is a work based on the Library, and explaining where to find the accompanying uncombined form of the same work. 8. You may not copy, modify, sublicense, link with, or distribute the Library except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense, link with, or distribute the Library is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 9. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Library or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Library (or any work based on the Library), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Library or works based on it. 10. Each time you redistribute the Library (or any work based on the Library), the recipient automatically receives a license from the original licensor to copy, distribute, link with or modify the Library subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties with this License. 11. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Library at all. For example, if a patent license would not permit royalty-free redistribution of the Library by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Library. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply, and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 12. If the distribution and/or use of the Library is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Library under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 13. The Free Software Foundation may publish revised and/or new versions of the Lesser General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Library specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Library does not specify a license version number, you may choose any version ever published by the Free Software Foundation. 14. If you wish to incorporate parts of the Library into other free programs whose distribution conditions are incompatible with these, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Libraries If you develop a new library, and you want it to be of the greatest possible use to the public, we recommend making it free software that everyone can redistribute and change. You can do so by permitting redistribution under these terms (or, alternatively, under the terms of the ordinary General Public License). To apply these terms, attach the following notices to the library. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library 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 Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA Also add information on how to contact you by electronic and paper mail. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the library, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the library `Frob' (a library for tweaking knobs) written by James Random Hacker. , 1 April 1990 Ty Coon, President of Vice That's all there is to it! libusb1-1.7/COPYING0000644000175000017500000004310312756433522015125 0ustar vincentvincent00000000000000 GNU GENERAL PUBLIC LICENSE Version 2, June 1991 Copyright (C) 1989, 1991 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This General Public License applies to most of the Free Software Foundation's software and to any other program whose authors commit to using it. (Some other Free Software Foundation software is covered by the GNU Lesser General Public License instead.) You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things. To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it. For example, if you distribute copies of such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software. Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations. Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that redistributors of a free program will individually obtain patent licenses, in effect making the program proprietary. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all. The precise terms and conditions for copying, distribution and modification follow. GNU GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any derivative work under copyright law: that is to say, a work containing the Program or a portion of it, either verbatim or with modifications and/or translated into another language. (Hereinafter, translation is included without limitation in the term "modification".) Each licensee is addressed as "you". Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running the Program is not restricted, and the output from the Program is covered only if its contents constitute a work based on the Program (independent of having been made by running the Program). Whether that is true depends on what the Program does. 1. You may copy and distribute verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and give any other recipients of the Program a copy of this License along with the Program. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Program or any portion of it, thus forming a work based on the Program, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) You must cause the modified files to carry prominent notices stating that you changed the files and the date of any change. b) You must cause any work that you distribute or publish, that in whole or in part contains or is derived from the Program or any part thereof, to be licensed as a whole at no charge to all third parties under the terms of this License. c) If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the most ordinary way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this License. (Exception: if the Program itself is interactive but does not normally print such an announcement, your work based on the Program is not required to print an announcement.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Program, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Program. In addition, mere aggregation of another work not based on the Program with the Program (or with a work based on the Program) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may copy and distribute the Program (or a work based on it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you also do one of the following: a) Accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, b) Accompany it with a written offer, valid for at least three years, to give any third party, for a charge no more than your cost of physically performing source distribution, a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, c) Accompany it with the information you received as to the offer to distribute corresponding source code. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form with such an offer, in accord with Subsection b above.) The source code for a work means the preferred form of the work for making modifications to it. For an executable work, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the executable. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. If distribution of executable or object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code. 4. You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Program is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 5. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Program or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Program (or any work based on the Program), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Program or works based on it. 6. Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License. 7. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Program at all. For example, if a patent license would not permit royalty-free redistribution of the Program by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Program. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system, which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 8. If the distribution and/or use of the Program is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Program under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 9. The Free Software Foundation may publish revised and/or new versions of the General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of this License, you may choose any version ever published by the Free Software Foundation. 10. If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. Also add information on how to contact you by electronic and paper mail. If the program is interactive, make it output a short notice like this when it starts in an interactive mode: Gnomovision version 69, Copyright (C) year name of author Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, the commands you use may be called something other than `show w' and `show c'; they could even be mouse-clicks or menu items--whatever suits your program. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the program, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the program `Gnomovision' (which makes passes at compilers) written by James Hacker. , 1 April 1989 Ty Coon, President of Vice This General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Lesser General Public License instead of this License. libusb1-1.7/libusb1.py0000644000175000017500000000033313015312250015762 0ustar vincentvincent00000000000000import warnings warnings.warn( 'Importing this module should not be needed, everything intended to be ' 'exposed should be available in usb1 module.', DeprecationWarning, ) del warnings from usb1.libusb1 import * libusb1-1.7/usb1/0000755000175000017500000000000013404567420014737 5ustar vincentvincent00000000000000libusb1-1.7/usb1/testUSB1.py0000644000175000017500000003113413404567324016730 0ustar vincentvincent00000000000000# Copyright (C) 2010-2018 Vincent Pelletier # # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public # License as published by the Free Software Foundation; either # version 2.1 of the License, or (at your option) any later version. # # This library 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 # Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public # License along with this library; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA # pylint: disable=invalid-name, missing-docstring, too-many-public-methods import unittest import sys import itertools import select import threading import usb1 import libusb1 from ctypes import pointer buff_len = 1024 buffer_base = [x % 256 for x in range(buff_len)] if sys.version_info[0] == 3: buff = bytes(buffer_base) other_buff = bytes(reversed(buffer_base)) else: buff = ''.join(chr(x) for x in buffer_base) other_buff = ''.join(chr(x) for x in reversed(buffer_base)) bytearray_buff = bytearray(buffer_base) class USBContext(usb1.USBContext): def open(self): try: return super(USBContext, self).open() except usb1.USBError: raise unittest.SkipTest( 'usb1.USBContext() fails - no USB bus on system ?' ) class PollDetector(object): def __init__(self, *args, **kw): try: poll = select.poll except AttributeError: raise unittest.SkipTest('select.poll missing') self.__poll = poll(*args, **kw) self.__event = threading.Event() def poll(self, *args, **kw): self.__event.set() return self.__poll.poll(*args, **kw) def wait(self, *args, **kw): self.__event.wait(*args, **kw) def __getattr__(self, name): return getattr(self.__poll, name) class USBTransferTests(unittest.TestCase): @staticmethod def getTransfer(iso_packets=0): # Dummy handle return usb1.USBTransfer( pointer(libusb1.libusb_device_handle()), iso_packets, lambda x: None, lambda x: None) @staticmethod def testGetVersion(): """ Just testing getVersion doesn't raise... """ usb1.getVersion() @staticmethod def testHasCapability(): """ Just testing hasCapability doesn't raise... """ usb1.hasCapability(usb1.CAP_HAS_CAPABILITY) def testSetControl(self): """ Simplest test: feed some data, must not raise. """ transfer = self.getTransfer() request_type = usb1.TYPE_STANDARD request = usb1.REQUEST_GET_STATUS value = 0 index = 0 def callback(transfer): pass user_data = [] timeout = 1000 # All provided, buffer variant transfer.setControl( request_type, request, value, index, buff, callback=callback, user_data=user_data, timeout=timeout) self.assertEqual(buff, transfer.getBuffer()) self.assertRaises(ValueError, transfer.setBuffer, buff) # All provided, buffer length variant transfer.setControl( request_type, request, value, index, buff_len, callback=callback, user_data=user_data, timeout=timeout) # No timeout transfer.setControl( request_type, request, value, index, buff, callback=callback, user_data=user_data) # No user data transfer.setControl( request_type, request, value, index, buff, callback=callback) # No callback transfer.setControl(request_type, request, value, index, buff) def _testTransferSetter(self, transfer, setter_id): endpoint = 0x81 def callback(transfer): pass user_data = [] timeout = 1000 setter = getattr(transfer, setter_id) # All provided, buffer variant setter( endpoint, buff, callback=callback, user_data=user_data, timeout=timeout) self.assertEqual(buff, transfer.getBuffer()) transfer.setBuffer(other_buff) self.assertEqual(other_buff, transfer.getBuffer()) transfer.setBuffer(bytearray_buff) self.assertEqual(bytearray_buff, transfer.getBuffer()) transfer.setBuffer(buff_len) self.assertEqual(buff_len, len(transfer.getBuffer())) # All provided, buffer length variant setter( endpoint, buff_len, callback=callback, user_data=user_data, timeout=timeout) # No timeout setter(endpoint, buff, callback=callback, user_data=user_data) # No user data setter(endpoint, buff, callback=callback) # No callback setter(endpoint, buff) def testSetBulk(self): """ Simplest test: feed some data, must not raise. Also, test setBuffer/getBuffer. """ self._testTransferSetter(self.getTransfer(), 'setBulk') def testSetInterrupt(self): """ Simplest test: feed some data, must not raise. Also, test setBuffer/getBuffer. """ self._testTransferSetter(self.getTransfer(), 'setInterrupt') def testSetIsochronous(self): """ Simplest test: feed some data, must not raise. Also, test setBuffer/getBuffer/getISOBufferList/iterISO. """ iso_transfer_count = 16 transfer = self.getTransfer(iso_transfer_count) self._testTransferSetter(transfer, 'setIsochronous') # Returns whole buffers self.assertEqual( bytearray(itertools.chain(*transfer.getISOBufferList())), buff, ) # Returns actually transfered data, so here nothing self.assertEqual(bytearray( itertools.chain(*[x for _, x in transfer.iterISO()])), bytearray(), ) # Fake reception of whole transfers c_transfer = getattr( transfer, '_' + transfer.__class__.__name__ + '__transfer' ) for iso_metadata in libusb1.get_iso_packet_list(c_transfer): iso_metadata.actual_length = iso_metadata.length # Now iterISO returns everythig self.assertEqual(bytearray( itertools.chain(*[x for _, x in transfer.iterISO()])), buff, ) def testSetGetCallback(self): transfer = self.getTransfer() def callback(transfer): pass transfer.setCallback(callback) got_callback = transfer.getCallback() self.assertEqual(callback, got_callback) def testUSBPollerThreadExit(self): """ USBPollerThread must exit by itself when context is destroyed. """ with USBContext() as context: poll_detector = PollDetector() try: poller = usb1.USBPollerThread(context, poll_detector) except OSError: raise unittest.SkipTest('libusb without file descriptor events') poller.start() poll_detector.wait(1) poller.join(1) self.assertFalse(poller.is_alive()) def testUSBPollerThreadException(self): """ USBPollerThread exception handling. """ class FakeEventPoll(PollDetector): # pylint: disable=method-hidden def poll(self, *args, **kw): self.poll = super(FakeEventPoll, self).poll return ['dummy'] # pylint: enable=method-hidden with USBContext() as context: def fakeHandleEventsLocked(): raise usb1.USBError(0) context.handleEventsLocked = fakeHandleEventsLocked exception_event = threading.Event() exception_list = [] def exceptionHandler(exc): exception_list.append(exc) exception_event.set() try: poller = usb1.USBPollerThread( context, FakeEventPoll(), exceptionHandler) except OSError: raise unittest.SkipTest('libusb without file descriptor events') poller.start() exception_event.wait(1) self.assertTrue(exception_list, exception_list) self.assertTrue(poller.is_alive()) @staticmethod def testDescriptors(): """ Test descriptor walk. Needs any usb device, which won't be opened. """ with USBContext() as context: device_list = context.getDeviceList(skip_on_error=True) found = False for device in device_list: device.getBusNumber() device.getPortNumber() device.getPortNumberList() device.getDeviceAddress() for settings in device.iterSettings(): for endpoint in settings: pass for configuration in device.iterConfigurations(): for interface in configuration: for settings in interface: for endpoint in settings: found = True if not found: raise unittest.SkipTest('descriptor walk test did not complete') def testDefaultEnumScope(self): """ Enum instances must only affect the scope they are created in. """ ENUM_NAME = 'THE_ANSWER' ENUM_VALUE = 42 local_dict = locals() global_dict = globals() self.assertEqual(local_dict.get(ENUM_NAME), None) self.assertEqual(global_dict.get(ENUM_NAME), None) self.assertEqual(getattr(libusb1, ENUM_NAME, None), None) # pylint: disable=unused-variable TEST_ENUM = libusb1.Enum({ENUM_NAME: ENUM_VALUE}) # pylint: enable=unused-variable self.assertEqual(local_dict.get(ENUM_NAME), ENUM_VALUE) self.assertEqual(global_dict.get(ENUM_NAME), None) self.assertEqual(getattr(libusb1, ENUM_NAME, None), None) def testExplicitEnumScope(self): """ Enum instances must only affect the scope they are created in. """ ENUM_NAME = 'THE_ANSWER' ENUM_VALUE = 42 local_dict = locals() global_dict = globals() self.assertEqual(local_dict.get(ENUM_NAME), None) self.assertEqual(global_dict.get(ENUM_NAME), None) self.assertEqual(getattr(libusb1, ENUM_NAME, None), None) # pylint: disable=unused-variable TEST_ENUM = libusb1.Enum({ENUM_NAME: ENUM_VALUE}, global_dict) # pylint: enable=unused-variable try: self.assertEqual(local_dict.get(ENUM_NAME), None) self.assertEqual(global_dict.get(ENUM_NAME), ENUM_VALUE) self.assertEqual(getattr(libusb1, ENUM_NAME, None), None) finally: del global_dict[ENUM_NAME] def testImplicitUSBContextOpening(self): """ Test pre-1.5 API backward compatibility. First method call which needs a context succeeds. Further calls return None. """ context = USBContext() # Deprecated try: fd_list = context.getPollFDList() except NotImplementedError: raise unittest.SkipTest('libusb without file descriptor events') self.assertNotEqual(fd_list, None) context.exit() # Deprecated self.assertEqual(context.getPollFDList(), None) def testUSBTransferMayRaiseUSBError(self): """ mayRaiseUSBError needs to be a class property to be reliably able to call it during interpreter shutdown. But setting a function as property makes it bound to the instance when accessed. This is not obviously visible because mayRaiseUSBError has a should-never-be-used second argument for exactly the same purpose, but which accidentally receives the value intended as the first parameter (the first parameter being replaced by "self" as for any bound method). So this test verifies that USBTranfer.__mayRaiseUSBError behaves the intended way. And to make this test work when there is no USB device around, directly instanciate USBTransfer and access tested private property. """ transfer = usb1.USBTransfer(None, 0, None, None) transfer._USBTransfer__mayRaiseUSBError(0) self.assertRaises(usb1.USBErrorIO, transfer._USBTransfer__mayRaiseUSBError, usb1.ERROR_IO) if __name__ == '__main__': unittest.main() libusb1-1.7/usb1/libusb1.py0000644000175000017500000014253713404567324016671 0ustar vincentvincent00000000000000# Copyright (C) 2010-2018 Vincent Pelletier # # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public # License as published by the Free Software Foundation; either # version 2.1 of the License, or (at your option) any later version. # # This library 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 # Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public # License along with this library; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA # pylint: disable=invalid-name, too-few-public-methods, too-many-arguments # pylint: disable=missing-docstring """ Python ctypes bindings for libusb-1.0. You should not need to import this if you use usb1 module. Declares all constants, data structures and exported symbols. Locates and loads libusb1 dynamic library. """ from ctypes import Structure, LittleEndianStructure, \ CFUNCTYPE, POINTER, addressof, sizeof, cast, \ c_short, c_int, c_uint, c_size_t, c_long, \ c_uint8, c_uint16, c_uint32, \ c_void_p, c_char_p, py_object, pointer, c_char try: from ctypes import c_ssize_t except ImportError: from ctypes import c_longlong # c_ssize_t is new in Python 2.7 if sizeof(c_int) == sizeof(c_size_t): c_ssize_t = c_int elif sizeof(c_long) == sizeof(c_size_t): c_ssize_t = c_long elif sizeof(c_longlong) == sizeof(c_size_t): c_ssize_t = c_longlong else: raise ValueError('Unsupported arch: sizeof(c_size_t) = %r' % ( sizeof(c_size_t), )) import ctypes.util import platform import os.path import sys class Enum(object): def __init__(self, member_dict, scope_dict=None): if scope_dict is None: # Affect caller's locals, not this module's. # pylint: disable=protected-access scope_dict = sys._getframe(1).f_locals # pylint: enable=protected-access forward_dict = {} reverse_dict = {} next_value = 0 for name, value in member_dict.items(): if value is None: value = next_value next_value += 1 forward_dict[name] = value if value in reverse_dict: raise ValueError('Multiple names for value %r: %r, %r' % ( value, reverse_dict[value], name )) reverse_dict[value] = name scope_dict[name] = value self.forward_dict = forward_dict self.reverse_dict = reverse_dict def __call__(self, value): return self.reverse_dict[value] def get(self, value, default=None): return self.reverse_dict.get(value, default) def buffer_at(address, length): """ Simular to ctypes.string_at, but zero-copy and requires an integer address. """ return bytearray((c_char * length).from_address(address)) _desc_type_dict = { 'b': c_uint8, 'bcd': c_uint16, 'bm': c_uint8, 'dw': c_uint32, 'i': c_uint8, 'id': c_uint16, 'w': c_uint16, } def newStruct(field_name_list): """ Create a ctype structure class based on USB standard field naming (type-prefixed). """ field_list = [] append = field_list.append for field in field_name_list: type_prefix = '' for char in field: if not char.islower(): break type_prefix += char append((field, _desc_type_dict[type_prefix])) result = type('some_descriptor', (LittleEndianStructure, ), {}) # Not using type()'s 3rd param to initialise class, as per ctypes # documentation: # _pack_ must already be defined when _fields_ is assigned, otherwise it # will have no effect. # pylint: disable=protected-access result._pack_ = 1 result._fields_ = field_list # pylint: enable=protected-access return result def newDescriptor(field_name_list): """ Create a USB descriptor ctype structure, ie starting with bLength and bDescriptorType fields. See newStruct(). """ return newStruct(['bLength', 'bDescriptorType'] + list(field_name_list)) class USBError(Exception): value = None def __init__(self, value=None): Exception.__init__(self) if value is not None: self.value = value def __str__(self): return '%s [%s]' % (libusb_error.get(self.value, 'Unknown error'), self.value) if sys.version_info[0] == 3: _string_item_to_int = lambda x: x _empty_char_p = bytes() else: _string_item_to_int = ord _empty_char_p = '' c_uchar = c_uint8 c_int_p = POINTER(c_int) LITTLE_ENDIAN = sys.byteorder == 'little' class timeval(Structure): _fields_ = [('tv_sec', c_long), ('tv_usec', c_long)] timeval_p = POINTER(timeval) def _loadLibrary(): system = platform.system() if system == 'Windows': dll_loader = ctypes.WinDLL suffix = '.dll' else: dll_loader = ctypes.CDLL suffix = system == 'Darwin' and '.dylib' or '.so' loader_kw = {} if sys.version_info[:2] >= (2, 6): loader_kw['use_errno'] = True loader_kw['use_last_error'] = True try: return dll_loader('libusb-1.0' + suffix, **loader_kw) except OSError: libusb_path = None base_name = 'usb-1.0' if 'FreeBSD' in system: # libusb.so.2 on FreeBSD: load('libusb.so') would work fine, but... # libusb.so.2debian on Debian GNU/kFreeBSD: here it wouldn't work. # So use find_library instead. base_name = 'usb' elif system == 'Darwin': for libusb_path in ( # macport standard path '/opt/local/lib/libusb-1.0.dylib', # fink standard path '/sw/lib/libusb-1.0.dylib', ): if os.path.exists(libusb_path): break else: libusb_path = None if libusb_path is None: libusb_path = ctypes.util.find_library(base_name) if libusb_path is None: raise return dll_loader(libusb_path, **loader_kw) libusb = _loadLibrary() # libusb.h def bswap16(x): return ((x & 0xff) << 8) | (x >> 8) if LITTLE_ENDIAN: def libusb_cpu_to_le16(x): return x def libusb_le16_to_cpu(x): return x else: libusb_cpu_to_le16 = bswap16 libusb_le16_to_cpu = bswap16 # standard USB stuff # Device and/or Interface Class codes libusb_class_code = Enum({ # In the context of a device descriptor, # this bDeviceClass value indicates that each interface specifies its # own class information and all interfaces operate independently. 'LIBUSB_CLASS_PER_INTERFACE': 0, # Audio class 'LIBUSB_CLASS_AUDIO': 1, # Communications class 'LIBUSB_CLASS_COMM': 2, # Human Interface Device class 'LIBUSB_CLASS_HID': 3, # Physical 'LIBUSB_CLASS_PHYSICAL': 5, # Printer class 'LIBUSB_CLASS_PRINTER': 7, # Picture transfer protocol class 'LIBUSB_CLASS_PTP': 6, # Mass storage class 'LIBUSB_CLASS_MASS_STORAGE': 8, # Hub class 'LIBUSB_CLASS_HUB': 9, # Data class 'LIBUSB_CLASS_DATA': 10, # Smart Card 'LIBUSB_CLASS_SMART_CARD': 0x0b, # Content Security 'LIBUSB_CLASS_CONTENT_SECURITY': 0x0d, # Video 'LIBUSB_CLASS_VIDEO': 0x0e, # Personal Healthcare 'LIBUSB_CLASS_PERSONAL_HEALTHCARE': 0x0f, # Diagnostic Device 'LIBUSB_CLASS_DIAGNOSTIC_DEVICE': 0xdc, # Wireless class 'LIBUSB_CLASS_WIRELESS': 0xe0, # Application class 'LIBUSB_CLASS_APPLICATION': 0xfe, # Class is vendor-specific 'LIBUSB_CLASS_VENDOR_SPEC': 0xff }) # pylint: disable=undefined-variable LIBUSB_CLASS_IMAGE = LIBUSB_CLASS_PTP # pylint: enable=undefined-variable # Descriptor types as defined by the USB specification. libusb_descriptor_type = Enum({ # Device descriptor. See libusb_device_descriptor. 'LIBUSB_DT_DEVICE': 0x01, # Configuration descriptor. See libusb_config_descriptor. 'LIBUSB_DT_CONFIG': 0x02, # String descriptor 'LIBUSB_DT_STRING': 0x03, # Interface descriptor. See libusb_interface_descriptor. 'LIBUSB_DT_INTERFACE': 0x04, # Endpoint descriptor. See libusb_endpoint_descriptor. 'LIBUSB_DT_ENDPOINT': 0x05, # HID descriptor 'LIBUSB_DT_HID': 0x21, # HID report descriptor 'LIBUSB_DT_REPORT': 0x22, # Physical descriptor 'LIBUSB_DT_PHYSICAL': 0x23, # Hub descriptor 'LIBUSB_DT_HUB': 0x29, }) # Descriptor sizes per descriptor type LIBUSB_DT_DEVICE_SIZE = 18 LIBUSB_DT_CONFIG_SIZE = 9 LIBUSB_DT_INTERFACE_SIZE = 9 LIBUSB_DT_ENDPOINT_SIZE = 7 LIBUSB_DT_ENDPOINT_AUDIO_SIZE = 9 # Audio extension LIBUSB_DT_HUB_NONVAR_SIZE = 7 LIBUSB_ENDPOINT_ADDRESS_MASK = 0x0f # in bEndpointAddress LIBUSB_ENDPOINT_DIR_MASK = 0x80 # BBB USB_ENDPOINT_ADDRESS_MASK = LIBUSB_ENDPOINT_ADDRESS_MASK USB_ENDPOINT_DIR_MASK = LIBUSB_ENDPOINT_DIR_MASK # Endpoint direction. Values for bit 7 of the endpoint address scheme. libusb_endpoint_direction = Enum({ # In: device-to-host 'LIBUSB_ENDPOINT_IN': 0x80, # Out: host-to-device 'LIBUSB_ENDPOINT_OUT': 0x00 }) LIBUSB_TRANSFER_TYPE_MASK = 0x03 # in bmAttributes # Endpoint transfer type. Values for bits 0:1 of the endpoint attributes field. libusb_transfer_type = Enum({ # Control endpoint 'LIBUSB_TRANSFER_TYPE_CONTROL': 0, # Isochronous endpoint 'LIBUSB_TRANSFER_TYPE_ISOCHRONOUS': 1, # Bulk endpoint 'LIBUSB_TRANSFER_TYPE_BULK': 2, # Interrupt endpoint 'LIBUSB_TRANSFER_TYPE_INTERRUPT': 3, }) # Standard requests, as defined in table 9-3 of the USB2 specifications libusb_standard_request = Enum({ # Request status of the specific recipient 'LIBUSB_REQUEST_GET_STATUS': 0x00, # Clear or disable a specific feature 'LIBUSB_REQUEST_CLEAR_FEATURE': 0x01, # 0x02 is reserved # Set or enable a specific feature 'LIBUSB_REQUEST_SET_FEATURE': 0x03, # 0x04 is reserved # Set device address for all future accesses 'LIBUSB_REQUEST_SET_ADDRESS': 0x05, # Get the specified descriptor 'LIBUSB_REQUEST_GET_DESCRIPTOR': 0x06, # Used to update existing descriptors or add new descriptors 'LIBUSB_REQUEST_SET_DESCRIPTOR': 0x07, # Get the current device configuration value 'LIBUSB_REQUEST_GET_CONFIGURATION': 0x08, # Set device configuration 'LIBUSB_REQUEST_SET_CONFIGURATION': 0x09, # Return the selected alternate setting for the specified interface 'LIBUSB_REQUEST_GET_INTERFACE': 0x0a, # Select an alternate interface for the specified interface 'LIBUSB_REQUEST_SET_INTERFACE': 0x0b, # Set then report an endpoint's synchronization frame 'LIBUSB_REQUEST_SYNCH_FRAME': 0x0c, }) # Request type bits of the bmRequestType field in control transfers. libusb_request_type = Enum({ # Standard 'LIBUSB_REQUEST_TYPE_STANDARD': (0x00 << 5), # Class 'LIBUSB_REQUEST_TYPE_CLASS': (0x01 << 5), # Vendor 'LIBUSB_REQUEST_TYPE_VENDOR': (0x02 << 5), # Reserved 'LIBUSB_REQUEST_TYPE_RESERVED': (0x03 << 5), }) # BBB # pylint: disable=bad-whitespace,undefined-variable LIBUSB_TYPE_STANDARD = LIBUSB_REQUEST_TYPE_STANDARD LIBUSB_TYPE_CLASS = LIBUSB_REQUEST_TYPE_CLASS LIBUSB_TYPE_VENDOR = LIBUSB_REQUEST_TYPE_VENDOR LIBUSB_TYPE_RESERVED = LIBUSB_REQUEST_TYPE_RESERVED # pylint: enable=bad-whitespace,undefined-variable # Recipient bits of the bmRequestType field in control transfers. Values 4 # through 31 are reserved. libusb_request_recipient = Enum({ # Device 'LIBUSB_RECIPIENT_DEVICE': 0x00, # Interface 'LIBUSB_RECIPIENT_INTERFACE': 0x01, # Endpoint 'LIBUSB_RECIPIENT_ENDPOINT': 0x02, # Other 'LIBUSB_RECIPIENT_OTHER': 0x03, }) LIBUSB_ISO_SYNC_TYPE_MASK = 0x0c # Synchronization type for isochronous endpoints. Values for bits 2:3 of the # bmAttributes field in libusb_endpoint_descriptor. libusb_iso_sync_type = Enum({ # No synchronization 'LIBUSB_ISO_SYNC_TYPE_NONE': 0, # Asynchronous 'LIBUSB_ISO_SYNC_TYPE_ASYNC': 1, # Adaptive 'LIBUSB_ISO_SYNC_TYPE_ADAPTIVE': 2, # Synchronous 'LIBUSB_ISO_SYNC_TYPE_SYNC': 3, }) LIBUSB_ISO_USAGE_TYPE_MASK = 0x30 # Usage type for isochronous endpoints. Values for bits 4:5 of the # bmAttributes field in libusb_endpoint_descriptor. libusb_iso_usage_type = Enum({ # Data endpoint 'LIBUSB_ISO_USAGE_TYPE_DATA': 0, # Feedback endpoint 'LIBUSB_ISO_USAGE_TYPE_FEEDBACK': 1, # Implicit feedback Data endpoint 'LIBUSB_ISO_USAGE_TYPE_IMPLICIT': 2, }) # A structure representing the standard USB device descriptor. This # descriptor is documented in section 9.6.1 of the USB 2.0 specification. # All multiple-byte fields are represented in host-endian format. class libusb_device_descriptor(Structure): _fields_ = [ # Size of this descriptor (in bytes) ('bLength', c_uint8), # Descriptor type. Will have value LIBUSB_DT_DEVICE in this # context. ('bDescriptorType', c_uint8), # USB specification release number in binary-coded decimal. A # value of 0x0200 indicates USB 2.0, 0x0110 indicates USB 1.1, # etc. ('bcdUSB', c_uint16), # USB-IF class code for the device. See libusb_class_code. ('bDeviceClass', c_uint8), # USB-IF subclass code for the device, qualified by the # bDeviceClass value ('bDeviceSubClass', c_uint8), # USB-IF protocol code for the device, qualified by the # bDeviceClass and bDeviceSubClass values ('bDeviceProtocol', c_uint8), # Maximum packet size for endpoint 0 ('bMaxPacketSize0', c_uint8), # USB-IF vendor ID ('idVendor', c_uint16), # USB-IF product ID ('idProduct', c_uint16), # Device release number in binary-coded decimal ('bcdDevice', c_uint16), # Index of string descriptor describing manufacturer ('iManufacturer', c_uint8), # Index of string descriptor describing product ('iProduct', c_uint8), # Index of string descriptor containing device serial number ('iSerialNumber', c_uint8), # Number of possible configurations ('bNumConfigurations', c_uint8)] libusb_device_descriptor_p = POINTER(libusb_device_descriptor) class libusb_endpoint_descriptor(Structure): _fields_ = [ ('bLength', c_uint8), ('bDescriptorType', c_uint8), ('bEndpointAddress', c_uint8), ('bmAttributes', c_uint8), ('wMaxPacketSize', c_uint16), ('bInterval', c_uint8), ('bRefresh', c_uint8), ('bSynchAddress', c_uint8), ('extra', c_void_p), ('extra_length', c_int)] libusb_endpoint_descriptor_p = POINTER(libusb_endpoint_descriptor) class libusb_interface_descriptor(Structure): _fields_ = [ ('bLength', c_uint8), ('bDescriptorType', c_uint8), ('bInterfaceNumber', c_uint8), ('bAlternateSetting', c_uint8), ('bNumEndpoints', c_uint8), ('bInterfaceClass', c_uint8), ('bInterfaceSubClass', c_uint8), ('bInterfaceProtocol', c_uint8), ('iInterface', c_uint8), ('endpoint', libusb_endpoint_descriptor_p), ('extra', c_void_p), ('extra_length', c_int)] libusb_interface_descriptor_p = POINTER(libusb_interface_descriptor) class libusb_interface(Structure): _fields_ = [('altsetting', libusb_interface_descriptor_p), ('num_altsetting', c_int)] libusb_interface_p = POINTER(libusb_interface) class libusb_config_descriptor(Structure): _fields_ = [ ('bLength', c_uint8), ('bDescriptorType', c_uint8), ('wTotalLength', c_uint16), ('bNumInterfaces', c_uint8), ('bConfigurationValue', c_uint8), ('iConfiguration', c_uint8), ('bmAttributes', c_uint8), ('MaxPower', c_uint8), ('interface', libusb_interface_p), ('extra', c_void_p), ('extra_length', c_int)] libusb_config_descriptor_p = POINTER(libusb_config_descriptor) libusb_config_descriptor_p_p = POINTER(libusb_config_descriptor_p) class libusb_control_setup(Structure): _fields_ = [ ('bmRequestType', c_uint8), ('bRequest', c_uint8), ('wValue', c_uint16), ('wIndex', c_uint16), ('wLength', c_uint16)] libusb_control_setup_p = POINTER(libusb_control_setup) LIBUSB_CONTROL_SETUP_SIZE = sizeof(libusb_control_setup) # Structure representing a libusb session. The concept of individual libusb # sessions allows for your program to use two libraries (or dynamically # load two modules) which both independently use libusb. This will prevent # interference between the individual libusb users - for example # libusb_set_debug() will not affect the other user of the library, and # libusb_exit() will not destroy resources that the other user is still # using. # # Sessions are created by libusb_init() and destroyed through libusb_exit(). # If your application is guaranteed to only ever include a single libusb # user (i.e. you), you do not have to worry about contexts: pass NULL in # every function call where a context is required. The default context # will be used. # # For more information, see \ref contexts. class libusb_context(Structure): pass libusb_context_p = POINTER(libusb_context) libusb_context_p_p = POINTER(libusb_context_p) # Structure representing a USB device detected on the system. This is an # opaque type for which you are only ever provided with a pointer, usually # originating from libusb_get_device_list(). # # Certain operations can be performed on a device, but in order to do any # I/O you will have to first obtain a device handle using libusb_open(). # # Devices are reference counted with libusb_device_ref() and # libusb_device_unref(), and are freed when the reference count reaches 0. # New devices presented by libusb_get_device_list() have a reference count of # 1, and libusb_free_device_list() can optionally decrease the reference count # on all devices in the list. libusb_open() adds another reference which is # later destroyed by libusb_close(). class libusb_device(Structure): pass libusb_device_p = POINTER(libusb_device) libusb_device_p_p = POINTER(libusb_device_p) libusb_device_p_p_p = POINTER(libusb_device_p_p) # Structure representing a handle on a USB device. This is an opaque type for # which you are only ever provided with a pointer, usually originating from # libusb_open(). # # A device handle is used to perform I/O and other operations. When finished # with a device handle, you should call libusb_close(). class libusb_device_handle(Structure): pass libusb_device_handle_p = POINTER(libusb_device_handle) libusb_device_handle_p_p = POINTER(libusb_device_handle_p) class libusb_version(Structure): _fields_ = [ ('major', c_uint16), ('minor', c_uint16), ('micro', c_uint16), ('nano', c_uint16), ('rc', c_char_p), ('describe', c_char_p), ] libusb_speed = Enum({ # The OS doesn't report or know the device speed. 'LIBUSB_SPEED_UNKNOWN': 0, # The device is operating at low speed (1.5MBit/s). 'LIBUSB_SPEED_LOW': 1, # The device is operating at full speed (12MBit/s). 'LIBUSB_SPEED_FULL': 2, # The device is operating at high speed (480MBit/s). 'LIBUSB_SPEED_HIGH': 3, # The device is operating at super speed (5000MBit/s). 'LIBUSB_SPEED_SUPER': 4, }) libusb_supported_speed = Enum({ # Low speed operation supported (1.5MBit/s). 'LIBUSB_LOW_SPEED_OPERATION': 1, # Full speed operation supported (12MBit/s). 'LIBUSB_FULL_SPEED_OPERATION': 2, # High speed operation supported (480MBit/s). 'LIBUSB_HIGH_SPEED_OPERATION': 4, # Superspeed operation supported (5000MBit/s). 'LIBUSB_5GBPS_OPERATION': 8, }) # Error codes. Most libusb functions return 0 on success or one of these # codes on failure. libusb_error = Enum({ # Success (no error) 'LIBUSB_SUCCESS': 0, # Input/output error 'LIBUSB_ERROR_IO': -1, # Invalid parameter 'LIBUSB_ERROR_INVALID_PARAM': -2, # Access denied (insufficient permissions) 'LIBUSB_ERROR_ACCESS': -3, # No such device (it may have been disconnected) 'LIBUSB_ERROR_NO_DEVICE': -4, # Entity not found 'LIBUSB_ERROR_NOT_FOUND': -5, # Resource busy 'LIBUSB_ERROR_BUSY': -6, # Operation timed out 'LIBUSB_ERROR_TIMEOUT': -7, # Overflow 'LIBUSB_ERROR_OVERFLOW': -8, # Pipe error 'LIBUSB_ERROR_PIPE': -9, # System call interrupted (perhaps due to signal) 'LIBUSB_ERROR_INTERRUPTED': -10, # Insufficient memory 'LIBUSB_ERROR_NO_MEM': -11, # Operation not supported or unimplemented on this platform 'LIBUSB_ERROR_NOT_SUPPORTED': -12, # Other error 'LIBUSB_ERROR_OTHER': -99, }) # Transfer status codes libusb_transfer_status = Enum({ # Transfer completed without error. Note that this does not indicate # that the entire amount of requested data was transferred. 'LIBUSB_TRANSFER_COMPLETED': 0, # Transfer failed 'LIBUSB_TRANSFER_ERROR': 1, # Transfer timed out 'LIBUSB_TRANSFER_TIMED_OUT': 2, # Transfer was cancelled 'LIBUSB_TRANSFER_CANCELLED': 3, # For bulk/interrupt endpoints: halt condition detected (endpoint # stalled). For control endpoints: control request not supported. 'LIBUSB_TRANSFER_STALL': 4, # Device was disconnected 'LIBUSB_TRANSFER_NO_DEVICE': 5, # Device sent more data than requested 'LIBUSB_TRANSFER_OVERFLOW': 6, }) # libusb_transfer.flags values libusb_transfer_flags = Enum({ # Report short frames as errors 'LIBUSB_TRANSFER_SHORT_NOT_OK': 1 << 0, # Automatically free() transfer buffer during libusb_free_transfer() 'LIBUSB_TRANSFER_FREE_BUFFER': 1 << 1, # Automatically call libusb_free_transfer() after callback returns. # If this flag is set, it is illegal to call libusb_free_transfer() # from your transfer callback, as this will result in a double-free # when this flag is acted upon. 'LIBUSB_TRANSFER_FREE_TRANSFER': 1 << 2, # Terminate transfers that are a multiple of the endpoint's # wMaxPacketSize with an extra zero length packet. 'LIBUSB_TRANSFER_ADD_ZERO_PACKET': 1 << 3, }) # Isochronous packet descriptor. class libusb_iso_packet_descriptor(Structure): _fields_ = [('length', c_uint), ('actual_length', c_uint), ('status', c_int)] # enum libusb_transfer_status libusb_iso_packet_descriptor_p = POINTER(libusb_iso_packet_descriptor) class libusb_transfer(Structure): pass libusb_transfer_p = POINTER(libusb_transfer) libusb_transfer_cb_fn_p = CFUNCTYPE(None, libusb_transfer_p) _libusb_transfer_fields = [ ('dev_handle', libusb_device_handle_p), ('flags', c_uint8), ('endpoint', c_uchar), ('type', c_uchar), ('timeout', c_uint), ('status', c_int), # enum libusb_transfer_status ('length', c_int), ('actual_length', c_int), ('callback', libusb_transfer_cb_fn_p), ('user_data', c_void_p), ('buffer', c_void_p), ('num_iso_packets', c_int), ('iso_packet_desc', libusb_iso_packet_descriptor) ] if 'FreeBSD' in platform.system() and getattr( libusb, 'libusb_get_string_descriptor', None ) is None: # Old FreeBSD version has a slight ABI incompatibility. # Work around it unless libusb_get_string_descriptor is available, as it # is only available on fixed versions. assert _libusb_transfer_fields[2][0] == 'endpoint' _libusb_transfer_fields[2] = ('endpoint', c_uint32) assert _libusb_transfer_fields[11][0] == 'num_iso_packets' _libusb_transfer_fields.insert(11, ('os_priv', c_void_p)) # pylint: disable=protected-access libusb_transfer._fields_ = _libusb_transfer_fields # pylint: enable=protected-access libusb_capability = Enum({ # The libusb_has_capability() API is available. 'LIBUSB_CAP_HAS_CAPABILITY': 0x0000, # Hotplug support is available. 'LIBUSB_CAP_HAS_HOTPLUG': 0x0001, # The library can access HID devices without requiring user intervention. 'LIBUSB_CAP_HAS_HID_ACCESS': 0x0100, # The library supports detaching of the default USB driver. 'LIBUSB_CAP_SUPPORTS_DETACH_KERNEL_DRIVER': 0x0101, }) libusb_log_level = Enum({ 'LIBUSB_LOG_LEVEL_NONE': 0, 'LIBUSB_LOG_LEVEL_ERROR': 1, 'LIBUSB_LOG_LEVEL_WARNING': 2, 'LIBUSB_LOG_LEVEL_INFO': 3, 'LIBUSB_LOG_LEVEL_DEBUG': 4, }) #int libusb_init(libusb_context **ctx); libusb_init = libusb.libusb_init libusb_init.argtypes = [libusb_context_p_p] #void libusb_exit(libusb_context *ctx); libusb_exit = libusb.libusb_exit libusb_exit.argtypes = [libusb_context_p] libusb_exit.restype = None #void libusb_set_debug(libusb_context *ctx, int level); libusb_set_debug = libusb.libusb_set_debug libusb_set_debug.argtypes = [libusb_context_p, c_int] libusb_set_debug.restype = None #const struct libusb_version * libusb_get_version(void); try: libusb_get_version = libusb.libusb_get_version except AttributeError: _dummy_version = libusb_version(0, 0, 0, 0, _empty_char_p, _empty_char_p) _dummy_version_p = pointer(_dummy_version) def libusb_get_version(): return _dummy_version_p else: libusb_get_version.argtypes = [] libusb_get_version.restype = POINTER(libusb_version) #int libusb_has_capability(uint32_t capability); try: libusb_has_capability = libusb.libusb_has_capability except AttributeError: def libusb_has_capability(_): return 0 else: libusb_has_capability.argtypes = [c_uint32] libusb_has_capability.restype = c_int try: # Note: Should be equivalent to libusb_error.get (except libusb_error.get # one raises on unknown values). #char *libusb_error_name(int errcode); libusb_error_name = libusb.libusb_error_name except AttributeError: # pylint: disable=unused-argument def libusb_error_name(errcode): return None # pylint: enable=unused-argument else: libusb_error_name.argtypes = [c_int] libusb_error_name.restype = c_char_p # Note on libusb_strerror, libusb_setlocale and future functions in the # same spirit: # I do not think end-user-facing messages belong to a technical library. # Such features bring a new, non essential set of problems, and is a luxury # I do not want to spend time supporting considering limited resources and # more important stuff to work on. # For backward compatibility, expose libusb_strerror placeholder. # pylint: disable=unused-argument def libusb_strerror(errcode): return None # pylint: enable=unused-argument #ssize_t libusb_get_device_list(libusb_context *ctx, # libusb_device ***list); libusb_get_device_list = libusb.libusb_get_device_list libusb_get_device_list.argtypes = [libusb_context_p, libusb_device_p_p_p] libusb_get_device_list.restype = c_ssize_t #void libusb_free_device_list(libusb_device **list, int unref_devices); libusb_free_device_list = libusb.libusb_free_device_list libusb_free_device_list.argtypes = [libusb_device_p_p, c_int] libusb_free_device_list.restype = None #libusb_device *libusb_ref_device(libusb_device *dev); libusb_ref_device = libusb.libusb_ref_device libusb_ref_device.argtypes = [libusb_device_p] libusb_ref_device.restype = libusb_device_p #void libusb_unref_device(libusb_device *dev); libusb_unref_device = libusb.libusb_unref_device libusb_unref_device.argtypes = [libusb_device_p] libusb_unref_device.restype = None #int libusb_get_configuration(libusb_device_handle *dev, int *config); libusb_get_configuration = libusb.libusb_get_configuration libusb_get_configuration.argtypes = [libusb_device_handle_p, c_int_p] #int libusb_get_device_descriptor(libusb_device *dev, # struct libusb_device_descriptor *desc); libusb_get_device_descriptor = libusb.libusb_get_device_descriptor libusb_get_device_descriptor.argtypes = [ libusb_device_p, libusb_device_descriptor_p] #int libusb_get_active_config_descriptor(libusb_device *dev, # struct libusb_config_descriptor **config); libusb_get_active_config_descriptor = libusb.libusb_get_active_config_descriptor libusb_get_active_config_descriptor.argtypes = [ libusb_device_p, libusb_config_descriptor_p_p] #int libusb_get_config_descriptor(libusb_device *dev, uint8_t config_index, # struct libusb_config_descriptor **config); libusb_get_config_descriptor = libusb.libusb_get_config_descriptor libusb_get_config_descriptor.argtypes = [ libusb_device_p, c_uint8, libusb_config_descriptor_p_p] #int libusb_get_config_descriptor_by_value(libusb_device *dev, # uint8_t bConfigurationValue, struct libusb_config_descriptor **config); libusb_get_config_descriptor_by_value = \ libusb.libusb_get_config_descriptor_by_value libusb_get_config_descriptor_by_value.argtypes = [ libusb_device_p, c_uint8, libusb_config_descriptor_p_p] #void libusb_free_config_descriptor(struct libusb_config_descriptor *config); libusb_free_config_descriptor = libusb.libusb_free_config_descriptor libusb_free_config_descriptor.argtypes = [libusb_config_descriptor_p] libusb_free_config_descriptor.restype = None #uint8_t libusb_get_bus_number(libusb_device *dev); libusb_get_bus_number = libusb.libusb_get_bus_number libusb_get_bus_number.argtypes = [libusb_device_p] libusb_get_bus_number.restype = c_uint8 try: #uint8_t libusb_get_port_number(libusb_device *dev); libusb_get_port_number = libusb.libusb_get_port_number except AttributeError: pass else: libusb_get_port_number.argtypes = [libusb_device_p] libusb_get_port_number.restype = c_uint8 try: #int libusb_get_port_numbers(libusb_device *dev, # uint8_t* port_numbers, int port_numbers_len); libusb_get_port_numbers = libusb.libusb_get_port_numbers except AttributeError: pass else: libusb_get_port_numbers.argtypes = [ libusb_device_p, POINTER(c_uint8), c_int] libusb_get_port_numbers.restype = c_int # Missing: libusb_get_port_path (deprecated since 1.0.16) try: #libusb_device * LIBUSB_CALL libusb_get_parent(libusb_device *dev); libusb_get_parent = libusb.libusb_get_parent except AttributeError: pass else: libusb_get_parent.argtypes = [libusb_device_p] libusb_get_parent.restype = libusb_device_p #uint8_t libusb_get_device_address(libusb_device *dev); libusb_get_device_address = libusb.libusb_get_device_address libusb_get_device_address.argtypes = [libusb_device_p] libusb_get_device_address.restype = c_uint8 try: #int libusb_get_device_speed(libusb_device *dev); libusb_get_device_speed = libusb.libusb_get_device_speed except AttributeError: # Place holder def libusb_get_device_speed(_): # pylint: disable=undefined-variable return LIBUSB_SPEED_UNKNOWN # pylint: enable=undefined-variable else: libusb_get_device_speed.argtypes = [libusb_device_p] #int libusb_get_max_packet_size(libusb_device *dev, unsigned char endpoint); libusb_get_max_packet_size = libusb.libusb_get_max_packet_size libusb_get_max_packet_size.argtypes = [libusb_device_p, c_uchar] #int libusb_get_max_iso_packet_size(libusb_device *dev, unsigned char endpoint); try: libusb_get_max_iso_packet_size = libusb.libusb_get_max_iso_packet_size except AttributeError: # FreeBSD's reimplementation of the API [used to ]lack[s] this function. # It has been added in r234193, but is lacking in default 9.x install as # of this change. Provide a fallback to error-out only if actually used. # pylint: disable=unused-argument def libusb_get_max_iso_packet_size(_, __): raise NotImplementedError # pylint: enable=unused-argument else: libusb_get_max_iso_packet_size.argtypes = [libusb_device_p, c_uchar] #int libusb_open(libusb_device *dev, libusb_device_handle **handle); libusb_open = libusb.libusb_open libusb_open.argtypes = [libusb_device_p, libusb_device_handle_p_p] #void libusb_close(libusb_device_handle *dev_handle); libusb_close = libusb.libusb_close libusb_close.argtypes = [libusb_device_handle_p] libusb_close.restype = None #libusb_device *libusb_get_device(libusb_device_handle *dev_handle); libusb_get_device = libusb.libusb_get_device libusb_get_device.argtypes = [libusb_device_handle_p] libusb_get_device.restype = libusb_device_p #int libusb_set_configuration(libusb_device_handle *dev, int configuration); libusb_set_configuration = libusb.libusb_set_configuration libusb_set_configuration.argtypes = [libusb_device_handle_p, c_int] #int libusb_claim_interface(libusb_device_handle *dev, int iface); libusb_claim_interface = libusb.libusb_claim_interface libusb_claim_interface.argtypes = [libusb_device_handle_p, c_int] #int libusb_release_interface(libusb_device_handle *dev, int iface); libusb_release_interface = libusb.libusb_release_interface libusb_release_interface.argtypes = [libusb_device_handle_p, c_int] #libusb_device_handle *libusb_open_device_with_vid_pid(libusb_context *ctx, # uint16_t vendor_id, uint16_t product_id); libusb_open_device_with_vid_pid = libusb.libusb_open_device_with_vid_pid libusb_open_device_with_vid_pid.argtypes = [ libusb_context_p, c_uint16, c_uint16] libusb_open_device_with_vid_pid.restype = libusb_device_handle_p #int libusb_set_interface_alt_setting(libusb_device_handle *dev, # int interface_number, int alternate_setting); libusb_set_interface_alt_setting = libusb.libusb_set_interface_alt_setting libusb_set_interface_alt_setting.argtypes = [ libusb_device_handle_p, c_int, c_int] #int libusb_clear_halt(libusb_device_handle *dev, unsigned char endpoint); libusb_clear_halt = libusb.libusb_clear_halt libusb_clear_halt.argtypes = [libusb_device_handle_p, c_uchar] #int libusb_reset_device(libusb_device_handle *dev); libusb_reset_device = libusb.libusb_reset_device libusb_reset_device.argtypes = [libusb_device_handle_p] #int libusb_kernel_driver_active(libusb_device_handle *dev, int interface); libusb_kernel_driver_active = libusb.libusb_kernel_driver_active libusb_kernel_driver_active.argtypes = [libusb_device_handle_p, c_int] #int libusb_detach_kernel_driver(libusb_device_handle *dev, int interface); libusb_detach_kernel_driver = libusb.libusb_detach_kernel_driver libusb_detach_kernel_driver.argtypes = [libusb_device_handle_p, c_int] #int libusb_attach_kernel_driver(libusb_device_handle *dev, int interface); libusb_attach_kernel_driver = libusb.libusb_attach_kernel_driver libusb_attach_kernel_driver.argtypes = [libusb_device_handle_p, c_int] try: #int libusb_set_auto_detach_kernel_driver( # libusb_device_handle *dev, int enable); libusb_set_auto_detach_kernel_driver = \ libusb.libusb_set_auto_detach_kernel_driver except AttributeError: pass else: libusb_set_auto_detach_kernel_driver.argtypes = [ libusb_device_handle_p, c_int] libusb_set_auto_detach_kernel_driver.restype = c_int # Get the data section of a control transfer. This convenience function is here # to remind you that the data does not start until 8 bytes into the actual # buffer, as the setup packet comes first. # # Calling this function only makes sense from a transfer callback function, # or situations where you have already allocated a suitably sized buffer at # transfer->buffer. # # \param transfer a transfer # \returns pointer to the first byte of the data section def libusb_control_transfer_get_data(transfer_p): transfer = transfer_p.contents return buffer_at(transfer.buffer.value, transfer.length)[ LIBUSB_CONTROL_SETUP_SIZE:] def libusb_control_transfer_get_setup(transfer_p): return cast(transfer_p.contents.buffer, libusb_control_setup_p) def libusb_fill_control_setup( setup_p, bmRequestType, bRequest, wValue, wIndex, wLength): setup = cast(setup_p, libusb_control_setup_p).contents setup.bmRequestType = bmRequestType setup.bRequest = bRequest setup.wValue = libusb_cpu_to_le16(wValue) setup.wIndex = libusb_cpu_to_le16(wIndex) setup.wLength = libusb_cpu_to_le16(wLength) #struct libusb_transfer *libusb_alloc_transfer(int iso_packets); libusb_alloc_transfer = libusb.libusb_alloc_transfer libusb_alloc_transfer.argtypes = [c_int] libusb_alloc_transfer.restype = libusb_transfer_p #int libusb_submit_transfer(struct libusb_transfer *transfer); libusb_submit_transfer = libusb.libusb_submit_transfer libusb_submit_transfer.argtypes = [libusb_transfer_p] #int libusb_cancel_transfer(struct libusb_transfer *transfer); libusb_cancel_transfer = libusb.libusb_cancel_transfer libusb_cancel_transfer.argtypes = [libusb_transfer_p] #void libusb_free_transfer(struct libusb_transfer *transfer); libusb_free_transfer = libusb.libusb_free_transfer libusb_free_transfer.argtypes = [libusb_transfer_p] libusb_free_transfer.restype = None # pylint: disable=redefined-builtin def libusb_fill_control_transfer( transfer_p, dev_handle, buffer, callback, user_data, timeout): transfer = transfer_p.contents transfer.dev_handle = dev_handle transfer.endpoint = 0 # pylint: disable=undefined-variable transfer.type = LIBUSB_TRANSFER_TYPE_CONTROL # pylint: enable=undefined-variable transfer.timeout = timeout transfer.buffer = cast(buffer, c_void_p) if buffer is not None: setup = cast(buffer, libusb_control_setup_p).contents # pylint: disable=undefined-variable transfer.length = LIBUSB_CONTROL_SETUP_SIZE + \ libusb_le16_to_cpu(setup.wLength) # pylint: enable=undefined-variable transfer.user_data = user_data transfer.callback = callback # pylint: enable=redefined-builtin # pylint: disable=redefined-builtin def libusb_fill_bulk_transfer( transfer_p, dev_handle, endpoint, buffer, length, callback, user_data, timeout): transfer = transfer_p.contents transfer.dev_handle = dev_handle transfer.endpoint = endpoint # pylint: disable=undefined-variable transfer.type = LIBUSB_TRANSFER_TYPE_BULK # pylint: enable=undefined-variable transfer.timeout = timeout transfer.buffer = cast(buffer, c_void_p) transfer.length = length transfer.user_data = user_data transfer.callback = callback # pylint: enable=redefined-builtin # pylint: disable=redefined-builtin def libusb_fill_interrupt_transfer( transfer_p, dev_handle, endpoint, buffer, length, callback, user_data, timeout): transfer = transfer_p.contents transfer.dev_handle = dev_handle transfer.endpoint = endpoint # pylint: disable=undefined-variable transfer.type = LIBUSB_TRANSFER_TYPE_INTERRUPT # pylint: enable=undefined-variable transfer.timeout = timeout transfer.buffer = cast(buffer, c_void_p) transfer.length = length transfer.user_data = user_data transfer.callback = callback # pylint: enable=redefined-builtin # pylint: disable=redefined-builtin def libusb_fill_iso_transfer( transfer_p, dev_handle, endpoint, buffer, length, num_iso_packets, callback, user_data, timeout): transfer = transfer_p.contents transfer.dev_handle = dev_handle transfer.endpoint = endpoint # pylint: disable=undefined-variable transfer.type = LIBUSB_TRANSFER_TYPE_ISOCHRONOUS # pylint: enable=undefined-variable transfer.timeout = timeout transfer.buffer = cast(buffer, c_void_p) transfer.length = length transfer.num_iso_packets = num_iso_packets transfer.user_data = user_data transfer.callback = callback # pylint: enable=redefined-builtin def _get_iso_packet_list(transfer): list_type = libusb_iso_packet_descriptor * transfer.num_iso_packets return list_type.from_address(addressof(transfer.iso_packet_desc)) def get_iso_packet_list(transfer_p): """ Python-specific helper extracting a list of iso packet descriptors, because it's not as straight-forward as in C. """ return _get_iso_packet_list(transfer_p.contents) def _get_iso_packet_buffer(transfer, offset, length): return buffer_at(transfer.buffer + offset, length) def get_iso_packet_buffer_list(transfer_p): """ Python-specific helper extracting a list of iso packet buffers. """ transfer = transfer_p.contents offset = 0 result = [] append = result.append for iso_transfer in _get_iso_packet_list(transfer): length = iso_transfer.length append(_get_iso_packet_buffer(transfer, offset, length)) offset += length return result def get_extra(descriptor): """ Python-specific helper to access "extra" field of descriptors, because it's not as straight-forward as in C. Returns a list, where each entry is an individual extra descriptor. """ result = [] extra_length = descriptor.extra_length if extra_length: extra = buffer_at(descriptor.extra.value, extra_length) append = result.append while extra: length = _string_item_to_int(extra[0]) if not 0 < length <= len(extra): raise ValueError( 'Extra descriptor %i is incomplete/invalid' % ( len(result), ), ) append(extra[:length]) extra = extra[length:] return result def libusb_set_iso_packet_lengths(transfer_p, length): transfer = transfer_p.contents for iso_packet_desc in _get_iso_packet_list(transfer): iso_packet_desc.length = length def libusb_get_iso_packet_buffer(transfer_p, packet): transfer = transfer_p.contents offset = 0 if packet >= transfer.num_iso_packets: return None iso_packet_desc_list = _get_iso_packet_list(transfer) for i in xrange(packet): offset += iso_packet_desc_list[i].length return _get_iso_packet_buffer( transfer, offset, iso_packet_desc_list[packet].length) def libusb_get_iso_packet_buffer_simple(transfer_p, packet): transfer = transfer_p.contents if packet >= transfer.num_iso_packets: return None iso_length = transfer.iso_packet_desc.length return _get_iso_packet_buffer(transfer, iso_length * packet, iso_length) # sync I/O #int libusb_control_transfer(libusb_device_handle *dev_handle, # uint8_t request_type, uint8_t request, uint16_t value, uint16_t index, # unsigned char *data, uint16_t length, unsigned int timeout); libusb_control_transfer = libusb.libusb_control_transfer libusb_control_transfer.argtypes = [libusb_device_handle_p, c_uint8, c_uint8, c_uint16, c_uint16, c_void_p, c_uint16, c_uint] #int libusb_bulk_transfer(libusb_device_handle *dev_handle, # unsigned char endpoint, unsigned char *data, int length, # int *actual_length, unsigned int timeout); libusb_bulk_transfer = libusb.libusb_bulk_transfer libusb_bulk_transfer.argtypes = [libusb_device_handle_p, c_uchar, c_void_p, c_int, c_int_p, c_uint] #int libusb_interrupt_transfer(libusb_device_handle *dev_handle, # unsigned char endpoint, unsigned char *data, int length, # int *actual_length, unsigned int timeout); libusb_interrupt_transfer = libusb.libusb_interrupt_transfer libusb_interrupt_transfer.argtypes = [libusb_device_handle_p, c_uchar, c_void_p, c_int, c_int_p, c_uint] # pylint: disable=undefined-variable def libusb_get_descriptor(dev, desc_type, desc_index, data, length): return libusb_control_transfer(dev, LIBUSB_ENDPOINT_IN, LIBUSB_REQUEST_GET_DESCRIPTOR, (desc_type << 8) | desc_index, 0, data, length, 1000) # pylint: enable=undefined-variable # pylint: disable=undefined-variable def libusb_get_string_descriptor(dev, desc_index, langid, data, length): return libusb_control_transfer(dev, LIBUSB_ENDPOINT_IN, LIBUSB_REQUEST_GET_DESCRIPTOR, (LIBUSB_DT_STRING << 8) | desc_index, langid, data, length, 1000) # pylint: enable=undefined-variable #int libusb_get_string_descriptor_ascii(libusb_device_handle *dev, # uint8_t index, unsigned char *data, int length); libusb_get_string_descriptor_ascii = libusb.libusb_get_string_descriptor_ascii libusb_get_string_descriptor_ascii.argtypes = [libusb_device_handle_p, c_uint8, c_void_p, c_int] # polling and timeouts #int libusb_try_lock_events(libusb_context *ctx); libusb_try_lock_events = libusb.libusb_try_lock_events libusb_try_lock_events.argtypes = [libusb_context_p] #void libusb_lock_events(libusb_context *ctx); libusb_lock_events = libusb.libusb_lock_events libusb_lock_events.argtypes = [libusb_context_p] #void libusb_unlock_events(libusb_context *ctx); libusb_unlock_events = libusb.libusb_unlock_events libusb_unlock_events.argtypes = [libusb_context_p] libusb_unlock_events.restype = None #int libusb_event_handling_ok(libusb_context *ctx); libusb_event_handling_ok = libusb.libusb_event_handling_ok libusb_event_handling_ok.argtypes = [libusb_context_p] #int libusb_event_handler_active(libusb_context *ctx); libusb_event_handler_active = libusb.libusb_event_handler_active libusb_event_handler_active.argtypes = [libusb_context_p] #void libusb_lock_event_waiters(libusb_context *ctx); libusb_lock_event_waiters = libusb.libusb_lock_event_waiters libusb_lock_event_waiters.argtypes = [libusb_context_p] libusb_lock_event_waiters.restype = None #void libusb_unlock_event_waiters(libusb_context *ctx); libusb_unlock_event_waiters = libusb.libusb_unlock_event_waiters libusb_unlock_event_waiters.argtypes = [] libusb_unlock_event_waiters.restype = None #int libusb_wait_for_event(libusb_context *ctx, struct timeval *tv); libusb_wait_for_event = libusb.libusb_wait_for_event libusb_wait_for_event.argtypes = [libusb_context_p, timeval_p] #int libusb_handle_events_timeout(libusb_context *ctx, struct timeval *tv); libusb_handle_events_timeout = libusb.libusb_handle_events_timeout libusb_handle_events_timeout.argtypes = [libusb_context_p, timeval_p] #int libusb_handle_events_timeout_completed(libusb_context *ctx, # struct timeval *tv, int *completed); try: libusb_handle_events_timeout_completed = libusb.\ libusb_handle_events_timeout_completed except AttributeError: # No safe replacement possible. pass else: libusb_handle_events_timeout_completed.argtypes = [ libusb_context_p, timeval_p, c_int_p] #int libusb_handle_events(libusb_context *ctx); libusb_handle_events = libusb.libusb_handle_events libusb_handle_events.argtypes = [libusb_context_p] #int libusb_handle_events_completed(libusb_context *ctx, int *completed); try: libusb_handle_events_completed = libusb.libusb_handle_events_completed except AttributeError: # No safe replacement possible. pass else: libusb_handle_events_completed.argtypes = [libusb_context_p, c_int_p] #int libusb_handle_events_locked(libusb_context *ctx, struct timeval *tv); libusb_handle_events_locked = libusb.libusb_handle_events_locked libusb_handle_events_locked.argtypes = [libusb_context_p, timeval_p] #int libusb_get_next_timeout(libusb_context *ctx, struct timeval *tv); libusb_get_next_timeout = libusb.libusb_get_next_timeout libusb_get_next_timeout.argtypes = [libusb_context_p, timeval_p] class libusb_pollfd(Structure): _fields_ = [ ('fd', c_int), ('events', c_short), ] libusb_pollfd_p = POINTER(libusb_pollfd) libusb_pollfd_p_p = POINTER(libusb_pollfd_p) libusb_pollfd_added_cb_p = CFUNCTYPE(None, c_int, c_short, py_object) libusb_pollfd_removed_cb_p = CFUNCTYPE(None, c_int, py_object) #const struct libusb_pollfd **libusb_get_pollfds(libusb_context *ctx); libusb_get_pollfds = libusb.libusb_get_pollfds libusb_get_pollfds.argtypes = [libusb_context_p] libusb_get_pollfds.restype = libusb_pollfd_p_p #void libusb_set_pollfd_notifiers(libusb_context *ctx, # libusb_pollfd_added_cb added_cb, libusb_pollfd_removed_cb removed_cb, # void *user_data); libusb_set_pollfd_notifiers = libusb.libusb_set_pollfd_notifiers libusb_set_pollfd_notifiers.argtypes = [libusb_context_p, libusb_pollfd_added_cb_p, libusb_pollfd_removed_cb_p, py_object] libusb_set_pollfd_notifiers.restype = None #typedef int libusb_hotplug_callback_handle; libusb_hotplug_callback_handle = c_int libusb_hotplug_flag = Enum({ 'LIBUSB_HOTPLUG_ENUMERATE': 1, }) libusb_hotplug_event = Enum({ 'LIBUSB_HOTPLUG_EVENT_DEVICE_ARRIVED': 0x01, 'LIBUSB_HOTPLUG_EVENT_DEVICE_LEFT': 0x02, }) LIBUSB_HOTPLUG_MATCH_ANY = -1 #typedef int (*libusb_hotplug_callback_fn)(libusb_context *ctx, # libusb_device *device, libusb_hotplug_event event, void *user_data); libusb_hotplug_callback_fn_p = CFUNCTYPE( c_int, libusb_context_p, libusb_device_p, c_int, c_void_p) #int libusb_hotplug_register_callback(libusb_context *ctx, # libusb_hotplug_event events, libusb_hotplug_flag flags, # int vendor_id, int product_id, int dev_class, # libusb_hotplug_callback_fn cb_fn, void *user_data, # libusb_hotplug_callback_handle *handle); try: libusb_hotplug_register_callback = libusb.libusb_hotplug_register_callback except AttributeError: pass else: libusb_hotplug_register_callback.argtypes = [ libusb_context_p, c_int, c_int, c_int, c_int, c_int, libusb_hotplug_callback_fn_p, c_void_p, POINTER(libusb_hotplug_callback_handle), ] libusb_hotplug_register_callback.restype = c_int #void libusb_hotplug_deregister_callback(libusb_context *ctx, # libusb_hotplug_callback_handle handle); try: libusb_hotplug_deregister_callback = \ libusb.libusb_hotplug_deregister_callback except AttributeError: pass else: libusb_hotplug_deregister_callback.argtypes = [ libusb_context_p, libusb_hotplug_callback_handle, ] libusb_hotplug_deregister_callback.restype = None # /libusb.h libusb1-1.7/usb1/__init__.py0000644000175000017500000027206413404567324017066 0ustar vincentvincent00000000000000# Copyright (C) 2010-2018 Vincent Pelletier # # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public # License as published by the Free Software Foundation; either # version 2.1 of the License, or (at your option) any later version. # # This library 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 # Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public # License along with this library; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA # pylint: disable=invalid-name, too-many-locals, too-many-arguments # pylint: disable=too-many-public-methods, too-many-instance-attributes # pylint: disable=missing-docstring """ Pythonic wrapper for libusb-1.0. The first thing you must do is to get an "USB context". To do so, create an USBContext instance. Then, you can use it to browse available USB devices and open the one you want to talk to. At this point, you should have a USBDeviceHandle instance (as returned by USBContext or USBDevice instances), and you can start exchanging with the device. Features: - Basic device settings (configuration & interface selection, ...) - String descriptor lookups (ASCII & unicode), and list supported language codes - Synchronous I/O (control, bulk, interrupt) - Asynchronous I/O (control, bulk, interrupt, isochronous) Note: Isochronous support is not well tested. See USBPoller, USBTransfer and USBTransferHelper. All LIBUSB_* constants are available in this module, without the LIBUSB_ prefix - with one exception: LIBUSB_5GBPS_OPERATION is available as SUPER_SPEED_OPERATION, so it is a valid python identifier. All LIBUSB_ERROR_* constants are available in this module as exception classes, subclassing USBError. """ from __future__ import division, absolute_import from ctypes import byref, c_int, sizeof, POINTER, \ cast, c_uint8, c_uint16, c_ubyte, c_void_p, cdll, addressof, \ c_char from ctypes.util import find_library import sys import threading import warnings import weakref import collections import functools import contextlib import inspect from . import libusb1 if sys.version_info[:2] >= (2, 6): # pylint: disable=wrong-import-order,ungrouped-imports if sys.platform == 'win32': from ctypes import get_last_error as get_errno else: from ctypes import get_errno # pylint: enable=wrong-import-order,ungrouped-imports else: def get_errno(): raise NotImplementedError( 'Your python version does not support errno/last_error' ) __all__ = [ 'USBContext', 'USBDeviceHandle', 'USBDevice', 'hasCapability', 'USBPoller', 'USBTransfer', 'USBTransferHelper', 'EVENT_CALLBACK_SET', 'USBEndpoint', 'USBInterfaceSetting', 'USBInterface', 'USBConfiguration', 'DoomedTransferError', 'getVersion', 'USBError', ] # Bind libusb1 constants and libusb1.USBError to this module, so user does not # have to import two modules. USBError = libusb1.USBError STATUS_TO_EXCEPTION_DICT = {} def __bindConstants(): global_dict = globals() PREFIX = 'LIBUSB_' for name, value in libusb1.__dict__.items(): if name.startswith(PREFIX): name = name[len(PREFIX):] # Gah. if name == '5GBPS_OPERATION': name = 'SUPER_SPEED_OPERATION' assert name not in global_dict global_dict[name] = value __all__.append(name) # Finer-grained exceptions. for name, value in libusb1.libusb_error.forward_dict.items(): if value: assert name.startswith(PREFIX + 'ERROR_'), name if name == 'LIBUSB_ERROR_IO': name = 'ErrorIO' else: name = ''.join(x.capitalize() for x in name.split('_')[1:]) name = 'USB' + name assert name not in global_dict, name assert value not in STATUS_TO_EXCEPTION_DICT STATUS_TO_EXCEPTION_DICT[value] = global_dict[name] = type( name, (USBError, ), {'value': value}, ) __all__.append(name) __bindConstants() del __bindConstants def raiseUSBError( value, # Avoid globals lookup on call to work during interpreter shutdown. # pylint: disable=dangerous-default-value __STATUS_TO_EXCEPTION_DICT=STATUS_TO_EXCEPTION_DICT, # pylint: enable=dangerous-default-value __USBError=USBError, ): raise __STATUS_TO_EXCEPTION_DICT.get(value, __USBError)(value) def mayRaiseUSBError( value, # Avoid globals lookup on call to work during interpreter shutdown. __raiseUSBError=raiseUSBError, ): if value < 0: __raiseUSBError(value) return value try: namedtuple = collections.namedtuple except AttributeError: Version = tuple else: Version = namedtuple( 'Version', ['major', 'minor', 'micro', 'nano', 'rc', 'describe'], ) if sys.version_info[0] == 3: BYTE = bytes([0]) # pylint: disable=redefined-builtin xrange = range long = int # pylint: enable=redefined-builtin integer_memoryview = memoryview else: BYTE = '\x00' # Work around python2's memoryview, which only accepts & generates strings. # For consistency between async control and other async transfers. # Python 2.7 will not be fixed, so wrap its memoryview. # Breaks the no-copy promise, but control transfer performance should # matter less than other types. class integer_memoryview(object): def __init__(self, view): if not isinstance(view, memoryview): view = memoryview(view) self.__view = view # Many boring magic methods, just to mimic memoryview def __eq__(self, other): return self.__view == other def __ge__(self, other): return self.__view >= other def __gt__(self, other): return self.__view > other def __le__(self, other): return self.__view <= other def __lt__(self, other): return self.__view < other def __ne__(self, other): return self.__view != other def __hash__(self): # raises return hash(self.__view) def __delitem__(self, key): # raises del self.__view[key] def __len__(self): return len(self.__view) # To access format, itemsize, ndim, readonly, shape, strides, # suboffsets, tobytes, tolist. def __getattr__(self, name): return getattr(self.__view, name) # Actual payload of this class. def __getitem__(self, key): value = self.__view[key] if isinstance(value, memoryview): return self.__class__(value) return ord(value) def __setitem__(self, key, value): if isinstance(value, (int, long)): value = chr(value) else: value = ''.join(chr(x) for x in value) self.__view[key] = value # pylint: disable=undefined-variable CONTROL_SETUP = BYTE * CONTROL_SETUP_SIZE # pylint: enable=undefined-variable __libc_name = find_library('c') if __libc_name is None: # Of course, will leak memory. # Should we warn user ? How ? _free = lambda x: None else: _free = getattr(cdll, __libc_name).free del __libc_name try: WeakSet = weakref.WeakSet except AttributeError: # Python < 2.7: tiny wrapper around WeakKeyDictionary class WeakSet(object): def __init__(self): self.__dict = weakref.WeakKeyDictionary() def add(self, item): self.__dict[item] = None def pop(self): return self.__dict.popitem()[0] # Default string length # From a comment in libusb-1.0: "Some devices choke on size > 255" STRING_LENGTH = 255 # As of v3 of USB specs, there cannot be more than 7 hubs from controller to # device. PATH_MAX_DEPTH = 7 EVENT_CALLBACK_SET = frozenset(( # pylint: disable=undefined-variable TRANSFER_COMPLETED, TRANSFER_ERROR, TRANSFER_TIMED_OUT, TRANSFER_CANCELLED, TRANSFER_STALL, TRANSFER_NO_DEVICE, TRANSFER_OVERFLOW, # pylint: enable=undefined-variable )) DEFAULT_ASYNC_TRANSFER_ERROR_CALLBACK = lambda x: False def create_binary_buffer(init_or_size): """ ctypes.create_string_buffer variant which does not add a trailing null when init_or_size is not a size. """ # As per ctypes.create_string_buffer, as of python 2.7.10 at least: # - int or long is a length # - str or unicode is an initialiser # Testing the latter confuses 2to3, so test the former. if isinstance(init_or_size, (int, long)): init_or_size = bytearray(init_or_size) return create_initialised_buffer(init_or_size) def create_initialised_buffer(init): # raises if init is an integer - this is intentional string_type = c_char * len(init) try: # zero-copy if init is a writable buffer return string_type.from_buffer(init), init # cpython (2.7 and 3.5) raises TypeError, pypy 5.4.1 raises ValueError except (TypeError, ValueError): # create our own writable buffer init = bytearray(init) return string_type.from_buffer(init), init class DoomedTransferError(Exception): """Exception raised when altering/submitting a doomed transfer.""" pass class USBTransfer(object): """ USB asynchronous transfer control & data. All modification methods will raise if called on a submitted transfer. Methods noted as "should not be called on a submitted transfer" will not prevent you from reading, but returned value is unspecified. Note on user_data: because of pypy's current ctype restrictions, user_data is not provided to C level, but is managed purely in python. It should change nothing for you, unless you are looking at underlying C transfer structure - which you should never have to. """ # Prevent garbage collector from freeing the free function before our # instances, as we need it to property destruct them. __libusb_free_transfer = libusb1.libusb_free_transfer __libusb_cancel_transfer = libusb1.libusb_cancel_transfer __USBError = USBError # pylint: disable=undefined-variable __USBErrorNotFound = USBErrorNotFound __mayRaiseUSBError = staticmethod(mayRaiseUSBError) # pylint: enable=undefined-variable __transfer = None __initialized = False __submitted = False __callback = None __ctypesCallbackWrapper = None __doomed = False __user_data = None __transfer_buffer = None __transfer_py_buffer = None def __init__(self, handle, iso_packets, before_submit, after_completion): """ You should not instanciate this class directly. Call "getTransfer" method on an USBDeviceHandle instance to get instances of this class. """ if iso_packets < 0: raise ValueError( 'Cannot request a negative number of iso packets.' ) self.__handle = handle self.__before_submit = before_submit self.__after_completion = after_completion self.__num_iso_packets = iso_packets result = libusb1.libusb_alloc_transfer(iso_packets) if not result: # pylint: disable=undefined-variable raise USBErrorNoMem # pylint: enable=undefined-variable self.__transfer = result self.__ctypesCallbackWrapper = libusb1.libusb_transfer_cb_fn_p( self.__callbackWrapper) def close(self): """ Break reference cycles to allow instance to be garbage-collected. Raises if called on a submitted transfer. """ if self.__submitted: raise ValueError('Cannot close a submitted transfer') self.doom() self.__initialized = False # Break possible external reference cycles self.__callback = None self.__user_data = None # Break libusb_transfer reference cycles self.__ctypesCallbackWrapper = None # For some reason, overwriting callback is not enough to remove this # reference cycle - though sometimes it works: # self -> self.__dict__ -> libusb_transfer -> dict[x] -> dict[x] -> # CThunkObject -> __callbackWrapper -> self # So free transfer altogether. if self.__transfer is not None: self.__libusb_free_transfer(self.__transfer) self.__transfer = None self.__transfer_buffer = None # Break USBDeviceHandle reference cycle self.__before_submit = None self.__after_completion = None def doom(self): """ Prevent transfer from being submitted again. """ self.__doomed = True def __del__(self): if self.__transfer is not None: try: # If this doesn't raise, we're doomed; transfer was submitted, # still python decided to garbage-collect this instance. # Stick to libusb's documentation, and don't free the # transfer. If interpreter is shutting down, kernel will # reclaim memory anyway. # Note: we can't prevent transfer's buffer from being # garbage-collected as soon as there will be no remaining # reference to transfer, so a segfault might happen anyway. # Should we warn user ? How ? self.cancel() except self.__USBErrorNotFound: # Transfer was not submitted, we can free it. self.__libusb_free_transfer(self.__transfer) # pylint: disable=unused-argument def __callbackWrapper(self, transfer_p): """ Makes it possible for user-provided callback to alter transfer when fired (ie, mark transfer as not submitted upon call). """ self.__submitted = False self.__after_completion(self) callback = self.__callback if callback is not None: callback(self) if self.__doomed: self.close() # pylint: enable=unused-argument def setCallback(self, callback): """ Change transfer's callback. """ self.__callback = callback def getCallback(self): """ Get currently set callback. """ return self.__callback def setControl( self, request_type, request, value, index, buffer_or_len, callback=None, user_data=None, timeout=0): """ Setup transfer for control use. request_type, request, value, index See USBDeviceHandle.controlWrite. request_type defines transfer direction (see ENDPOINT_OUT and ENDPOINT_IN)). buffer_or_len Either a string (when sending data), or expected data length (when receiving data). callback Callback function to be invoked on transfer completion. Called with transfer as parameter, return value ignored. user_data User data to pass to callback function. timeout Transfer timeout in milliseconds. 0 to disable. """ if self.__submitted: raise ValueError('Cannot alter a submitted transfer') if self.__doomed: raise DoomedTransferError('Cannot reuse a doomed transfer') if isinstance(buffer_or_len, (int, long)): length = buffer_or_len # pylint: disable=undefined-variable string_buffer, transfer_py_buffer = create_binary_buffer( length + CONTROL_SETUP_SIZE, ) # pylint: enable=undefined-variable else: length = len(buffer_or_len) string_buffer, transfer_py_buffer = create_binary_buffer( CONTROL_SETUP + buffer_or_len, ) self.__initialized = False self.__transfer_buffer = string_buffer # pylint: disable=undefined-variable self.__transfer_py_buffer = integer_memoryview( transfer_py_buffer, )[CONTROL_SETUP_SIZE:] # pylint: enable=undefined-variable self.__user_data = user_data libusb1.libusb_fill_control_setup( string_buffer, request_type, request, value, index, length) libusb1.libusb_fill_control_transfer( self.__transfer, self.__handle, string_buffer, self.__ctypesCallbackWrapper, None, timeout) self.__callback = callback self.__initialized = True def setBulk( self, endpoint, buffer_or_len, callback=None, user_data=None, timeout=0): """ Setup transfer for bulk use. endpoint Endpoint to submit transfer to. Defines transfer direction (see ENDPOINT_OUT and ENDPOINT_IN)). buffer_or_len Either a string (when sending data), or expected data length (when receiving data) To avoid memory copies, use an object implementing the writeable buffer interface (ex: bytearray). callback Callback function to be invoked on transfer completion. Called with transfer as parameter, return value ignored. user_data User data to pass to callback function. timeout Transfer timeout in milliseconds. 0 to disable. """ if self.__submitted: raise ValueError('Cannot alter a submitted transfer') if self.__doomed: raise DoomedTransferError('Cannot reuse a doomed transfer') string_buffer, self.__transfer_py_buffer = create_binary_buffer( buffer_or_len ) self.__initialized = False self.__transfer_buffer = string_buffer self.__user_data = user_data libusb1.libusb_fill_bulk_transfer( self.__transfer, self.__handle, endpoint, string_buffer, sizeof(string_buffer), self.__ctypesCallbackWrapper, None, timeout) self.__callback = callback self.__initialized = True def setInterrupt( self, endpoint, buffer_or_len, callback=None, user_data=None, timeout=0): """ Setup transfer for interrupt use. endpoint Endpoint to submit transfer to. Defines transfer direction (see ENDPOINT_OUT and ENDPOINT_IN)). buffer_or_len Either a string (when sending data), or expected data length (when receiving data) To avoid memory copies, use an object implementing the writeable buffer interface (ex: bytearray). callback Callback function to be invoked on transfer completion. Called with transfer as parameter, return value ignored. user_data User data to pass to callback function. timeout Transfer timeout in milliseconds. 0 to disable. """ if self.__submitted: raise ValueError('Cannot alter a submitted transfer') if self.__doomed: raise DoomedTransferError('Cannot reuse a doomed transfer') string_buffer, self.__transfer_py_buffer = create_binary_buffer( buffer_or_len ) self.__initialized = False self.__transfer_buffer = string_buffer self.__user_data = user_data libusb1.libusb_fill_interrupt_transfer( self.__transfer, self.__handle, endpoint, string_buffer, sizeof(string_buffer), self.__ctypesCallbackWrapper, None, timeout) self.__callback = callback self.__initialized = True def setIsochronous( self, endpoint, buffer_or_len, callback=None, user_data=None, timeout=0, iso_transfer_length_list=None): """ Setup transfer for isochronous use. endpoint Endpoint to submit transfer to. Defines transfer direction (see ENDPOINT_OUT and ENDPOINT_IN)). buffer_or_len Either a string (when sending data), or expected data length (when receiving data) To avoid memory copies, use an object implementing the writeable buffer interface (ex: bytearray). callback Callback function to be invoked on transfer completion. Called with transfer as parameter, return value ignored. user_data User data to pass to callback function. timeout Transfer timeout in milliseconds. 0 to disable. iso_transfer_length_list List of individual transfer sizes. If not provided, buffer_or_len will be divided evenly among available transfers if possible, and raise ValueError otherwise. """ if self.__submitted: raise ValueError('Cannot alter a submitted transfer') num_iso_packets = self.__num_iso_packets if num_iso_packets == 0: raise TypeError( 'This transfer canot be used for isochronous I/O. ' 'You must get another one with a non-zero iso_packets ' 'parameter.' ) if self.__doomed: raise DoomedTransferError('Cannot reuse a doomed transfer') string_buffer, transfer_py_buffer = create_binary_buffer(buffer_or_len) buffer_length = sizeof(string_buffer) if iso_transfer_length_list is None: iso_length, remainder = divmod(buffer_length, num_iso_packets) if remainder: raise ValueError( 'Buffer size %i cannot be evenly distributed among %i ' 'transfers' % ( buffer_length, num_iso_packets, ) ) iso_transfer_length_list = [iso_length] * num_iso_packets configured_iso_packets = len(iso_transfer_length_list) if configured_iso_packets > num_iso_packets: raise ValueError( 'Too many ISO transfer lengths (%i), there are ' 'only %i ISO transfers available' % ( configured_iso_packets, num_iso_packets, ) ) if sum(iso_transfer_length_list) > buffer_length: raise ValueError( 'ISO transfers too long (%i), there are only ' '%i bytes available' % ( sum(iso_transfer_length_list), buffer_length, ) ) transfer_p = self.__transfer self.__initialized = False self.__transfer_buffer = string_buffer self.__transfer_py_buffer = transfer_py_buffer self.__user_data = user_data libusb1.libusb_fill_iso_transfer( transfer_p, self.__handle, endpoint, string_buffer, buffer_length, configured_iso_packets, self.__ctypesCallbackWrapper, None, timeout) for length, iso_packet_desc in zip( iso_transfer_length_list, libusb1.get_iso_packet_list(transfer_p)): if length <= 0: raise ValueError( 'Negative/null length transfers are not possible.' ) iso_packet_desc.length = length self.__callback = callback self.__initialized = True def getType(self): """ Get transfer type. Returns one of: TRANSFER_TYPE_CONTROL TRANSFER_TYPE_ISOCHRONOUS TRANSFER_TYPE_BULK TRANSFER_TYPE_INTERRUPT """ return self.__transfer.contents.type def getEndpoint(self): """ Get endpoint. """ return self.__transfer.contents.endpoint def getStatus(self): """ Get transfer status. Should not be called on a submitted transfer. """ return self.__transfer.contents.status def getActualLength(self): """ Get actually transfered data length. Should not be called on a submitted transfer. """ return self.__transfer.contents.actual_length def getBuffer(self): """ Get data buffer content. Should not be called on a submitted transfer. """ return self.__transfer_py_buffer def getUserData(self): """ Retrieve user data provided on setup. """ return self.__user_data def setUserData(self, user_data): """ Change user data. """ self.__user_data = user_data def getISOBufferList(self): """ Get individual ISO transfer's buffer. Returns a list with one item per ISO transfer, with their individually-configured sizes. Returned list is consistent with getISOSetupList return value. Should not be called on a submitted transfer. See also iterISO. """ transfer_p = self.__transfer transfer = transfer_p.contents # pylint: disable=undefined-variable if transfer.type != TRANSFER_TYPE_ISOCHRONOUS: # pylint: enable=undefined-variable raise TypeError( 'This method cannot be called on non-iso transfers.' ) return libusb1.get_iso_packet_buffer_list(transfer_p) def getISOSetupList(self): """ Get individual ISO transfer's setup. Returns a list of dicts, each containing an individual ISO transfer parameters: - length - actual_length - status (see libusb1's API documentation for their signification) Returned list is consistent with getISOBufferList return value. Should not be called on a submitted transfer (except for 'length' values). """ transfer_p = self.__transfer transfer = transfer_p.contents # pylint: disable=undefined-variable if transfer.type != TRANSFER_TYPE_ISOCHRONOUS: # pylint: enable=undefined-variable raise TypeError( 'This method cannot be called on non-iso transfers.' ) return [ { 'length': x.length, 'actual_length': x.actual_length, 'status': x.status, } for x in libusb1.get_iso_packet_list(transfer_p) ] def iterISO(self): """ Generator yielding (status, buffer) for each isochornous transfer. buffer is truncated to actual_length. This is more efficient than calling both getISOBufferList and getISOSetupList when receiving data. Should not be called on a submitted transfer. """ transfer_p = self.__transfer transfer = transfer_p.contents # pylint: disable=undefined-variable if transfer.type != TRANSFER_TYPE_ISOCHRONOUS: # pylint: enable=undefined-variable raise TypeError( 'This method cannot be called on non-iso transfers.' ) buffer_position = transfer.buffer for iso_transfer in libusb1.get_iso_packet_list(transfer_p): yield ( iso_transfer.status, libusb1.buffer_at(buffer_position, iso_transfer.actual_length), ) buffer_position += iso_transfer.length def setBuffer(self, buffer_or_len): """ Replace buffer with a new one. Allows resizing read buffer and replacing data sent. Note: resizing is not allowed for isochronous buffer (use setIsochronous). Note: disallowed on control transfers (use setControl). """ if self.__submitted: raise ValueError('Cannot alter a submitted transfer') transfer = self.__transfer.contents # pylint: disable=undefined-variable if transfer.type == TRANSFER_TYPE_CONTROL: # pylint: enable=undefined-variable raise ValueError( 'To alter control transfer buffer, use setControl' ) buff, transfer_py_buffer = create_binary_buffer(buffer_or_len) # pylint: disable=undefined-variable if transfer.type == TRANSFER_TYPE_ISOCHRONOUS and \ sizeof(buff) != transfer.length: # pylint: enable=undefined-variable raise ValueError( 'To alter isochronous transfer buffer length, use ' 'setIsochronous' ) self.__transfer_buffer = buff self.__transfer_py_buffer = transfer_py_buffer transfer.buffer = cast(buff, c_void_p) transfer.length = sizeof(buff) def isSubmitted(self): """ Tells if this transfer is submitted and still pending. """ return self.__submitted def submit(self): """ Submit transfer for asynchronous handling. """ if self.__submitted: raise ValueError('Cannot submit a submitted transfer') if not self.__initialized: raise ValueError( 'Cannot submit a transfer until it has been initialized' ) if self.__doomed: raise DoomedTransferError('Cannot submit doomed transfer') self.__before_submit(self) self.__submitted = True result = libusb1.libusb_submit_transfer(self.__transfer) if result: self.__after_completion(self) self.__submitted = False raiseUSBError(result) def cancel(self): """ Cancel transfer. Note: cancellation happens asynchronously, so you must wait for TRANSFER_CANCELLED. """ if not self.__submitted: # XXX: Workaround for a bug reported on libusb 1.0.8: calling # libusb_cancel_transfer on a non-submitted transfer might # trigger a segfault. raise self.__USBErrorNotFound self.__mayRaiseUSBError(self.__libusb_cancel_transfer(self.__transfer)) class USBTransferHelper(object): """ Simplifies subscribing to the same transfer over and over, and callback handling: - no need to read event status to execute apropriate code, just setup different functions for each status code - just return True instead of calling submit - no need to check if transfer is doomed before submitting it again, DoomedTransferError is caught. Callbacks used in this class must follow the callback API described in USBTransfer, and are expected to return a boolean: - True if transfer is to be submitted again (to receive/send more data) - False otherwise Note: as per libusb1 specifications, isochronous transfer global state might be TRANSFER_COMPLETED although some individual packets might have an error status. You can check individual packet status by calling getISOSetupList on transfer object in your callback. """ def __init__(self, transfer=None): """ Create a transfer callback dispatcher. transfer parameter is deprecated. If provided, it will be equivalent to: helper = USBTransferHelper() transfer.setCallback(helper) and also allows using deprecated methods on this class (otherwise, they raise AttributeError). """ if transfer is not None: # Deprecated: to drop self.__transfer = transfer transfer.setCallback(self) self.__event_callback_dict = {} self.__errorCallback = DEFAULT_ASYNC_TRANSFER_ERROR_CALLBACK def submit(self): """ Submit the asynchronous read request. Deprecated. Use submit on transfer. """ # Deprecated: to drop self.__transfer.submit() def cancel(self): """ Cancel a pending read request. Deprecated. Use cancel on transfer. """ # Deprecated: to drop self.__transfer.cancel() def setEventCallback(self, event, callback): """ Set a function to call for a given event. event must be one of: TRANSFER_COMPLETED TRANSFER_ERROR TRANSFER_TIMED_OUT TRANSFER_CANCELLED TRANSFER_STALL TRANSFER_NO_DEVICE TRANSFER_OVERFLOW """ if event not in EVENT_CALLBACK_SET: raise ValueError('Unknown event %r.' % (event, )) self.__event_callback_dict[event] = callback def setDefaultCallback(self, callback): """ Set the function to call for event which don't have a specific callback registered. The initial default callback does nothing and returns False. """ self.__errorCallback = callback def getEventCallback(self, event, default=None): """ Return the function registered to be called for given event identifier. """ return self.__event_callback_dict.get(event, default) def __call__(self, transfer): """ Callback to set on transfers. """ if self.getEventCallback(transfer.getStatus(), self.__errorCallback)( transfer): try: transfer.submit() except DoomedTransferError: pass def isSubmited(self): """ Returns whether this reader is currently waiting for an event. Deprecatd. Use isSubmitted on transfer. """ # Deprecated: to drop return self.__transfer.isSubmitted() # BBB class USBPollerThread(threading.Thread): """ Implements libusb1 documentation about threaded, asynchronous applications. In short, instanciate this class once (...per USBContext instance), call start() on the instance, and do whatever you need. This thread will be used to execute transfer completion callbacks, and you are free to use libusb1's synchronous API in another thread, and can forget about libusb1 file descriptors. See http://libusb.sourceforge.net/api-1.0/mtasync.html . """ _can_run = True def __init__(self, context, poller, exc_callback=None): """ Create a poller thread for given context. Warning: it will not check if another poller instance was already present for that context, and will replace it. poller (same as USBPoller.__init__ "poller" parameter) exc_callback (callable) Called with a libusb_error value as single parameter when event handling fails. If not given, an USBError will be raised, interrupting the thread. """ super(USBPollerThread, self).__init__() warnings.warn( 'USBPollerThread causes long stalls when used with poll (it was ' 'intended for epoll), and is generally misleading. Consider ' 'calling looping over context.handleEvents() in a thread instead.', DeprecationWarning, ) self.daemon = True self.__context = context self.__poller = poller self.__fd_set = set() if exc_callback is not None: self.exceptionHandler = exc_callback def stop(self): """ Stop & join thread. Allows stopping event thread before context gets closed. """ self._can_run = False self.join() # pylint: disable=method-hidden @staticmethod def exceptionHandler(exc): raise exc # pylint: enable=method-hidden def run(self): # We expect quite some spinning in below loop, so move any unneeded # operation out of it. context = self.__context poll = self.__poller.poll try_lock_events = context.tryLockEvents lock_event_waiters = context.lockEventWaiters wait_for_event = context.waitForEvent unlock_event_waiters = context.unlockEventWaiters event_handling_ok = context.eventHandlingOK unlock_events = context.unlockEvents handle_events_locked = context.handleEventsLocked event_handler_active = context.eventHandlerActive getNextTimeout = context.getNextTimeout exceptionHandler = self.exceptionHandler fd_set = self.__fd_set context.setPollFDNotifiers(self._registerFD, self._unregisterFD) for fd, events in context.getPollFDList(): self._registerFD(fd, events, None) try: while fd_set and self._can_run: if try_lock_events(): lock_event_waiters() while event_handler_active(): wait_for_event() unlock_event_waiters() else: try: while event_handling_ok(): if poll(getNextTimeout()): try: handle_events_locked() except USBError: exceptionHandler(sys.exc_info()[1]) finally: unlock_events() finally: context.setPollFDNotifiers(None, None) def _registerFD(self, fd, events, _): self.__poller.register(fd, events) self.__fd_set.add(fd) def _unregisterFD(self, fd, _): self.__fd_set.discard(fd) self.__poller.unregister(fd) class USBPoller(object): """ Class allowing integration of USB event polling in a file-descriptor monitoring event loop. WARNING: Do not call "poll" from several threads concurently. Do not use synchronous USB transfers in a thread while "poll" is running. Doing so will result in unnecessarily long pauses in some threads. Opening and/or closing devices while polling can cause race conditions to occur. """ def __init__(self, context, poller): """ Create a poller for given context. Warning: it will not check if another poller instance was already present for that context, and will replace it. poller is a polling instance implementing the following methods: - register(fd, event_flags) event_flags have the same meaning as in poll API (POLLIN & POLLOUT) - unregister(fd) - poll(timeout) timeout being a float in seconds, or negative/None if there is no timeout. It must return a list of (descriptor, event) pairs. Note: USBPoller is itself a valid poller. Note2: select.poll uses a timeout in milliseconds, for some reason (all other select.* classes use seconds for timeout), so you should wrap it to convert & round/truncate timeout. """ self.__context = context self.__poller = poller self.__fd_set = set() context.setPollFDNotifiers(self._registerFD, self._unregisterFD) for fd, events in context.getPollFDList(): self._registerFD(fd, events) def __del__(self): self.__context.setPollFDNotifiers(None, None) def poll(self, timeout=None): """ Poll for events. timeout can be a float in seconds, or None for no timeout. Returns a list of (descriptor, event) pairs. """ next_usb_timeout = self.__context.getNextTimeout() if timeout is None or timeout < 0: usb_timeout = next_usb_timeout elif next_usb_timeout: usb_timeout = min(next_usb_timeout, timeout) else: usb_timeout = timeout event_list = self.__poller.poll(usb_timeout) if event_list: fd_set = self.__fd_set result = [(x, y) for x, y in event_list if x not in fd_set] if len(result) != len(event_list): self.__context.handleEventsTimeout() else: result = event_list self.__context.handleEventsTimeout() return result def register(self, fd, events): """ Register an USB-unrelated fd to poller. Convenience method. """ if fd in self.__fd_set: raise ValueError( 'This fd is a special USB event fd, it cannot be polled.' ) self.__poller.register(fd, events) def unregister(self, fd): """ Unregister an USB-unrelated fd from poller. Convenience method. """ if fd in self.__fd_set: raise ValueError( 'This fd is a special USB event fd, it must stay registered.' ) self.__poller.unregister(fd) # pylint: disable=unused-argument def _registerFD(self, fd, events, user_data=None): self.register(fd, events) self.__fd_set.add(fd) # pylint: enable=unused-argument # pylint: disable=unused-argument def _unregisterFD(self, fd, user_data=None): self.__fd_set.discard(fd) self.unregister(fd) # pylint: enable=unused-argument class _ReleaseInterface(object): def __init__(self, handle, interface): self._handle = handle self._interface = interface def __enter__(self): # USBDeviceHandle.claimInterface already claimed the interface. pass def __exit__(self, exc_type, exc_val, exc_tb): self._handle.releaseInterface(self._interface) class USBDeviceHandle(object): """ Represents an opened USB device. """ __handle = None __libusb_close = libusb1.libusb_close # pylint: disable=undefined-variable __USBErrorNoDevice = USBErrorNoDevice __USBErrorNotFound = USBErrorNotFound __USBErrorInterrupted = USBErrorInterrupted # pylint: enable=undefined-variable __set = set __KeyError = KeyError __sys = sys def __init__(self, context, handle, device): """ You should not instanciate this class directly. Call "open" method on an USBDevice instance to get an USBDeviceHandle instance. """ self.__context = context # Weak reference to transfers about this device so we can clean up # before closing device. self.__transfer_set = WeakSet() # Strong references to inflight transfers so they do not get freed # even if user drops all strong references to them. If this instance # is garbage-collected, we close all transfers, so it's fine. self.__inflight = inflight = set() # XXX: For some reason, doing self.__inflight.{add|remove} inside # getTransfer causes extra intermediate python objects for each # allocated transfer. Storing them as properties solves this. Found # with objgraph. self.__inflight_add = inflight.add self.__inflight_remove = inflight.remove self.__handle = handle self.__device = device def __del__(self): self.close() def close(self): """ Close this handle. If not called explicitely, will be called by destructor. This method cancels any in-flight transfer when it is called. As cancellation is not immediate, this method needs to let libusb handle events until transfers are actually cancelled. In multi-threaded programs, this can lead to stalls. To avoid this, do not close nor let GC collect a USBDeviceHandle which has in-flight transfers. """ handle = self.__handle if handle is None: return # Build a strong set from weak self.__transfer_set so we can doom # and close all contained transfers. # Because of backward compatibility, self.__transfer_set might be a # wrapper around WeakKeyDictionary. As it might be modified by gc, # we must pop until there is not key left instead of iterating over # it. weak_transfer_set = self.__transfer_set transfer_set = self.__set() while True: try: transfer = weak_transfer_set.pop() except self.__KeyError: break transfer_set.add(transfer) transfer.doom() inflight = self.__inflight for transfer in inflight: try: transfer.cancel() except (self.__USBErrorNotFound, self.__USBErrorNoDevice): pass while inflight: try: self.__context.handleEvents() except self.__USBErrorInterrupted: pass for transfer in transfer_set: transfer.close() self.__libusb_close(handle) self.__handle = None def getDevice(self): """ Get an USBDevice instance for the device accessed through this handle, to access to the descriptors available in OS cache. """ return self.__device def getConfiguration(self): """ Get the current configuration number for this device. """ configuration = c_int() mayRaiseUSBError(libusb1.libusb_get_configuration( self.__handle, byref(configuration), )) return configuration.value def setConfiguration(self, configuration): """ Set the configuration number for this device. """ mayRaiseUSBError( libusb1.libusb_set_configuration(self.__handle, configuration), ) def getManufacturer(self): """ Get device's manufaturer name. """ return self.getASCIIStringDescriptor( self.__device.device_descriptor.iManufacturer, ) def getProduct(self): """ Get device's product name. """ return self.getASCIIStringDescriptor( self.__device.device_descriptor.iProduct, ) def getSerialNumber(self): """ Get device's serial number. """ return self.getASCIIStringDescriptor( self.__device.device_descriptor.iSerialNumber, ) def claimInterface(self, interface): """ Claim (= get exclusive access to) given interface number. Required to receive/send data. Can be used as a context manager: with handle.claimInterface(0): # do stuff # handle.releaseInterface(0) gets automatically called """ mayRaiseUSBError( libusb1.libusb_claim_interface(self.__handle, interface), ) return _ReleaseInterface(self, interface) def releaseInterface(self, interface): """ Release interface, allowing another process to use it. """ mayRaiseUSBError( libusb1.libusb_release_interface(self.__handle, interface), ) def setInterfaceAltSetting(self, interface, alt_setting): """ Set interface's alternative setting (both parameters are integers). """ mayRaiseUSBError(libusb1.libusb_set_interface_alt_setting( self.__handle, interface, alt_setting, )) def clearHalt(self, endpoint): """ Clear a halt state on given endpoint number. """ mayRaiseUSBError(libusb1.libusb_clear_halt(self.__handle, endpoint)) def resetDevice(self): """ Reinitialise current device. Attempts to restore current configuration & alt settings. If this fails, will result in a device disconnect & reconnect, so you have to close current device and rediscover it (notified by a ERROR_NOT_FOUND error code). """ mayRaiseUSBError(libusb1.libusb_reset_device(self.__handle)) def kernelDriverActive(self, interface): """ Tell whether a kernel driver is active on given interface number. """ result = libusb1.libusb_kernel_driver_active(self.__handle, interface) if result == 0: return False elif result == 1: return True raiseUSBError(result) def detachKernelDriver(self, interface): """ Ask kernel driver to detach from given interface number. """ mayRaiseUSBError( libusb1.libusb_detach_kernel_driver(self.__handle, interface), ) def attachKernelDriver(self, interface): """ Ask kernel driver to re-attach to given interface number. """ mayRaiseUSBError( libusb1.libusb_attach_kernel_driver(self.__handle, interface), ) def setAutoDetachKernelDriver(self, enable): """ Control automatic kernel driver detach. enable (bool) True to enable auto-detach, False to disable it. """ mayRaiseUSBError(libusb1.libusb_set_auto_detach_kernel_driver( self.__handle, bool(enable), )) def getSupportedLanguageList(self): """ Return a list of USB language identifiers (as integers) supported by current device for its string descriptors. Note: language identifiers seem (I didn't check them all...) very similar to windows language identifiers, so you may want to use locales.windows_locale to get an rfc3066 representation. The 5 standard HID language codes are missing though. """ descriptor_string, _ = create_binary_buffer(STRING_LENGTH) result = libusb1.libusb_get_string_descriptor( self.__handle, 0, 0, descriptor_string, sizeof(descriptor_string), ) # pylint: disable=undefined-variable if result == ERROR_PIPE: # pylint: enable=undefined-variable # From libusb_control_transfer doc: # control request not supported by the device return [] mayRaiseUSBError(result) langid_list = cast(descriptor_string, POINTER(c_uint16)) return [ libusb1.libusb_le16_to_cpu(langid_list[offset]) for offset in xrange(1, cast(descriptor_string, POINTER(c_ubyte))[0] // 2) ] def getStringDescriptor(self, descriptor, lang_id, errors='strict'): """ Fetch description string for given descriptor and in given language. Use getSupportedLanguageList to know which languages are available. Return value is a unicode string. Return None if there is no such descriptor on device. """ if descriptor == 0: return None descriptor_string = bytearray(STRING_LENGTH) try: received = mayRaiseUSBError(libusb1.libusb_get_string_descriptor( self.__handle, descriptor, lang_id, create_binary_buffer(descriptor_string)[0], STRING_LENGTH, )) # pylint: disable=undefined-variable except USBErrorNotFound: # pylint: enable=undefined-variable return None if received < 2 or descriptor_string[1] != DT_STRING: raise ValueError('Invalid string descriptor') return descriptor_string[2:min( received, descriptor_string[0], )].decode('UTF-16-LE', errors=errors) def getASCIIStringDescriptor(self, descriptor, errors='strict'): """ Fetch description string for given descriptor in first available language. Return value is a unicode string. Return None if there is no such descriptor on device. """ if descriptor == 0: return None descriptor_string = bytearray(STRING_LENGTH) try: received = mayRaiseUSBError(libusb1.libusb_get_string_descriptor_ascii( self.__handle, descriptor, create_binary_buffer(descriptor_string)[0], STRING_LENGTH, )) # pylint: disable=undefined-variable except USBErrorNotFound: # pylint: enable=undefined-variable return None return descriptor_string[:received].decode('ASCII', errors=errors) # Sync I/O def _controlTransfer( self, request_type, request, value, index, data, length, timeout): result = libusb1.libusb_control_transfer( self.__handle, request_type, request, value, index, data, length, timeout, ) mayRaiseUSBError(result) return result def controlWrite( self, request_type, request, value, index, data, timeout=0): """ Synchronous control write. request_type: request type bitmask (bmRequestType), see constants TYPE_* and RECIPIENT_*. request: request id (some values are standard). value, index, data: meaning is request-dependent. timeout: in milliseconds, how long to wait for device acknowledgement. Set to 0 to disable. To avoid memory copies, use an object implementing the writeable buffer interface (ex: bytearray) for the "data" parameter. Returns the number of bytes actually sent. """ # pylint: disable=undefined-variable request_type = (request_type & ~ENDPOINT_DIR_MASK) | ENDPOINT_OUT # pylint: enable=undefined-variable data, _ = create_initialised_buffer(data) return self._controlTransfer(request_type, request, value, index, data, sizeof(data), timeout) def controlRead( self, request_type, request, value, index, length, timeout=0): """ Synchronous control read. timeout: in milliseconds, how long to wait for data. Set to 0 to disable. See controlWrite for other parameters description. To avoid memory copies, use an object implementing the writeable buffer interface (ex: bytearray) for the "data" parameter. Returns received data. """ # pylint: disable=undefined-variable request_type = (request_type & ~ENDPOINT_DIR_MASK) | ENDPOINT_IN # pylint: enable=undefined-variable data, data_buffer = create_binary_buffer(length) transferred = self._controlTransfer( request_type, request, value, index, data, length, timeout, ) return data_buffer[:transferred] def _bulkTransfer(self, endpoint, data, length, timeout): transferred = c_int() try: mayRaiseUSBError(libusb1.libusb_bulk_transfer( self.__handle, endpoint, data, length, byref(transferred), timeout, )) except USBErrorTimeout as exception: exception.transferred = transferred.value raise return transferred.value def bulkWrite(self, endpoint, data, timeout=0): """ Synchronous bulk write. endpoint: endpoint to send data to. data: data to send. timeout: in milliseconds, how long to wait for device acknowledgement. Set to 0 to disable. To avoid memory copies, use an object implementing the writeable buffer interface (ex: bytearray) for the "data" parameter. Returns the number of bytes actually sent. May raise an exception from the USBError family. USBErrorTimeout exception has a "transferred" property giving the number of bytes sent up to the timeout. """ # pylint: disable=undefined-variable endpoint = (endpoint & ~ENDPOINT_DIR_MASK) | ENDPOINT_OUT # pylint: enable=undefined-variable data, _ = create_initialised_buffer(data) return self._bulkTransfer(endpoint, data, sizeof(data), timeout) def bulkRead(self, endpoint, length, timeout=0): """ Synchronous bulk read. timeout: in milliseconds, how long to wait for data. Set to 0 to disable. See bulkWrite for other parameters description. To avoid memory copies, use an object implementing the writeable buffer interface (ex: bytearray) for the "data" parameter. Returns received data. May raise an exception from the USBError family. USBErrorTimeout exception has a "received" property giving the bytes received up to the timeout. """ # pylint: disable=undefined-variable endpoint = (endpoint & ~ENDPOINT_DIR_MASK) | ENDPOINT_IN # pylint: enable=undefined-variable data, data_buffer = create_binary_buffer(length) try: transferred = self._bulkTransfer(endpoint, data, length, timeout) except USBErrorTimeout as exception: exception.received = data_buffer[:exception.transferred] raise return data_buffer[:transferred] def _interruptTransfer(self, endpoint, data, length, timeout): transferred = c_int() try: mayRaiseUSBError(libusb1.libusb_interrupt_transfer( self.__handle, endpoint, data, length, byref(transferred), timeout, )) except USBErrorTimeout as exception: exception.transferred = transferred.value raise return transferred.value def interruptWrite(self, endpoint, data, timeout=0): """ Synchronous interrupt write. endpoint: endpoint to send data to. data: data to send. timeout: in milliseconds, how long to wait for device acknowledgement. Set to 0 to disable. To avoid memory copies, use an object implementing the writeable buffer interface (ex: bytearray) for the "data" parameter. Returns the number of bytes actually sent. May raise an exception from the USBError family. USBErrorTimeout exception has a "transferred" property giving the number of bytes sent up to the timeout. """ # pylint: disable=undefined-variable endpoint = (endpoint & ~ENDPOINT_DIR_MASK) | ENDPOINT_OUT # pylint: enable=undefined-variable data, _ = create_initialised_buffer(data) return self._interruptTransfer(endpoint, data, sizeof(data), timeout) def interruptRead(self, endpoint, length, timeout=0): """ Synchronous interrupt write. timeout: in milliseconds, how long to wait for data. Set to 0 to disable. See interruptWrite for other parameters description. To avoid memory copies, use an object implementing the writeable buffer interface (ex: bytearray) for the "data" parameter. Returns received data. May raise an exception from the USBError family. USBErrorTimeout exception has a "received" property giving the bytes received up to the timeout. """ # pylint: disable=undefined-variable endpoint = (endpoint & ~ENDPOINT_DIR_MASK) | ENDPOINT_IN # pylint: enable=undefined-variable data, data_buffer = create_binary_buffer(length) try: transferred = self._interruptTransfer( endpoint, data, length, timeout, ) except USBErrorTimeout as exception: exception.received = data_buffer[:exception.transferred] raise return data_buffer[:transferred] def getTransfer(self, iso_packets=0): """ Get an USBTransfer instance for asynchronous use. iso_packets: the number of isochronous transfer descriptors to allocate. """ result = USBTransfer( self.__handle, iso_packets, self.__inflight_add, self.__inflight_remove, ) self.__transfer_set.add(result) return result class USBConfiguration(object): def __init__(self, context, config): """ You should not instanciate this class directly. Call USBDevice methods to get instances of this class. """ if not isinstance(config, libusb1.libusb_config_descriptor): raise TypeError('Unexpected descriptor type.') self.__config = config self.__context = context def getNumInterfaces(self): return self.__config.bNumInterfaces __len__ = getNumInterfaces def getConfigurationValue(self): return self.__config.bConfigurationValue def getDescriptor(self): return self.__config.iConfiguration def getAttributes(self): return self.__config.bmAttributes def getMaxPower(self): """ Returns device's power consumption in mW. Beware of unit: USB descriptor uses 2mW increments, this method converts it to mW units. """ return self.__config.MaxPower * 2 def getExtra(self): """ Returns a list of extra (non-basic) descriptors (DFU, HID, ...). """ return libusb1.get_extra(self.__config) def __iter__(self): """ Iterates over interfaces available in this configuration, yielding USBInterface instances. """ context = self.__context interface_list = self.__config.interface for interface_num in xrange(self.getNumInterfaces()): yield USBInterface(context, interface_list[interface_num]) # BBB iterInterfaces = __iter__ def __getitem__(self, interface): """ Returns an USBInterface instance. """ if not isinstance(interface, int): raise TypeError('interface parameter must be an integer') if not 0 <= interface < self.getNumInterfaces(): raise IndexError('No such interface: %r' % (interface, )) return USBInterface(self.__context, self.__config.interface[interface]) class USBInterface(object): def __init__(self, context, interface): """ You should not instanciate this class directly. Call USBConfiguration methods to get instances of this class. """ if not isinstance(interface, libusb1.libusb_interface): raise TypeError('Unexpected descriptor type.') self.__interface = interface self.__context = context def getNumSettings(self): return self.__interface.num_altsetting __len__ = getNumSettings def __iter__(self): """ Iterates over settings in this insterface, yielding USBInterfaceSetting instances. """ context = self.__context alt_setting_list = self.__interface.altsetting for alt_setting_num in xrange(self.getNumSettings()): yield USBInterfaceSetting( context, alt_setting_list[alt_setting_num]) # BBB iterSettings = __iter__ def __getitem__(self, alt_setting): """ Returns an USBInterfaceSetting instance. """ if not isinstance(alt_setting, int): raise TypeError('alt_setting parameter must be an integer') if not 0 <= alt_setting < self.getNumSettings(): raise IndexError('No such setting: %r' % (alt_setting, )) return USBInterfaceSetting( self.__context, self.__interface.altsetting[alt_setting]) class USBInterfaceSetting(object): def __init__(self, context, alt_setting): """ You should not instanciate this class directly. Call USBDevice or USBInterface methods to get instances of this class. """ if not isinstance(alt_setting, libusb1.libusb_interface_descriptor): raise TypeError('Unexpected descriptor type.') self.__alt_setting = alt_setting self.__context = context def getNumber(self): return self.__alt_setting.bInterfaceNumber def getAlternateSetting(self): return self.__alt_setting.bAlternateSetting def getNumEndpoints(self): return self.__alt_setting.bNumEndpoints __len__ = getNumEndpoints def getClass(self): return self.__alt_setting.bInterfaceClass def getSubClass(self): return self.__alt_setting.bInterfaceSubClass def getClassTuple(self): """ For convenience: class and subclass are probably often matched simultaneously. """ alt_setting = self.__alt_setting return (alt_setting.bInterfaceClass, alt_setting.bInterfaceSubClass) # BBB getClassTupple = getClassTuple def getProtocol(self): return self.__alt_setting.bInterfaceProtocol def getDescriptor(self): return self.__alt_setting.iInterface def getExtra(self): return libusb1.get_extra(self.__alt_setting) def __iter__(self): """ Iterates over endpoints in this interface setting , yielding USBEndpoint instances. """ context = self.__context endpoint_list = self.__alt_setting.endpoint for endpoint_num in xrange(self.getNumEndpoints()): yield USBEndpoint(context, endpoint_list[endpoint_num]) # BBB iterEndpoints = __iter__ def __getitem__(self, endpoint): """ Returns an USBEndpoint instance. """ if not isinstance(endpoint, int): raise TypeError('endpoint parameter must be an integer') if not 0 <= endpoint < self.getNumEndpoints(): raise ValueError('No such endpoint: %r' % (endpoint, )) return USBEndpoint( self.__context, self.__alt_setting.endpoint[endpoint]) class USBEndpoint(object): def __init__(self, context, endpoint): if not isinstance(endpoint, libusb1.libusb_endpoint_descriptor): raise TypeError('Unexpected descriptor type.') self.__endpoint = endpoint self.__context = context def getAddress(self): return self.__endpoint.bEndpointAddress def getAttributes(self): return self.__endpoint.bmAttributes def getMaxPacketSize(self): return self.__endpoint.wMaxPacketSize def getInterval(self): return self.__endpoint.bInterval def getRefresh(self): return self.__endpoint.bRefresh def getSyncAddress(self): return self.__endpoint.bSynchAddress def getExtra(self): return libusb1.get_extra(self.__endpoint) class USBDevice(object): """ Represents a USB device. Exposes USB descriptors which are available from OS without needing to get a USBDeviceHandle: device descriptor, configuration descriptors, interface descriptors, setting descritptors, endpoint descriptors. """ __configuration_descriptor_list = () __libusb_unref_device = libusb1.libusb_unref_device __libusb_free_config_descriptor = libusb1.libusb_free_config_descriptor __byref = byref __KeyError = KeyError def __init__(self, context, device_p, can_load_configuration=True): """ You should not instanciate this class directly. Call USBContext methods to receive instances of this class. """ self.__context = context self.__close_set = WeakSet() libusb1.libusb_ref_device(device_p) self.device_p = device_p # Fetch device descriptor device_descriptor = libusb1.libusb_device_descriptor() result = libusb1.libusb_get_device_descriptor( device_p, byref(device_descriptor)) mayRaiseUSBError(result) self.device_descriptor = device_descriptor if can_load_configuration: self.__configuration_descriptor_list = descriptor_list = [] append = descriptor_list.append device_p = self.device_p for configuration_id in xrange( self.device_descriptor.bNumConfigurations): config = libusb1.libusb_config_descriptor_p() result = libusb1.libusb_get_config_descriptor( device_p, configuration_id, byref(config)) # pylint: disable=undefined-variable if result == ERROR_NOT_FOUND: # pylint: enable=undefined-variable # Some devices (ex windows' root hubs) tell they have # one configuration, but they have no configuration # descriptor. continue mayRaiseUSBError(result) append(config.contents) def __del__(self): self.close() def close(self): pop = self.__close_set.pop while True: try: closable = pop() except self.__KeyError: break closable.close() if not self.device_p: return self.__libusb_unref_device(self.device_p) # pylint: disable=redefined-outer-name byref = self.__byref # pylint: enable=redefined-outer-name descriptor_list = self.__configuration_descriptor_list while descriptor_list: self.__libusb_free_config_descriptor( byref(descriptor_list.pop()), ) self.device_p = None def __str__(self): return 'Bus %03i Device %03i: ID %04x:%04x' % ( self.getBusNumber(), self.getDeviceAddress(), self.getVendorID(), self.getProductID(), ) def __len__(self): return len(self.__configuration_descriptor_list) def __getitem__(self, index): return USBConfiguration( self.__context, self.__configuration_descriptor_list[index]) def __key(self): return ( id(self.__context), self.getBusNumber(), self.getDeviceAddress(), self.getVendorID(), self.getProductID(), ) def __hash__(self): return hash(self.__key()) def __eq__(self, other): # pylint: disable=unidiomatic-typecheck return type(self) == type(other) and ( # pylint: enable=unidiomatic-typecheck self.device_p == other.device_p or # pylint: disable=protected-access self.__key() == other.__key() # pylint: enable=protected-access ) def iterConfigurations(self): context = self.__context for config in self.__configuration_descriptor_list: yield USBConfiguration(context, config) # BBB iterConfiguations = iterConfigurations def iterSettings(self): for config in self.iterConfigurations(): for interface in config: for setting in interface: yield setting def getBusNumber(self): """ Get device's bus number. """ return libusb1.libusb_get_bus_number(self.device_p) def getPortNumber(self): """ Get device's port number. """ return libusb1.libusb_get_port_number(self.device_p) def getPortNumberList(self): """ Get the port number of each hub toward device. """ port_list = (c_uint8 * PATH_MAX_DEPTH)() result = libusb1.libusb_get_port_numbers( self.device_p, port_list, len(port_list)) mayRaiseUSBError(result) return list(port_list[:result]) # TODO: wrap libusb_get_parent when/if libusb removes the need to be inside # a libusb_(get|free)_device_list block. def getDeviceAddress(self): """ Get device's address on its bus. """ return libusb1.libusb_get_device_address(self.device_p) def getbcdUSB(self): """ Get the USB spec version device complies to, in BCD format. """ return self.device_descriptor.bcdUSB def getDeviceClass(self): """ Get device's class id. """ return self.device_descriptor.bDeviceClass def getDeviceSubClass(self): """ Get device's subclass id. """ return self.device_descriptor.bDeviceSubClass def getDeviceProtocol(self): """ Get device's protocol id. """ return self.device_descriptor.bDeviceProtocol def getMaxPacketSize0(self): """ Get device's max packet size for endpoint 0 (control). """ return self.device_descriptor.bMaxPacketSize0 def getMaxPacketSize(self, endpoint): """ Get device's max packet size for given endpoint. Warning: this function will not always give you the expected result. See https://libusb.org/ticket/77 . You should instead consult the endpoint descriptor of current configuration and alternate setting. """ result = libusb1.libusb_get_max_packet_size(self.device_p, endpoint) mayRaiseUSBError(result) return result def getMaxISOPacketSize(self, endpoint): """ Get the maximum size for a single isochronous packet for given endpoint. Warning: this function will not always give you the expected result. See https://libusb.org/ticket/77 . You should instead consult the endpoint descriptor of current configuration and alternate setting. """ result = libusb1.libusb_get_max_iso_packet_size(self.device_p, endpoint) mayRaiseUSBError(result) return result def getVendorID(self): """ Get device's vendor id. """ return self.device_descriptor.idVendor def getProductID(self): """ Get device's product id. """ return self.device_descriptor.idProduct def getbcdDevice(self): """ Get device's release number. """ return self.device_descriptor.bcdDevice def getSupportedLanguageList(self): """ Get the list of language ids device has string descriptors for. Note: opens the device temporarily and uses synchronous API. """ return self.open().getSupportedLanguageList() def getManufacturer(self): """ Get device's manufaturer name. Shortcut for .open().getManufacturer() . """ return self.open().getManufacturer() def getManufacturerDescriptor(self): """ Get the string index of device's manufacturer. You can pass this value to USBHandle.getASCIIStringDescriptor to get the actual manufacturer string. """ return self.device_descriptor.iManufacturer def getProduct(self): """ Get device's product name. Shortcut for .open().getProduct() . """ return self.open().getProduct() def getProductDescriptor(self): """ Get the string index of device's product name. You can pass this value to USBHandle.getASCIIStringDescriptor to get the actual product name string. """ return self.device_descriptor.iProduct def getSerialNumber(self): """ Get device's serial number. Shortcut for .open().getSerialNumber() . """ return self.open().getSerialNumber() def getSerialNumberDescriptor(self): """ Get the string index of device's serial number. You can pass this value to USBHandle.getASCIIStringDescriptor to get the actual serial number string. """ return self.device_descriptor.iSerialNumber def getNumConfigurations(self): """ Get device's number of possible configurations. """ return self.device_descriptor.bNumConfigurations def getDeviceSpeed(self): """ Get device's speed. Returns one of: SPEED_UNKNOWN SPEED_LOW SPEED_FULL SPEED_HIGH SPEED_SUPER """ return libusb1.libusb_get_device_speed(self.device_p) def open(self): """ Open device. Returns an USBDeviceHandle instance. """ handle = libusb1.libusb_device_handle_p() mayRaiseUSBError(libusb1.libusb_open(self.device_p, byref(handle))) result = USBDeviceHandle(self.__context, handle, self) self.__close_set.add(result) return result _zero_tv = libusb1.timeval(0, 0) _zero_tv_p = byref(_zero_tv) class USBContext(object): """ libusb1 USB context. Provides methods to enumerate & look up USB devices. Also provides access to global (device-independent) libusb1 functions. """ __libusb_exit = libusb1.libusb_exit __context_p = None __added_cb = None __removed_cb = None __poll_cb_user_data = None __libusb_set_pollfd_notifiers = libusb1.libusb_set_pollfd_notifiers __null_pointer = c_void_p() __KeyError = KeyError __cast = staticmethod(cast) __auto_open = True # pylint: disable=no-self-argument,protected-access def _validContext(func): # Defined inside USBContext so we can access "self.__*". @contextlib.contextmanager def refcount(self): with self.__context_cond: if not self.__context_p and self.__auto_open: # BBB warnings.warn( 'Use "with USBContext() as context:" for safer cleanup' ' on interpreter shutdown. See also USBContext.open().', DeprecationWarning, ) self.open() self.__context_refcount += 1 try: yield finally: with self.__context_cond: self.__context_refcount -= 1 if not self.__context_refcount: self.__context_cond.notifyAll() if inspect.isgeneratorfunction(func): def wrapper(self, *args, **kw): with refcount(self): if self.__context_p: # pylint: disable=not-callable for value in func(self, *args, **kw): # pylint: enable=not-callable yield value else: def wrapper(self, *args, **kw): with refcount(self): if self.__context_p: # pylint: disable=not-callable return func(self, *args, **kw) # pylint: enable=not-callable functools.update_wrapper(wrapper, func) return wrapper # pylint: enable=no-self-argument,protected-access def __init__(self): """ Create a new USB context. """ # Used to prevent an exit to cause a segfault if a concurrent thread # is still in libusb. self.__context_refcount = 0 self.__context_cond = threading.Condition() self.__context_p = libusb1.libusb_context_p() self.__hotplug_callback_dict = {} self.__close_set = WeakSet() def __del__(self): # Avoid locking. # XXX: Assumes __del__ should not normally be called while any # instance's method is being executed. It seems unlikely (they hold a # reference to their instance). self._exit() def __enter__(self): return self.open() def __exit__(self, exc_type, exc_val, exc_tb): self.close() def open(self): """ Finish context initialisation, as is normally done in __enter__ . This happens automatically on the first method call needing access to the uninitialised properties, but with a warning. Call this method ONLY if your usage pattern prevents you from using the with USBContext() as contewt: form: this means there are ways to avoid calling close(), which can cause issues particularly hard to debug (ex: interpreter hangs on exit). """ assert self.__context_refcount == 0 mayRaiseUSBError(libusb1.libusb_init(byref(self.__context_p))) return self def close(self): """ Close (destroy) this USB context, and all related instances. When this method has been called, methods on its instance will become mosty no-ops, returning None until explicitly re-opened (by calling open() or __enter__()). Note: "exit" is a deprecated alias of "close". """ self.__auto_open = False self.__context_cond.acquire() try: while self.__context_refcount and self.__context_p: self.__context_cond.wait() self._exit() finally: self.__context_cond.notifyAll() self.__context_cond.release() def _exit(self): context_p = self.__context_p if context_p: for handle in self.__hotplug_callback_dict.keys(): self.hotplugDeregisterCallback(handle) pop = self.__close_set.pop while True: try: closable = pop() except self.__KeyError: break closable.close() self.__libusb_exit(context_p) self.__context_p = libusb1.libusb_context_p() self.__added_cb = self.__null_pointer self.__removed_cb = self.__null_pointer # BBB exit = close @_validContext def getDeviceIterator(self, skip_on_error=False): """ Return an iterator over all USB devices currently plugged in, as USBDevice instances. skip_on_error (bool) If True, ignore devices which raise USBError. """ device_p_p = libusb1.libusb_device_p_p() libusb_device_p = libusb1.libusb_device_p device_list_len = libusb1.libusb_get_device_list(self.__context_p, byref(device_p_p)) mayRaiseUSBError(device_list_len) try: for device_p in device_p_p[:device_list_len]: try: # Instanciate our own libusb_device_p object so we can free # libusb-provided device list. Is this a bug in ctypes that # it doesn't copy pointer value (=pointed memory address) ? # At least, it's not so convenient and forces using such # weird code. device = USBDevice(self, libusb_device_p(device_p.contents)) except USBError: if not skip_on_error: raise else: self.__close_set.add(device) yield device finally: libusb1.libusb_free_device_list(device_p_p, 1) def getDeviceList(self, skip_on_access_error=False, skip_on_error=False): """ Return a list of all USB devices currently plugged in, as USBDevice instances. skip_on_error (bool) If True, ignore devices which raise USBError. skip_on_access_error (bool) DEPRECATED. Alias for skip_on_error. """ return list( self.getDeviceIterator( skip_on_error=skip_on_access_error or skip_on_error, ), ) def getByVendorIDAndProductID( self, vendor_id, product_id, skip_on_access_error=False, skip_on_error=False): """ Get the first USB device matching given vendor and product ids. Returns an USBDevice instance, or None if no present device match. skip_on_error (bool) (see getDeviceList) skip_on_access_error (bool) (see getDeviceList) """ for device in self.getDeviceIterator( skip_on_error=skip_on_access_error or skip_on_error, ): if device.getVendorID() == vendor_id and \ device.getProductID() == product_id: return device def openByVendorIDAndProductID( self, vendor_id, product_id, skip_on_access_error=False, skip_on_error=False): """ Get the first USB device matching given vendor and product ids. Returns an USBDeviceHandle instance, or None if no present device match. skip_on_error (bool) (see getDeviceList) skip_on_access_error (bool) (see getDeviceList) """ result = self.getByVendorIDAndProductID( vendor_id, product_id, skip_on_access_error=skip_on_access_error, skip_on_error=skip_on_error) if result is not None: return result.open() @_validContext def getPollFDList(self): """ Return file descriptors to be used to poll USB events. You should not have to call this method, unless you are integrating this class with a polling mechanism. """ pollfd_p_p = libusb1.libusb_get_pollfds(self.__context_p) if not pollfd_p_p: errno = get_errno() if errno: raise OSError(errno) else: # Assume not implemented raise NotImplementedError( 'Your libusb does not seem to implement pollable FDs') try: result = [] append = result.append fd_index = 0 while pollfd_p_p[fd_index]: append(( pollfd_p_p[fd_index].contents.fd, pollfd_p_p[fd_index].contents.events, )) fd_index += 1 finally: _free(pollfd_p_p) return result @_validContext def handleEvents(self): """ Handle any pending event (blocking). See libusb1 documentation for details (there is a timeout, so it's not "really" blocking). """ mayRaiseUSBError( libusb1.libusb_handle_events(self.__context_p), ) # TODO: handleEventsCompleted @_validContext def handleEventsTimeout(self, tv=0): """ Handle any pending event. If tv is 0, will return immediately after handling already-pending events. Otherwise, defines the maximum amount of time to wait for events, in seconds. """ if tv is None: tv = 0 tv_s = int(tv) real_tv = libusb1.timeval(tv_s, int((tv - tv_s) * 1000000)) mayRaiseUSBError( libusb1.libusb_handle_events_timeout( self.__context_p, byref(real_tv), ), ) # TODO: handleEventsTimeoutCompleted @_validContext def setPollFDNotifiers( self, added_cb=None, removed_cb=None, user_data=None): """ Give libusb1 methods to call when it should add/remove file descriptor for polling. You should not have to call this method, unless you are integrating this class with a polling mechanism. """ if added_cb is None: added_cb = self.__null_pointer else: added_cb = libusb1.libusb_pollfd_added_cb_p(added_cb) if removed_cb is None: removed_cb = self.__null_pointer else: removed_cb = libusb1.libusb_pollfd_removed_cb_p(removed_cb) if user_data is None: user_data = self.__null_pointer self.__added_cb = added_cb self.__removed_cb = removed_cb self.__poll_cb_user_data = user_data self.__libusb_set_pollfd_notifiers( self.__context_p, self.__cast(added_cb, libusb1.libusb_pollfd_added_cb_p), self.__cast(removed_cb, libusb1.libusb_pollfd_removed_cb_p), user_data, ) @_validContext def getNextTimeout(self): """ Returns the next internal timeout that libusb needs to handle, in seconds, or None if no timeout is needed. You should not have to call this method, unless you are integrating this class with a polling mechanism. """ timeval = libusb1.timeval() result = libusb1.libusb_get_next_timeout( self.__context_p, byref(timeval)) if result == 0: return None elif result == 1: return timeval.tv_sec + (timeval.tv_usec * 0.000001) raiseUSBError(result) @_validContext def setDebug(self, level): """ Set debugging level. See LOG_LEVEL_* constants. Note: depending on libusb compilation settings, this might have no effect. """ libusb1.libusb_set_debug(self.__context_p, level) @_validContext def tryLockEvents(self): """ See libusb_try_lock_events doc. """ return libusb1.libusb_try_lock_events(self.__context_p) @_validContext def lockEvents(self): """ See libusb_lock_events doc. """ libusb1.libusb_lock_events(self.__context_p) @_validContext def lockEventWaiters(self): """ See libusb_lock_event_waiters doc. """ libusb1.libusb_lock_event_waiters(self.__context_p) @_validContext def waitForEvent(self, tv=0): """ See libusb_wait_for_event doc. """ if tv is None: tv = 0 tv_s = int(tv) real_tv = libusb1.timeval(tv_s, int((tv - tv_s) * 1000000)) libusb1.libusb_wait_for_event(self.__context_p, byref(real_tv)) @_validContext def unlockEventWaiters(self): """ See libusb_unlock_event_waiters doc. """ libusb1.libusb_unlock_event_waiters(self.__context_p) @_validContext def eventHandlingOK(self): """ See libusb_event_handling_ok doc. """ return libusb1.libusb_event_handling_ok(self.__context_p) @_validContext def unlockEvents(self): """ See libusb_unlock_events doc. """ libusb1.libusb_unlock_events(self.__context_p) @_validContext def handleEventsLocked(self): """ See libusb_handle_events_locked doc. """ # XXX: does tv parameter need to be exposed ? mayRaiseUSBError(libusb1.libusb_handle_events_locked( self.__context_p, _zero_tv_p, )) @_validContext def eventHandlerActive(self): """ See libusb_event_handler_active doc. """ return libusb1.libusb_event_handler_active(self.__context_p) @staticmethod def hasCapability(capability): """ Backward-compatibility alias for module-level hasCapability. """ return hasCapability(capability) @_validContext def hotplugRegisterCallback( self, callback, # pylint: disable=undefined-variable events=HOTPLUG_EVENT_DEVICE_ARRIVED | HOTPLUG_EVENT_DEVICE_LEFT, flags=HOTPLUG_ENUMERATE, vendor_id=HOTPLUG_MATCH_ANY, product_id=HOTPLUG_MATCH_ANY, dev_class=HOTPLUG_MATCH_ANY, # pylint: enable=undefined-variable ): """ Registers an hotplug callback. On success, returns an opaque value which can be passed to hotplugDeregisterCallback. Callback must accept the following positional arguments: - this USBContext instance - an USBDevice instance If device has left, configuration descriptors may not be available. Its device descriptor will be available. - event type, one of: HOTPLUG_EVENT_DEVICE_ARRIVED HOTPLUG_EVENT_DEVICE_LEFT Callback must return whether it must be unregistered (any true value to be unregistered, any false value to be kept registered). Note: given callback will be invoked during event handling, meaning it cannot call any synchronous libusb function. """ def wrapped_callback(context_p, device_p, event, _): assert addressof(context_p.contents) == addressof( self.__context_p.contents), (context_p, self.__context_p) device = USBDevice( self, device_p, # pylint: disable=undefined-variable event != HOTPLUG_EVENT_DEVICE_LEFT, # pylint: enable=undefined-variable ) self.__close_set.add(device) unregister = bool(callback( self, device, event, )) if unregister: del self.__hotplug_callback_dict[handle] return unregister handle = c_int() callback_p = libusb1.libusb_hotplug_callback_fn_p(wrapped_callback) mayRaiseUSBError(libusb1.libusb_hotplug_register_callback( self.__context_p, events, flags, vendor_id, product_id, dev_class, callback_p, None, byref(handle), )) handle = handle.value # Keep strong references assert handle not in self.__hotplug_callback_dict, ( handle, self.__hotplug_callback_dict, ) self.__hotplug_callback_dict[handle] = (callback_p, wrapped_callback) return handle @_validContext def hotplugDeregisterCallback(self, handle): """ Deregisters an hotplug callback. handle (opaque) Return value of a former hotplugRegisterCallback call. """ del self.__hotplug_callback_dict[handle] libusb1.libusb_hotplug_deregister_callback(self.__context_p, handle) del USBContext._validContext def getVersion(): """ Returns underlying libusb's version information as a 6-namedtuple (or 6-tuple if namedtuples are not avaiable): - major - minor - micro - nano - rc - describe Returns (0, 0, 0, 0, '', '') if libusb doesn't have required entry point. """ version = libusb1.libusb_get_version().contents return Version( version.major, version.minor, version.micro, version.nano, version.rc, version.describe, ) def hasCapability(capability): """ Tests feature presence. capability should be one of: CAP_HAS_CAPABILITY CAP_HAS_HOTPLUG CAP_HAS_HID_ACCESS CAP_SUPPORTS_DETACH_KERNEL_DRIVER """ return libusb1.libusb_has_capability(capability) class LibUSBContext(USBContext): """ Backward-compatibility alias for USBContext. """ def __init__(self): warnings.warn( 'LibUSBContext is being renamed to USBContext', DeprecationWarning, ) super(LibUSBContext, self).__init__()