972 lines
35 KiB
Python
972 lines
35 KiB
Python
|
# Copyright 2009-2017 Wander Lairson Costa
|
||
|
# Copyright 2009-2021 PyUSB contributors
|
||
|
#
|
||
|
# Redistribution and use in source and binary forms, with or without
|
||
|
# modification, are permitted provided that the following conditions are
|
||
|
# met:
|
||
|
#
|
||
|
# 1. Redistributions of source code must retain the above copyright
|
||
|
# notice, this list of conditions and the following disclaimer.
|
||
|
#
|
||
|
# 2. Redistributions in binary form must reproduce the above copyright
|
||
|
# notice, this list of conditions and the following disclaimer in the
|
||
|
# documentation and/or other materials provided with the distribution.
|
||
|
#
|
||
|
# 3. Neither the name of the copyright holder nor the names of its
|
||
|
# contributors may be used to endorse or promote products derived from
|
||
|
# this software without specific prior written permission.
|
||
|
#
|
||
|
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||
|
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||
|
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||
|
# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||
|
# HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||
|
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||
|
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||
|
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||
|
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||
|
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||
|
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||
|
|
||
|
from ctypes import *
|
||
|
import usb.util
|
||
|
import sys
|
||
|
import logging
|
||
|
from usb._debug import methodtrace
|
||
|
import usb._interop as _interop
|
||
|
import usb._objfinalizer as _objfinalizer
|
||
|
import errno
|
||
|
import math
|
||
|
from usb.core import USBError, USBTimeoutError
|
||
|
import usb.libloader
|
||
|
|
||
|
__author__ = 'Wander Lairson Costa'
|
||
|
|
||
|
__all__ = [
|
||
|
'get_backend',
|
||
|
'LIBUSB_SUCCESS',
|
||
|
'LIBUSB_ERROR_IO',
|
||
|
'LIBUSB_ERROR_INVALID_PARAM',
|
||
|
'LIBUSB_ERROR_ACCESS',
|
||
|
'LIBUSB_ERROR_NO_DEVICE',
|
||
|
'LIBUSB_ERROR_NOT_FOUND',
|
||
|
'LIBUSB_ERROR_BUSY',
|
||
|
'LIBUSB_ERROR_TIMEOUT',
|
||
|
'LIBUSB_ERROR_OVERFLOW',
|
||
|
'LIBUSB_ERROR_PIPE',
|
||
|
'LIBUSB_ERROR_INTERRUPTED',
|
||
|
'LIBUSB_ERROR_NO_MEM',
|
||
|
'LIBUSB_ERROR_NOT_SUPPORTED',
|
||
|
'LIBUSB_ERROR_OTHER',
|
||
|
'LIBUSB_TRANSFER_COMPLETED',
|
||
|
'LIBUSB_TRANSFER_ERROR',
|
||
|
'LIBUSB_TRANSFER_TIMED_OUT',
|
||
|
'LIBUSB_TRANSFER_CANCELLED',
|
||
|
'LIBUSB_TRANSFER_STALL',
|
||
|
'LIBUSB_TRANSFER_NO_DEVICE',
|
||
|
'LIBUSB_TRANSFER_OVERFLOW',
|
||
|
]
|
||
|
|
||
|
_logger = logging.getLogger('usb.backend.libusb1')
|
||
|
|
||
|
# libusb.h
|
||
|
|
||
|
# transfer_type codes
|
||
|
# 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
|
||
|
|
||
|
# return codes
|
||
|
|
||
|
LIBUSB_SUCCESS = 0
|
||
|
LIBUSB_ERROR_IO = -1
|
||
|
LIBUSB_ERROR_INVALID_PARAM = -2
|
||
|
LIBUSB_ERROR_ACCESS = -3
|
||
|
LIBUSB_ERROR_NO_DEVICE = -4
|
||
|
LIBUSB_ERROR_NOT_FOUND = -5
|
||
|
LIBUSB_ERROR_BUSY = -6
|
||
|
LIBUSB_ERROR_TIMEOUT = -7
|
||
|
LIBUSB_ERROR_OVERFLOW = -8
|
||
|
LIBUSB_ERROR_PIPE = -9
|
||
|
LIBUSB_ERROR_INTERRUPTED = -10
|
||
|
LIBUSB_ERROR_NO_MEM = -11
|
||
|
LIBUSB_ERROR_NOT_SUPPORTED = -12
|
||
|
LIBUSB_ERROR_OTHER = -99
|
||
|
|
||
|
# map return codes to strings
|
||
|
_str_error_map = {
|
||
|
LIBUSB_SUCCESS:'Success (no error)',
|
||
|
LIBUSB_ERROR_IO:'Input/output error',
|
||
|
LIBUSB_ERROR_INVALID_PARAM:'Invalid parameter',
|
||
|
LIBUSB_ERROR_ACCESS:'Access denied (insufficient permissions)',
|
||
|
LIBUSB_ERROR_NO_DEVICE:'No such device (it may have been disconnected)',
|
||
|
LIBUSB_ERROR_NOT_FOUND:'Entity not found',
|
||
|
LIBUSB_ERROR_BUSY:'Resource busy',
|
||
|
LIBUSB_ERROR_TIMEOUT:'Operation timed out',
|
||
|
LIBUSB_ERROR_OVERFLOW:'Overflow',
|
||
|
LIBUSB_ERROR_PIPE:'Pipe error',
|
||
|
LIBUSB_ERROR_INTERRUPTED:'System call interrupted (perhaps due to signal)',
|
||
|
LIBUSB_ERROR_NO_MEM:'Insufficient memory',
|
||
|
LIBUSB_ERROR_NOT_SUPPORTED:'Operation not supported or unimplemented on this platform',
|
||
|
LIBUSB_ERROR_OTHER:'Unknown error'
|
||
|
}
|
||
|
|
||
|
# map return code to errno values
|
||
|
_libusb_errno = {
|
||
|
0:None,
|
||
|
LIBUSB_ERROR_IO:errno.__dict__.get('EIO', None),
|
||
|
LIBUSB_ERROR_INVALID_PARAM:errno.__dict__.get('EINVAL', None),
|
||
|
LIBUSB_ERROR_ACCESS:errno.__dict__.get('EACCES', None),
|
||
|
LIBUSB_ERROR_NO_DEVICE:errno.__dict__.get('ENODEV', None),
|
||
|
LIBUSB_ERROR_NOT_FOUND:errno.__dict__.get('ENOENT', None),
|
||
|
LIBUSB_ERROR_BUSY:errno.__dict__.get('EBUSY', None),
|
||
|
LIBUSB_ERROR_TIMEOUT:errno.__dict__.get('ETIMEDOUT', None),
|
||
|
LIBUSB_ERROR_OVERFLOW:errno.__dict__.get('EOVERFLOW', None),
|
||
|
LIBUSB_ERROR_PIPE:errno.__dict__.get('EPIPE', None),
|
||
|
LIBUSB_ERROR_INTERRUPTED:errno.__dict__.get('EINTR', None),
|
||
|
LIBUSB_ERROR_NO_MEM:errno.__dict__.get('ENOMEM', None),
|
||
|
LIBUSB_ERROR_NOT_SUPPORTED:errno.__dict__.get('ENOSYS', None),
|
||
|
LIBUSB_ERROR_OTHER:None
|
||
|
}
|
||
|
|
||
|
# Transfer status codes:
|
||
|
# Note that this does not indicate
|
||
|
# that the entire amount of requested data was transferred.
|
||
|
LIBUSB_TRANSFER_COMPLETED = 0
|
||
|
LIBUSB_TRANSFER_ERROR = 1
|
||
|
LIBUSB_TRANSFER_TIMED_OUT = 2
|
||
|
LIBUSB_TRANSFER_CANCELLED = 3
|
||
|
LIBUSB_TRANSFER_STALL = 4
|
||
|
LIBUSB_TRANSFER_NO_DEVICE = 5
|
||
|
LIBUSB_TRANSFER_OVERFLOW = 6
|
||
|
|
||
|
# map return codes to strings
|
||
|
_str_transfer_error = {
|
||
|
LIBUSB_TRANSFER_COMPLETED:'Success (no error)',
|
||
|
LIBUSB_TRANSFER_ERROR:'Transfer failed',
|
||
|
LIBUSB_TRANSFER_TIMED_OUT:'Transfer timed out',
|
||
|
LIBUSB_TRANSFER_CANCELLED:'Transfer was cancelled',
|
||
|
LIBUSB_TRANSFER_STALL:'For bulk/interrupt endpoints: halt condition '\
|
||
|
'detected (endpoint stalled). For control '\
|
||
|
'endpoints: control request not supported.',
|
||
|
LIBUSB_TRANSFER_NO_DEVICE:'Device was disconnected',
|
||
|
LIBUSB_TRANSFER_OVERFLOW:'Device sent more data than requested'
|
||
|
}
|
||
|
|
||
|
# map transfer codes to errno codes
|
||
|
_transfer_errno = {
|
||
|
LIBUSB_TRANSFER_COMPLETED:0,
|
||
|
LIBUSB_TRANSFER_ERROR:errno.__dict__.get('EIO', None),
|
||
|
LIBUSB_TRANSFER_TIMED_OUT:errno.__dict__.get('ETIMEDOUT', None),
|
||
|
LIBUSB_TRANSFER_CANCELLED:errno.__dict__.get('EAGAIN', None),
|
||
|
LIBUSB_TRANSFER_STALL:errno.__dict__.get('EIO', None),
|
||
|
LIBUSB_TRANSFER_NO_DEVICE:errno.__dict__.get('ENODEV', None),
|
||
|
LIBUSB_TRANSFER_OVERFLOW:errno.__dict__.get('EOVERFLOW', None)
|
||
|
}
|
||
|
|
||
|
def _strerror(errcode):
|
||
|
try:
|
||
|
return _lib.libusb_strerror(errcode).decode('utf8')
|
||
|
except AttributeError:
|
||
|
return _str_error_map[errcode]
|
||
|
|
||
|
# Data structures
|
||
|
|
||
|
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', POINTER(c_ubyte)),
|
||
|
('extra_length', c_int)]
|
||
|
|
||
|
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', POINTER(_libusb_endpoint_descriptor)),
|
||
|
('extra', POINTER(c_ubyte)),
|
||
|
('extra_length', c_int)]
|
||
|
|
||
|
class _libusb_interface(Structure):
|
||
|
_fields_ = [('altsetting', POINTER(_libusb_interface_descriptor)),
|
||
|
('num_altsetting', c_int)]
|
||
|
|
||
|
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),
|
||
|
('bMaxPower', c_uint8),
|
||
|
('interface', POINTER(_libusb_interface)),
|
||
|
('extra', POINTER(c_ubyte)),
|
||
|
('extra_length', c_int)]
|
||
|
|
||
|
class _libusb_device_descriptor(Structure):
|
||
|
_fields_ = [('bLength', c_uint8),
|
||
|
('bDescriptorType', c_uint8),
|
||
|
('bcdUSB', c_uint16),
|
||
|
('bDeviceClass', c_uint8),
|
||
|
('bDeviceSubClass', c_uint8),
|
||
|
('bDeviceProtocol', c_uint8),
|
||
|
('bMaxPacketSize0', c_uint8),
|
||
|
('idVendor', c_uint16),
|
||
|
('idProduct', c_uint16),
|
||
|
('bcdDevice', c_uint16),
|
||
|
('iManufacturer', c_uint8),
|
||
|
('iProduct', c_uint8),
|
||
|
('iSerialNumber', c_uint8),
|
||
|
('bNumConfigurations', c_uint8)]
|
||
|
|
||
|
|
||
|
# 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_device_handle = c_void_p
|
||
|
|
||
|
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),
|
||
|
('flags', c_uint8),
|
||
|
('endpoint', c_uint8),
|
||
|
('type', c_uint8),
|
||
|
('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', py_object),
|
||
|
('buffer', c_void_p),
|
||
|
('num_iso_packets', c_int),
|
||
|
('iso_packet_desc', _libusb_iso_packet_descriptor)
|
||
|
]
|
||
|
|
||
|
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))
|
||
|
|
||
|
_lib = None
|
||
|
_lib_object = None
|
||
|
|
||
|
def _load_library(find_library=None):
|
||
|
# Windows backend uses stdcall calling convention
|
||
|
#
|
||
|
# On FreeBSD 8/9, libusb 1.0 and libusb 0.1 are in the same shared
|
||
|
# object libusb.so, so if we found libusb library name, we must assure
|
||
|
# it is 1.0 version. We just try to get some symbol from 1.0 version
|
||
|
if sys.platform == 'win32':
|
||
|
win_cls = WinDLL
|
||
|
else:
|
||
|
win_cls = None
|
||
|
|
||
|
return usb.libloader.load_locate_library(
|
||
|
('usb-1.0', 'libusb-1.0', 'usb'),
|
||
|
'cygusb-1.0.dll', 'Libusb 1',
|
||
|
win_cls=win_cls,
|
||
|
find_library=find_library, check_symbols=('libusb_init',))
|
||
|
|
||
|
def _setup_prototypes(lib):
|
||
|
# void libusb_set_debug (libusb_context *ctx, int level)
|
||
|
lib.libusb_set_debug.argtypes = [c_void_p, c_int]
|
||
|
|
||
|
# int libusb_init (libusb_context **context)
|
||
|
lib.libusb_init.argtypes = [POINTER(c_void_p)]
|
||
|
|
||
|
# void libusb_exit (struct libusb_context *ctx)
|
||
|
lib.libusb_exit.argtypes = [c_void_p]
|
||
|
|
||
|
# ssize_t libusb_get_device_list (libusb_context *ctx,
|
||
|
# libusb_device ***list)
|
||
|
lib.libusb_get_device_list.argtypes = [
|
||
|
c_void_p,
|
||
|
POINTER(POINTER(c_void_p))
|
||
|
]
|
||
|
|
||
|
# libusb_device *libusb_get_parent (libusb_device *dev)
|
||
|
lib.libusb_get_parent.argtypes = [c_void_p]
|
||
|
lib.libusb_get_parent.restype = c_void_p
|
||
|
|
||
|
# void libusb_free_device_list (libusb_device **list,
|
||
|
# int unref_devices)
|
||
|
lib.libusb_free_device_list.argtypes = [
|
||
|
POINTER(c_void_p),
|
||
|
c_int
|
||
|
]
|
||
|
|
||
|
# libusb_device *libusb_ref_device (libusb_device *dev)
|
||
|
lib.libusb_ref_device.argtypes = [c_void_p]
|
||
|
lib.libusb_ref_device.restype = c_void_p
|
||
|
|
||
|
# void libusb_unref_device(libusb_device *dev)
|
||
|
lib.libusb_unref_device.argtypes = [c_void_p]
|
||
|
|
||
|
# int libusb_open(libusb_device *dev, libusb_device_handle **handle)
|
||
|
lib.libusb_open.argtypes = [c_void_p, POINTER(_libusb_device_handle)]
|
||
|
|
||
|
# void libusb_close(libusb_device_handle *dev_handle)
|
||
|
lib.libusb_close.argtypes = [_libusb_device_handle]
|
||
|
|
||
|
# int libusb_set_configuration(libusb_device_handle *dev,
|
||
|
# int configuration)
|
||
|
lib.libusb_set_configuration.argtypes = [_libusb_device_handle, c_int]
|
||
|
|
||
|
# int libusb_get_configuration(libusb_device_handle *dev,
|
||
|
# int *config)
|
||
|
lib.libusb_get_configuration.argtypes = [_libusb_device_handle, POINTER(c_int)]
|
||
|
|
||
|
# int libusb_claim_interface(libusb_device_handle *dev,
|
||
|
# int interface_number)
|
||
|
lib.libusb_claim_interface.argtypes = [_libusb_device_handle, c_int]
|
||
|
|
||
|
# int libusb_release_interface(libusb_device_handle *dev,
|
||
|
# int interface_number)
|
||
|
lib.libusb_release_interface.argtypes = [_libusb_device_handle, c_int]
|
||
|
|
||
|
# int libusb_set_interface_alt_setting(libusb_device_handle *dev,
|
||
|
# int interface_number,
|
||
|
# int alternate_setting)
|
||
|
lib.libusb_set_interface_alt_setting.argtypes = [
|
||
|
_libusb_device_handle,
|
||
|
c_int,
|
||
|
c_int
|
||
|
]
|
||
|
|
||
|
# int libusb_reset_device (libusb_device_handle *dev)
|
||
|
lib.libusb_reset_device.argtypes = [_libusb_device_handle]
|
||
|
|
||
|
# int libusb_kernel_driver_active(libusb_device_handle *dev,
|
||
|
# int interface)
|
||
|
lib.libusb_kernel_driver_active.argtypes = [
|
||
|
_libusb_device_handle,
|
||
|
c_int
|
||
|
]
|
||
|
|
||
|
# int libusb_detach_kernel_driver(libusb_device_handle *dev,
|
||
|
# int interface)
|
||
|
lib.libusb_detach_kernel_driver.argtypes = [
|
||
|
_libusb_device_handle,
|
||
|
c_int
|
||
|
]
|
||
|
|
||
|
# int libusb_attach_kernel_driver(libusb_device_handle *dev,
|
||
|
# int interface)
|
||
|
lib.libusb_attach_kernel_driver.argtypes = [
|
||
|
_libusb_device_handle,
|
||
|
c_int
|
||
|
]
|
||
|
|
||
|
# int libusb_get_device_descriptor(
|
||
|
# libusb_device *dev,
|
||
|
# struct libusb_device_descriptor *desc
|
||
|
# )
|
||
|
lib.libusb_get_device_descriptor.argtypes = [
|
||
|
c_void_p,
|
||
|
POINTER(_libusb_device_descriptor)
|
||
|
]
|
||
|
|
||
|
# int libusb_get_config_descriptor(
|
||
|
# libusb_device *dev,
|
||
|
# uint8_t config_index,
|
||
|
# struct libusb_config_descriptor **config
|
||
|
# )
|
||
|
lib.libusb_get_config_descriptor.argtypes = [
|
||
|
c_void_p,
|
||
|
c_uint8,
|
||
|
POINTER(POINTER(_libusb_config_descriptor))
|
||
|
]
|
||
|
|
||
|
# void libusb_free_config_descriptor(
|
||
|
# struct libusb_config_descriptor *config
|
||
|
# )
|
||
|
lib.libusb_free_config_descriptor.argtypes = [
|
||
|
POINTER(_libusb_config_descriptor)
|
||
|
]
|
||
|
|
||
|
# int libusb_get_string_descriptor_ascii(libusb_device_handle *dev,
|
||
|
# uint8_t desc_index,
|
||
|
# unsigned char *data,
|
||
|
# int length)
|
||
|
lib.libusb_get_string_descriptor_ascii.argtypes = [
|
||
|
_libusb_device_handle,
|
||
|
c_uint8,
|
||
|
POINTER(c_ubyte),
|
||
|
c_int
|
||
|
]
|
||
|
|
||
|
# int libusb_control_transfer(libusb_device_handle *dev_handle,
|
||
|
# uint8_t bmRequestType,
|
||
|
# uint8_t bRequest,
|
||
|
# uint16_t wValue,
|
||
|
# uint16_t wIndex,
|
||
|
# unsigned char *data,
|
||
|
# uint16_t wLength,
|
||
|
# unsigned int timeout)
|
||
|
lib.libusb_control_transfer.argtypes = [
|
||
|
_libusb_device_handle,
|
||
|
c_uint8,
|
||
|
c_uint8,
|
||
|
c_uint16,
|
||
|
c_uint16,
|
||
|
POINTER(c_ubyte),
|
||
|
c_uint16,
|
||
|
c_uint
|
||
|
]
|
||
|
|
||
|
#int libusb_bulk_transfer(
|
||
|
# struct libusb_device_handle *dev_handle,
|
||
|
# unsigned char endpoint,
|
||
|
# unsigned char *data,
|
||
|
# int length,
|
||
|
# int *transferred,
|
||
|
# unsigned int timeout
|
||
|
# )
|
||
|
lib.libusb_bulk_transfer.argtypes = [
|
||
|
_libusb_device_handle,
|
||
|
c_ubyte,
|
||
|
POINTER(c_ubyte),
|
||
|
c_int,
|
||
|
POINTER(c_int),
|
||
|
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
|
||
|
# );
|
||
|
lib.libusb_interrupt_transfer.argtypes = [
|
||
|
_libusb_device_handle,
|
||
|
c_ubyte,
|
||
|
POINTER(c_ubyte),
|
||
|
c_int,
|
||
|
POINTER(c_int),
|
||
|
c_uint
|
||
|
]
|
||
|
|
||
|
# libusb_transfer* libusb_alloc_transfer(int iso_packets);
|
||
|
lib.libusb_alloc_transfer.argtypes = [c_int]
|
||
|
lib.libusb_alloc_transfer.restype = POINTER(_libusb_transfer)
|
||
|
|
||
|
# void libusb_free_transfer(struct libusb_transfer *transfer)
|
||
|
lib.libusb_free_transfer.argtypes = [POINTER(_libusb_transfer)]
|
||
|
|
||
|
# int libusb_submit_transfer(struct libusb_transfer *transfer);
|
||
|
lib.libusb_submit_transfer.argtypes = [POINTER(_libusb_transfer)]
|
||
|
|
||
|
if hasattr(lib, 'libusb_strerror'):
|
||
|
# const char *libusb_strerror(enum libusb_error errcode)
|
||
|
lib.libusb_strerror.argtypes = [c_uint]
|
||
|
lib.libusb_strerror.restype = c_char_p
|
||
|
|
||
|
# int libusb_clear_halt(libusb_device_handle *dev, unsigned char endpoint)
|
||
|
lib.libusb_clear_halt.argtypes = [_libusb_device_handle, c_ubyte]
|
||
|
|
||
|
# void libusb_set_iso_packet_lengths(
|
||
|
# libusb_transfer* transfer,
|
||
|
# unsigned int length
|
||
|
# );
|
||
|
def libusb_set_iso_packet_lengths(transfer_p, length):
|
||
|
r"""This function is inline in the libusb.h file, so we must implement
|
||
|
it.
|
||
|
|
||
|
lib.libusb_set_iso_packet_lengths.argtypes = [
|
||
|
POINTER(_libusb_transfer),
|
||
|
c_int
|
||
|
]
|
||
|
"""
|
||
|
transfer = transfer_p.contents
|
||
|
for iso_packet_desc in _get_iso_packet_list(transfer):
|
||
|
iso_packet_desc.length = length
|
||
|
lib.libusb_set_iso_packet_lengths = libusb_set_iso_packet_lengths
|
||
|
|
||
|
#int libusb_get_max_iso_packet_size(libusb_device* dev,
|
||
|
# unsigned char endpoint);
|
||
|
lib.libusb_get_max_iso_packet_size.argtypes = [c_void_p,
|
||
|
c_ubyte]
|
||
|
|
||
|
# void libusb_fill_iso_transfer(
|
||
|
# struct libusb_transfer* transfer,
|
||
|
# libusb_device_handle* dev_handle,
|
||
|
# unsigned char endpoint,
|
||
|
# unsigned char* buffer,
|
||
|
# int length,
|
||
|
# int num_iso_packets,
|
||
|
# libusb_transfer_cb_fn callback,
|
||
|
# void * user_data,
|
||
|
# unsigned int timeout
|
||
|
# );
|
||
|
def libusb_fill_iso_transfer(_libusb_transfer_p, dev_handle, endpoint, buffer, length,
|
||
|
num_iso_packets, callback, user_data, timeout):
|
||
|
r"""This function is inline in the libusb.h file, so we must implement
|
||
|
it.
|
||
|
|
||
|
lib.libusb_fill_iso_transfer.argtypes = [
|
||
|
_libusb_transfer,
|
||
|
_libusb_device_handle,
|
||
|
c_ubyte,
|
||
|
POINTER(c_ubyte),
|
||
|
c_int,
|
||
|
c_int,
|
||
|
_libusb_transfer_cb_fn_p,
|
||
|
c_void_p,
|
||
|
c_uint
|
||
|
]
|
||
|
"""
|
||
|
transfer = _libusb_transfer_p.contents
|
||
|
transfer.dev_handle = dev_handle
|
||
|
transfer.endpoint = endpoint
|
||
|
transfer.type = _LIBUSB_TRANSFER_TYPE_ISOCHRONOUS
|
||
|
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
|
||
|
lib.libusb_fill_iso_transfer = libusb_fill_iso_transfer
|
||
|
|
||
|
# uint8_t libusb_get_bus_number(libusb_device *dev)
|
||
|
lib.libusb_get_bus_number.argtypes = [c_void_p]
|
||
|
lib.libusb_get_bus_number.restype = c_uint8
|
||
|
|
||
|
# uint8_t libusb_get_device_address(libusb_device *dev)
|
||
|
lib.libusb_get_device_address.argtypes = [c_void_p]
|
||
|
lib.libusb_get_device_address.restype = c_uint8
|
||
|
|
||
|
try:
|
||
|
# uint8_t libusb_get_device_speed(libusb_device *dev)
|
||
|
lib.libusb_get_device_speed.argtypes = [c_void_p]
|
||
|
lib.libusb_get_device_speed.restype = c_uint8
|
||
|
except AttributeError:
|
||
|
pass
|
||
|
|
||
|
try:
|
||
|
# uint8_t libusb_get_port_number(libusb_device *dev)
|
||
|
lib.libusb_get_port_number.argtypes = [c_void_p]
|
||
|
lib.libusb_get_port_number.restype = c_uint8
|
||
|
except AttributeError:
|
||
|
pass
|
||
|
|
||
|
try:
|
||
|
# int libusb_get_port_numbers(libusb_device *dev,
|
||
|
# uint8_t* port_numbers,
|
||
|
# int port_numbers_len)
|
||
|
lib.libusb_get_port_numbers.argtypes = [
|
||
|
c_void_p,
|
||
|
POINTER(c_uint8),
|
||
|
c_int
|
||
|
]
|
||
|
lib.libusb_get_port_numbers.restype = c_int
|
||
|
except AttributeError:
|
||
|
pass
|
||
|
|
||
|
#int libusb_handle_events(libusb_context *ctx);
|
||
|
lib.libusb_handle_events.argtypes = [c_void_p]
|
||
|
|
||
|
# check a libusb function call
|
||
|
def _check(ret):
|
||
|
if hasattr(ret, 'value'):
|
||
|
ret = ret.value
|
||
|
|
||
|
if ret < 0:
|
||
|
if ret == LIBUSB_ERROR_NOT_SUPPORTED:
|
||
|
raise NotImplementedError(_strerror(ret))
|
||
|
elif ret == LIBUSB_ERROR_TIMEOUT:
|
||
|
raise USBTimeoutError(_strerror(ret), ret, _libusb_errno[ret])
|
||
|
else:
|
||
|
raise USBError(_strerror(ret), ret, _libusb_errno[ret])
|
||
|
|
||
|
return ret
|
||
|
|
||
|
# wrap a device
|
||
|
class _Device(_objfinalizer.AutoFinalizedObject):
|
||
|
def __init__(self, devid):
|
||
|
self.devid = _lib.libusb_ref_device(devid)
|
||
|
def _finalize_object(self):
|
||
|
_lib.libusb_unref_device(self.devid)
|
||
|
|
||
|
# wrap a descriptor and keep a reference to another object
|
||
|
# Thanks to Thomas Reitmayr.
|
||
|
class _WrapDescriptor(object):
|
||
|
def __init__(self, desc, obj = None):
|
||
|
self.obj = obj
|
||
|
self.desc = desc
|
||
|
def __getattr__(self, name):
|
||
|
return getattr(self.desc, name)
|
||
|
|
||
|
# wrap a configuration descriptor
|
||
|
class _ConfigDescriptor(_objfinalizer.AutoFinalizedObject):
|
||
|
def __init__(self, desc):
|
||
|
self.desc = desc
|
||
|
def _finalize_object(self):
|
||
|
_lib.libusb_free_config_descriptor(self.desc)
|
||
|
def __getattr__(self, name):
|
||
|
return getattr(self.desc.contents, name)
|
||
|
|
||
|
|
||
|
# iterator for libusb devices
|
||
|
class _DevIterator(_objfinalizer.AutoFinalizedObject):
|
||
|
def __init__(self, ctx):
|
||
|
self.dev_list = POINTER(c_void_p)()
|
||
|
self.num_devs = _check(_lib.libusb_get_device_list(
|
||
|
ctx,
|
||
|
byref(self.dev_list))
|
||
|
)
|
||
|
def __iter__(self):
|
||
|
for i in range(self.num_devs):
|
||
|
yield _Device(self.dev_list[i])
|
||
|
def _finalize_object(self):
|
||
|
_lib.libusb_free_device_list(self.dev_list, 1)
|
||
|
|
||
|
class _DeviceHandle(object):
|
||
|
def __init__(self, dev):
|
||
|
self.handle = _libusb_device_handle()
|
||
|
self.devid = dev.devid
|
||
|
_check(_lib.libusb_open(self.devid, byref(self.handle)))
|
||
|
|
||
|
class _IsoTransferHandler(_objfinalizer.AutoFinalizedObject):
|
||
|
def __init__(self, dev_handle, ep, buff, timeout):
|
||
|
address, length = buff.buffer_info()
|
||
|
|
||
|
packet_length = _lib.libusb_get_max_iso_packet_size(dev_handle.devid, ep)
|
||
|
packet_count = int(math.ceil(float(length) / packet_length))
|
||
|
|
||
|
self.transfer = _lib.libusb_alloc_transfer(packet_count)
|
||
|
|
||
|
_lib.libusb_fill_iso_transfer(self.transfer,
|
||
|
dev_handle.handle,
|
||
|
ep,
|
||
|
cast(address, POINTER(c_ubyte)),
|
||
|
length,
|
||
|
packet_count,
|
||
|
_libusb_transfer_cb_fn_p(self.__callback),
|
||
|
None,
|
||
|
timeout)
|
||
|
|
||
|
self.__set_packets_length(length, packet_length)
|
||
|
|
||
|
def _finalize_object(self):
|
||
|
_lib.libusb_free_transfer(self.transfer)
|
||
|
|
||
|
def submit(self, ctx = None):
|
||
|
self.__callback_done = 0
|
||
|
_check(_lib.libusb_submit_transfer(self.transfer))
|
||
|
|
||
|
while not self.__callback_done:
|
||
|
_check(_lib.libusb_handle_events(ctx))
|
||
|
|
||
|
status = int(self.transfer.contents.status)
|
||
|
if status != LIBUSB_TRANSFER_COMPLETED:
|
||
|
raise usb.USBError(_str_transfer_error[status],
|
||
|
status,
|
||
|
_transfer_errno[status])
|
||
|
|
||
|
return self.__compute_size_transf_data()
|
||
|
|
||
|
def __compute_size_transf_data(self):
|
||
|
return sum([t.actual_length for t in
|
||
|
_get_iso_packet_list(self.transfer.contents)])
|
||
|
|
||
|
def __set_packets_length(self, n, packet_length):
|
||
|
_lib.libusb_set_iso_packet_lengths(self.transfer, packet_length)
|
||
|
r = n % packet_length
|
||
|
if r:
|
||
|
iso_packets = _get_iso_packet_list(self.transfer.contents)
|
||
|
# When the device is disconnected, this list may
|
||
|
# return with length 0
|
||
|
if len(iso_packets):
|
||
|
iso_packets[-1].length = r
|
||
|
|
||
|
def __callback(self, transfer):
|
||
|
self.__callback_done = 1
|
||
|
|
||
|
# implementation of libusb 1.0 backend
|
||
|
class _LibUSB(usb.backend.IBackend):
|
||
|
@methodtrace(_logger)
|
||
|
def __init__(self, lib):
|
||
|
usb.backend.IBackend.__init__(self)
|
||
|
self.lib = lib
|
||
|
self.ctx = c_void_p()
|
||
|
_check(self.lib.libusb_init(byref(self.ctx)))
|
||
|
|
||
|
@methodtrace(_logger)
|
||
|
def _finalize_object(self):
|
||
|
if self.ctx:
|
||
|
self.lib.libusb_exit(self.ctx)
|
||
|
|
||
|
|
||
|
@methodtrace(_logger)
|
||
|
def enumerate_devices(self):
|
||
|
return _DevIterator(self.ctx)
|
||
|
|
||
|
@methodtrace(_logger)
|
||
|
def get_parent(self, dev):
|
||
|
_parent = self.lib.libusb_get_parent(dev.devid)
|
||
|
if _parent is None:
|
||
|
return None
|
||
|
else:
|
||
|
return _Device(_parent)
|
||
|
|
||
|
@methodtrace(_logger)
|
||
|
def get_device_descriptor(self, dev):
|
||
|
dev_desc = _libusb_device_descriptor()
|
||
|
_check(self.lib.libusb_get_device_descriptor(dev.devid, byref(dev_desc)))
|
||
|
dev_desc.bus = self.lib.libusb_get_bus_number(dev.devid)
|
||
|
dev_desc.address = self.lib.libusb_get_device_address(dev.devid)
|
||
|
|
||
|
# Only available in newer versions of libusb
|
||
|
try:
|
||
|
dev_desc.speed = self.lib.libusb_get_device_speed(dev.devid)
|
||
|
except AttributeError:
|
||
|
dev_desc.speed = None
|
||
|
|
||
|
# Only available in newer versions of libusb
|
||
|
try:
|
||
|
dev_desc.port_number = self.lib.libusb_get_port_number(dev.devid)
|
||
|
except AttributeError:
|
||
|
dev_desc.port_number = None
|
||
|
|
||
|
# Only available in newer versions of libusb
|
||
|
try:
|
||
|
buff = (c_uint8 * 7)() # USB 3.0 maximum depth is 7
|
||
|
written = dev_desc.port_numbers = self.lib.libusb_get_port_numbers(
|
||
|
dev.devid, buff, len(buff))
|
||
|
if written > 0:
|
||
|
dev_desc.port_numbers = tuple(buff[:written])
|
||
|
else:
|
||
|
dev_desc.port_numbers = None
|
||
|
except AttributeError:
|
||
|
dev_desc.port_numbers = None
|
||
|
|
||
|
return dev_desc
|
||
|
|
||
|
@methodtrace(_logger)
|
||
|
def get_configuration_descriptor(self, dev, config):
|
||
|
cfg = POINTER(_libusb_config_descriptor)()
|
||
|
_check(self.lib.libusb_get_config_descriptor(
|
||
|
dev.devid,
|
||
|
config, byref(cfg)))
|
||
|
config_desc = _ConfigDescriptor(cfg)
|
||
|
config_desc.extra_descriptors = (
|
||
|
config_desc.extra[:config_desc.extra_length])
|
||
|
return config_desc
|
||
|
|
||
|
@methodtrace(_logger)
|
||
|
def get_interface_descriptor(self, dev, intf, alt, config):
|
||
|
cfg = self.get_configuration_descriptor(dev, config)
|
||
|
if intf >= cfg.bNumInterfaces:
|
||
|
raise IndexError('Invalid interface index ' + str(intf))
|
||
|
i = cfg.interface[intf]
|
||
|
if alt >= i.num_altsetting:
|
||
|
raise IndexError('Invalid alternate setting index ' + str(alt))
|
||
|
intf_desc = i.altsetting[alt]
|
||
|
intf_desc.extra_descriptors = intf_desc.extra[:intf_desc.extra_length]
|
||
|
return _WrapDescriptor(intf_desc, cfg)
|
||
|
|
||
|
@methodtrace(_logger)
|
||
|
def get_endpoint_descriptor(self, dev, ep, intf, alt, config):
|
||
|
i = self.get_interface_descriptor(dev, intf, alt, config)
|
||
|
if ep > i.bNumEndpoints:
|
||
|
raise IndexError('Invalid endpoint index ' + str(ep))
|
||
|
ep_desc = i.endpoint[ep]
|
||
|
ep_desc.extra_descriptors = ep_desc.extra[:ep_desc.extra_length]
|
||
|
return _WrapDescriptor(ep_desc, i)
|
||
|
|
||
|
@methodtrace(_logger)
|
||
|
def open_device(self, dev):
|
||
|
return _DeviceHandle(dev)
|
||
|
|
||
|
@methodtrace(_logger)
|
||
|
def close_device(self, dev_handle):
|
||
|
self.lib.libusb_close(dev_handle.handle)
|
||
|
|
||
|
@methodtrace(_logger)
|
||
|
def set_configuration(self, dev_handle, config_value):
|
||
|
_check(self.lib.libusb_set_configuration(dev_handle.handle, config_value))
|
||
|
|
||
|
@methodtrace(_logger)
|
||
|
def get_configuration(self, dev_handle):
|
||
|
config = c_int()
|
||
|
_check(self.lib.libusb_get_configuration(dev_handle.handle, byref(config)))
|
||
|
return config.value
|
||
|
|
||
|
@methodtrace(_logger)
|
||
|
def set_interface_altsetting(self, dev_handle, intf, altsetting):
|
||
|
_check(self.lib.libusb_set_interface_alt_setting(
|
||
|
dev_handle.handle,
|
||
|
intf,
|
||
|
altsetting))
|
||
|
|
||
|
@methodtrace(_logger)
|
||
|
def claim_interface(self, dev_handle, intf):
|
||
|
_check(self.lib.libusb_claim_interface(dev_handle.handle, intf))
|
||
|
|
||
|
@methodtrace(_logger)
|
||
|
def release_interface(self, dev_handle, intf):
|
||
|
_check(self.lib.libusb_release_interface(dev_handle.handle, intf))
|
||
|
|
||
|
@methodtrace(_logger)
|
||
|
def bulk_write(self, dev_handle, ep, intf, data, timeout):
|
||
|
return self.__write(self.lib.libusb_bulk_transfer,
|
||
|
dev_handle,
|
||
|
ep,
|
||
|
intf,
|
||
|
data,
|
||
|
timeout)
|
||
|
|
||
|
@methodtrace(_logger)
|
||
|
def bulk_read(self, dev_handle, ep, intf, buff, timeout):
|
||
|
return self.__read(self.lib.libusb_bulk_transfer,
|
||
|
dev_handle,
|
||
|
ep,
|
||
|
intf,
|
||
|
buff,
|
||
|
timeout)
|
||
|
|
||
|
@methodtrace(_logger)
|
||
|
def intr_write(self, dev_handle, ep, intf, data, timeout):
|
||
|
return self.__write(self.lib.libusb_interrupt_transfer,
|
||
|
dev_handle,
|
||
|
ep,
|
||
|
intf,
|
||
|
data,
|
||
|
timeout)
|
||
|
|
||
|
@methodtrace(_logger)
|
||
|
def intr_read(self, dev_handle, ep, intf, buff, timeout):
|
||
|
return self.__read(self.lib.libusb_interrupt_transfer,
|
||
|
dev_handle,
|
||
|
ep,
|
||
|
intf,
|
||
|
buff,
|
||
|
timeout)
|
||
|
|
||
|
@methodtrace(_logger)
|
||
|
def iso_write(self, dev_handle, ep, intf, data, timeout):
|
||
|
handler = _IsoTransferHandler(dev_handle, ep, data, timeout)
|
||
|
return handler.submit(self.ctx)
|
||
|
|
||
|
@methodtrace(_logger)
|
||
|
def iso_read(self, dev_handle, ep, intf, buff, timeout):
|
||
|
handler = _IsoTransferHandler(dev_handle, ep, buff, timeout)
|
||
|
return handler.submit(self.ctx)
|
||
|
|
||
|
@methodtrace(_logger)
|
||
|
def ctrl_transfer(self,
|
||
|
dev_handle,
|
||
|
bmRequestType,
|
||
|
bRequest,
|
||
|
wValue,
|
||
|
wIndex,
|
||
|
data,
|
||
|
timeout):
|
||
|
addr, length = data.buffer_info()
|
||
|
length *= data.itemsize
|
||
|
|
||
|
ret = _check(self.lib.libusb_control_transfer(
|
||
|
dev_handle.handle,
|
||
|
bmRequestType,
|
||
|
bRequest,
|
||
|
wValue,
|
||
|
wIndex,
|
||
|
cast(addr, POINTER(c_ubyte)),
|
||
|
length,
|
||
|
timeout))
|
||
|
|
||
|
return ret
|
||
|
|
||
|
@methodtrace(_logger)
|
||
|
def clear_halt(self, dev_handle, ep):
|
||
|
_check(self.lib.libusb_clear_halt(dev_handle.handle, ep))
|
||
|
|
||
|
@methodtrace(_logger)
|
||
|
def reset_device(self, dev_handle):
|
||
|
_check(self.lib.libusb_reset_device(dev_handle.handle))
|
||
|
|
||
|
@methodtrace(_logger)
|
||
|
def is_kernel_driver_active(self, dev_handle, intf):
|
||
|
return bool(_check(self.lib.libusb_kernel_driver_active(dev_handle.handle,
|
||
|
intf)))
|
||
|
|
||
|
@methodtrace(_logger)
|
||
|
def detach_kernel_driver(self, dev_handle, intf):
|
||
|
_check(self.lib.libusb_detach_kernel_driver(dev_handle.handle, intf))
|
||
|
|
||
|
@methodtrace(_logger)
|
||
|
def attach_kernel_driver(self, dev_handle, intf):
|
||
|
_check(self.lib.libusb_attach_kernel_driver(dev_handle.handle, intf))
|
||
|
|
||
|
def __write(self, fn, dev_handle, ep, intf, data, timeout):
|
||
|
address, length = data.buffer_info()
|
||
|
length *= data.itemsize
|
||
|
transferred = c_int()
|
||
|
retval = fn(dev_handle.handle,
|
||
|
ep,
|
||
|
cast(address, POINTER(c_ubyte)),
|
||
|
length,
|
||
|
byref(transferred),
|
||
|
timeout)
|
||
|
# do not assume LIBUSB_ERROR_TIMEOUT means no I/O.
|
||
|
if not (transferred.value and retval == LIBUSB_ERROR_TIMEOUT):
|
||
|
_check(retval)
|
||
|
|
||
|
return transferred.value
|
||
|
|
||
|
def __read(self, fn, dev_handle, ep, intf, buff, timeout):
|
||
|
address, length = buff.buffer_info()
|
||
|
length *= buff.itemsize
|
||
|
transferred = c_int()
|
||
|
retval = fn(dev_handle.handle,
|
||
|
ep,
|
||
|
cast(address, POINTER(c_ubyte)),
|
||
|
length,
|
||
|
byref(transferred),
|
||
|
timeout)
|
||
|
# do not assume LIBUSB_ERROR_TIMEOUT means no I/O.
|
||
|
if not (transferred.value and retval == LIBUSB_ERROR_TIMEOUT):
|
||
|
_check(retval)
|
||
|
return transferred.value
|
||
|
|
||
|
def get_backend(find_library=None):
|
||
|
global _lib, _lib_object
|
||
|
try:
|
||
|
if _lib_object is None:
|
||
|
_lib = _load_library(find_library=find_library)
|
||
|
_setup_prototypes(_lib)
|
||
|
_lib_object = _LibUSB(_lib)
|
||
|
return _lib_object
|
||
|
except usb.libloader.LibraryException:
|
||
|
# exception already logged (if any)
|
||
|
_logger.error('Error loading libusb 1.0 backend', exc_info=False)
|
||
|
return None
|
||
|
except Exception:
|
||
|
_logger.error('Error loading libusb 1.0 backend', exc_info=True)
|
||
|
return None
|