quarchpy 2.2.8.dev1__py2.py3-none-any.whl → 2.2.9.dev1__py2.py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- quarchpy/__pycache__/__init__.cpython-311.pyc +0 -0
- quarchpy/__pycache__/__init__.cpython-313.pyc +0 -0
- quarchpy/__pycache__/_version.cpython-311.pyc +0 -0
- quarchpy/__pycache__/_version.cpython-313.pyc +0 -0
- quarchpy/__pycache__/connection.cpython-311.pyc +0 -0
- quarchpy/__pycache__/connection.cpython-313.pyc +0 -0
- quarchpy/_version.py +1 -1
- quarchpy/config_files/__pycache__/__init__.cpython-311.pyc +0 -0
- quarchpy/config_files/__pycache__/__init__.cpython-313.pyc +0 -0
- quarchpy/config_files/__pycache__/quarch_config_parser.cpython-311.pyc +0 -0
- quarchpy/config_files/__pycache__/quarch_config_parser.cpython-313.pyc +0 -0
- quarchpy/connection_specific/QPS/win-amd64/qis/3rdPartyLicenses/com.sun.istack-license.html +59 -0
- quarchpy/connection_specific/QPS/win-amd64/qis/3rdPartyLicenses/dorkbox-LICENSE.Apachev2 +218 -0
- quarchpy/connection_specific/QPS/win-amd64/qis/3rdPartyLicenses/jSerialComm-LICENSE-APACHE-2.0 +202 -0
- quarchpy/connection_specific/QPS/win-amd64/qis/3rdPartyLicenses/jSerialComm-LICENSE-LGPL-3.0 +165 -0
- quarchpy/connection_specific/QPS/win-amd64/qis/3rdPartyLicenses/jakarta.activation-license.html +59 -0
- quarchpy/connection_specific/QPS/win-amd64/qis/3rdPartyLicenses/jakarta.xml.bind-api-license.html +59 -0
- quarchpy/connection_specific/QPS/win-amd64/qis/3rdPartyLicenses/javassist-License.html +381 -0
- quarchpy/connection_specific/QPS/win-amd64/qis/3rdPartyLicenses/jmdns-LICENSE.txt +202 -0
- quarchpy/connection_specific/QPS/win-amd64/qis/3rdPartyLicenses/jna-AL2.0 +177 -0
- quarchpy/connection_specific/QPS/win-amd64/qis/3rdPartyLicenses/kotlin-stdlib-LICENSE-2.0.txt +202 -0
- quarchpy/connection_specific/QPS/win-amd64/qis/3rdPartyLicenses/netty-LICENSE.txt +202 -0
- quarchpy/connection_specific/QPS/win-amd64/qis/3rdPartyLicenses/netty-NOTICE.txt +239 -0
- quarchpy/connection_specific/QPS/win-amd64/qis/3rdPartyLicenses/slf4j-LICENSE.txt +24 -0
- quarchpy/connection_specific/QPS/win-amd64/qis/3rdPartyLicenses/usb4java-LICENSE.md +20 -0
- quarchpy/connection_specific/QPS/win-amd64/qis/help.txt +50 -17
- quarchpy/connection_specific/QPS/win-amd64/qis/license.txt +1 -0
- quarchpy/connection_specific/QPS/win-amd64/qis/qis.jar +0 -0
- quarchpy/connection_specific/QPS/win-amd64/qis/qis_lib/CInterface-2.5.jar +0 -0
- quarchpy/connection_specific/QPS/win-amd64/qis/qis_lib/QuarchCommon-2.0.jar +0 -0
- quarchpy/connection_specific/QPS/win-amd64/qis/qis_lib/TorridonCommon-1.3.jar +0 -0
- quarchpy/connection_specific/QPS/win-amd64/qis/qis_lib/usb4java-1.3.1.jar +0 -0
- quarchpy/connection_specific/__pycache__/StreamChannels.cpython-311.pyc +0 -0
- quarchpy/connection_specific/__pycache__/StreamChannels.cpython-313.pyc +0 -0
- quarchpy/connection_specific/__pycache__/__init__.cpython-311.pyc +0 -0
- quarchpy/connection_specific/__pycache__/__init__.cpython-313.pyc +0 -0
- quarchpy/connection_specific/__pycache__/connection_QIS.cpython-311.pyc +0 -0
- quarchpy/connection_specific/__pycache__/connection_QIS.cpython-313.pyc +0 -0
- quarchpy/connection_specific/__pycache__/connection_QPS.cpython-311.pyc +0 -0
- quarchpy/connection_specific/__pycache__/connection_QPS.cpython-313.pyc +0 -0
- quarchpy/connection_specific/__pycache__/connection_ReST.cpython-311.pyc +0 -0
- quarchpy/connection_specific/__pycache__/connection_ReST.cpython-313.pyc +0 -0
- quarchpy/connection_specific/__pycache__/connection_Serial.cpython-311.pyc +0 -0
- quarchpy/connection_specific/__pycache__/connection_Serial.cpython-313.pyc +0 -0
- quarchpy/connection_specific/__pycache__/connection_TCP.cpython-311.pyc +0 -0
- quarchpy/connection_specific/__pycache__/connection_TCP.cpython-313.pyc +0 -0
- quarchpy/connection_specific/__pycache__/connection_USB.cpython-311.pyc +0 -0
- quarchpy/connection_specific/__pycache__/connection_USB.cpython-313.pyc +0 -0
- quarchpy/connection_specific/__pycache__/mDNS.cpython-311.pyc +0 -0
- quarchpy/connection_specific/__pycache__/mDNS.cpython-313.pyc +0 -0
- quarchpy/connection_specific/jdk_j21_jres/__pycache__/__init__.cpython-311.pyc +0 -0
- quarchpy/connection_specific/jdk_j21_jres/__pycache__/__init__.cpython-313.pyc +0 -0
- quarchpy/connection_specific/jdk_j21_jres/__pycache__/fix_permissions.cpython-311.pyc +0 -0
- quarchpy/connection_specific/jdk_j21_jres/__pycache__/fix_permissions.cpython-313.pyc +0 -0
- quarchpy/connection_specific/serial/__pycache__/__init__.cpython-311.pyc +0 -0
- quarchpy/connection_specific/serial/__pycache__/__init__.cpython-313.pyc +0 -0
- quarchpy/connection_specific/serial/__pycache__/serialutil.cpython-311.pyc +0 -0
- quarchpy/connection_specific/serial/__pycache__/serialutil.cpython-313.pyc +0 -0
- quarchpy/connection_specific/serial/__pycache__/serialwin32.cpython-311.pyc +0 -0
- quarchpy/connection_specific/serial/__pycache__/serialwin32.cpython-313.pyc +0 -0
- quarchpy/connection_specific/serial/__pycache__/win32.cpython-311.pyc +0 -0
- quarchpy/connection_specific/serial/__pycache__/win32.cpython-313.pyc +0 -0
- quarchpy/connection_specific/serial/tools/__pycache__/__init__.cpython-311.pyc +0 -0
- quarchpy/connection_specific/serial/tools/__pycache__/__init__.cpython-313.pyc +0 -0
- quarchpy/connection_specific/serial/tools/__pycache__/list_ports.cpython-311.pyc +0 -0
- quarchpy/connection_specific/serial/tools/__pycache__/list_ports.cpython-313.pyc +0 -0
- quarchpy/connection_specific/serial/tools/__pycache__/list_ports_common.cpython-311.pyc +0 -0
- quarchpy/connection_specific/serial/tools/__pycache__/list_ports_common.cpython-313.pyc +0 -0
- quarchpy/connection_specific/serial/tools/__pycache__/list_ports_windows.cpython-311.pyc +0 -0
- quarchpy/connection_specific/serial/tools/__pycache__/list_ports_windows.cpython-313.pyc +0 -0
- quarchpy/connection_specific/usb_libs/__pycache__/libusb1.cpython-311.pyc +0 -0
- quarchpy/connection_specific/usb_libs/__pycache__/usb1.cpython-311.pyc +0 -0
- quarchpy/connection_specific/usb_libs/usb1.py +2360 -2360
- quarchpy/debug/SystemTest.py +9 -2
- quarchpy/debug/__pycache__/SystemTest.cpython-311.pyc +0 -0
- quarchpy/debug/__pycache__/SystemTest.cpython-313.pyc +0 -0
- quarchpy/debug/__pycache__/__init__.cpython-311.pyc +0 -0
- quarchpy/debug/__pycache__/__init__.cpython-313.pyc +0 -0
- quarchpy/debug/__pycache__/module_debug.cpython-313.pyc +0 -0
- quarchpy/debug/__pycache__/simple_terminal.cpython-313.pyc +0 -0
- quarchpy/debug/__pycache__/upgrade_quarchpy.cpython-313.pyc +0 -0
- quarchpy/debug/__pycache__/versionCompare.cpython-311.pyc +0 -0
- quarchpy/debug/__pycache__/versionCompare.cpython-313.pyc +0 -0
- quarchpy/device/__pycache__/__init__.cpython-311.pyc +0 -0
- quarchpy/device/__pycache__/__init__.cpython-313.pyc +0 -0
- quarchpy/device/__pycache__/device.cpython-311.pyc +0 -0
- quarchpy/device/__pycache__/device.cpython-313.pyc +0 -0
- quarchpy/device/__pycache__/quarchArray.cpython-311.pyc +0 -0
- quarchpy/device/__pycache__/quarchArray.cpython-313.pyc +0 -0
- quarchpy/device/__pycache__/quarchPPM.cpython-311.pyc +0 -0
- quarchpy/device/__pycache__/quarchPPM.cpython-313.pyc +0 -0
- quarchpy/device/__pycache__/quarchQPS.cpython-311.pyc +0 -0
- quarchpy/device/__pycache__/quarchQPS.cpython-313.pyc +0 -0
- quarchpy/device/__pycache__/scanDevices.cpython-311.pyc +0 -0
- quarchpy/device/__pycache__/scanDevices.cpython-313.pyc +0 -0
- quarchpy/device/quarchQPS.py +96 -67
- quarchpy/disk_test/__pycache__/AbsDiskFinder.cpython-311.pyc +0 -0
- quarchpy/disk_test/__pycache__/AbsDiskFinder.cpython-313.pyc +0 -0
- quarchpy/disk_test/__pycache__/DiskTargetSelection.cpython-311.pyc +0 -0
- quarchpy/disk_test/__pycache__/DiskTargetSelection.cpython-313.pyc +0 -0
- quarchpy/disk_test/__pycache__/__init__.cpython-311.pyc +0 -0
- quarchpy/disk_test/__pycache__/__init__.cpython-313.pyc +0 -0
- quarchpy/disk_test/__pycache__/iometerDiskFinder.cpython-311.pyc +0 -0
- quarchpy/disk_test/__pycache__/iometerDiskFinder.cpython-313.pyc +0 -0
- quarchpy/docs/CHANGES.rst +4 -0
- quarchpy/docs/Makefile +20 -20
- quarchpy/docs/_build/html/_static/custom.css +1 -1
- quarchpy/docs/_build/html/_static/doctools.js +149 -149
- quarchpy/docs/_build/html/_static/jquery-3.4.1.js +10598 -10598
- quarchpy/docs/_build/html/_static/jquery.js +2 -2
- quarchpy/docs/_build/html/_static/searchtools.js +632 -632
- quarchpy/docs/_build/html/_static/sphinx_highlight.js +154 -154
- quarchpy/docs/_build/html/_static/underscore-1.3.1.js +999 -999
- quarchpy/docs/_build/html/_static/underscore.js +5 -5
- quarchpy/fio/__pycache__/FIO_interface.cpython-311.pyc +0 -0
- quarchpy/fio/__pycache__/FIO_interface.cpython-313.pyc +0 -0
- quarchpy/fio/__pycache__/__init__.cpython-311.pyc +0 -0
- quarchpy/fio/__pycache__/__init__.cpython-313.pyc +0 -0
- quarchpy/iometer/__pycache__/__init__.cpython-311.pyc +0 -0
- quarchpy/iometer/__pycache__/__init__.cpython-313.pyc +0 -0
- quarchpy/iometer/__pycache__/gen_iometer_template.cpython-311.pyc +0 -0
- quarchpy/iometer/__pycache__/{gen_iometer_template.cpython-312.pyc → gen_iometer_template.cpython-313.pyc} +0 -0
- quarchpy/iometer/__pycache__/iometerFuncs.cpython-311.pyc +0 -0
- quarchpy/iometer/__pycache__/iometerFuncs.cpython-313.pyc +0 -0
- quarchpy/qis/__pycache__/StreamHeaderInfo.cpython-311.pyc +0 -0
- quarchpy/qis/__pycache__/StreamHeaderInfo.cpython-313.pyc +0 -0
- quarchpy/qis/__pycache__/__init__.cpython-311.pyc +0 -0
- quarchpy/qis/__pycache__/__init__.cpython-313.pyc +0 -0
- quarchpy/qis/__pycache__/qisFuncs.cpython-311.pyc +0 -0
- quarchpy/qis/__pycache__/qisFuncs.cpython-313.pyc +0 -0
- quarchpy/qis/qisFuncs.py +4 -2
- quarchpy/qps/__pycache__/__init__.cpython-311.pyc +0 -0
- quarchpy/qps/__pycache__/__init__.cpython-313.pyc +0 -0
- quarchpy/qps/__pycache__/qpsFuncs.cpython-311.pyc +0 -0
- quarchpy/qps/__pycache__/qpsFuncs.cpython-313.pyc +0 -0
- quarchpy/user_interface/__pycache__/__init__.cpython-311.pyc +0 -0
- quarchpy/user_interface/__pycache__/__init__.cpython-313.pyc +0 -0
- quarchpy/user_interface/__pycache__/user_interface.cpython-311.pyc +0 -0
- quarchpy/user_interface/__pycache__/user_interface.cpython-313.pyc +0 -0
- quarchpy/utilities/__pycache__/TestCenter.cpython-311.pyc +0 -0
- quarchpy/utilities/__pycache__/TestCenter.cpython-313.pyc +0 -0
- quarchpy/utilities/__pycache__/TimeValue.cpython-311.pyc +0 -0
- quarchpy/utilities/__pycache__/TimeValue.cpython-313.pyc +0 -0
- quarchpy/utilities/__pycache__/Version.cpython-311.pyc +0 -0
- quarchpy/utilities/__pycache__/Version.cpython-313.pyc +0 -0
- quarchpy/utilities/__pycache__/__init__.cpython-311.pyc +0 -0
- quarchpy/utilities/__pycache__/__init__.cpython-313.pyc +0 -0
- {quarchpy-2.2.8.dev1.dist-info → quarchpy-2.2.9.dev1.dist-info}/METADATA +5 -1
- {quarchpy-2.2.8.dev1.dist-info → quarchpy-2.2.9.dev1.dist-info}/RECORD +152 -150
- quarchpy/.idea/.name +0 -1
- quarchpy/.idea/inspectionProfiles/Project_Default.xml +0 -50
- quarchpy/.idea/inspectionProfiles/profiles_settings.xml +0 -6
- quarchpy/.idea/misc.xml +0 -7
- quarchpy/.idea/modules.xml +0 -8
- quarchpy/.idea/quarchpy.iml +0 -12
- quarchpy/.idea/vcs.xml +0 -6
- quarchpy/.idea/workspace.xml +0 -173
- quarchpy/__pycache__/__init__.cpython-312.pyc +0 -0
- quarchpy/__pycache__/_version.cpython-312.pyc +0 -0
- quarchpy/__pycache__/connection.cpython-312.pyc +0 -0
- quarchpy/__pycache__/run.cpython-311.pyc +0 -0
- quarchpy/__pycache__/run.cpython-312.pyc +0 -0
- quarchpy/config_files/__pycache__/__init__.cpython-312.pyc +0 -0
- quarchpy/config_files/__pycache__/quarch_config_parser.cpython-312.pyc +0 -0
- quarchpy/connection_specific/QPS/win-amd64/InstallType.dat +0 -1
- quarchpy/connection_specific/QPS/win-amd64/qis/myFile.csv +0 -50
- quarchpy/connection_specific/__pycache__/StreamChannels.cpython-312.pyc +0 -0
- quarchpy/connection_specific/__pycache__/__init__.cpython-312.pyc +0 -0
- quarchpy/connection_specific/__pycache__/connection_QIS.cpython-312.pyc +0 -0
- quarchpy/connection_specific/__pycache__/connection_QPS.cpython-312.pyc +0 -0
- quarchpy/connection_specific/__pycache__/connection_ReST.cpython-312.pyc +0 -0
- quarchpy/connection_specific/__pycache__/connection_Serial.cpython-312.pyc +0 -0
- quarchpy/connection_specific/__pycache__/connection_TCP.cpython-312.pyc +0 -0
- quarchpy/connection_specific/__pycache__/connection_USB.cpython-312.pyc +0 -0
- quarchpy/connection_specific/__pycache__/mDNS.cpython-312.pyc +0 -0
- quarchpy/connection_specific/jdk_j21_jres/__pycache__/__init__.cpython-312.pyc +0 -0
- quarchpy/connection_specific/jdk_j21_jres/__pycache__/fix_permissions.cpython-312.pyc +0 -0
- quarchpy/connection_specific/serial/__pycache__/__init__.cpython-312.pyc +0 -0
- quarchpy/connection_specific/serial/__pycache__/serialutil.cpython-312.pyc +0 -0
- quarchpy/connection_specific/serial/__pycache__/serialwin32.cpython-312.pyc +0 -0
- quarchpy/connection_specific/serial/__pycache__/win32.cpython-312.pyc +0 -0
- quarchpy/connection_specific/serial/tools/__pycache__/__init__.cpython-312.pyc +0 -0
- quarchpy/connection_specific/serial/tools/__pycache__/list_ports.cpython-312.pyc +0 -0
- quarchpy/connection_specific/serial/tools/__pycache__/list_ports_common.cpython-312.pyc +0 -0
- quarchpy/connection_specific/serial/tools/__pycache__/list_ports_windows.cpython-312.pyc +0 -0
- quarchpy/connection_specific/usb_libs/__pycache__/libusb1.cpython-312.pyc +0 -0
- quarchpy/connection_specific/usb_libs/__pycache__/usb1.cpython-312.pyc +0 -0
- quarchpy/debug/__pycache__/SystemTest.cpython-312.pyc +0 -0
- quarchpy/debug/__pycache__/__init__.cpython-312.pyc +0 -0
- quarchpy/debug/__pycache__/module_debug.cpython-311.pyc +0 -0
- quarchpy/debug/__pycache__/module_debug.cpython-312.pyc +0 -0
- quarchpy/debug/__pycache__/simple_terminal.cpython-311.pyc +0 -0
- quarchpy/debug/__pycache__/simple_terminal.cpython-312.pyc +0 -0
- quarchpy/debug/__pycache__/upgrade_quarchpy.cpython-311.pyc +0 -0
- quarchpy/debug/__pycache__/upgrade_quarchpy.cpython-312.pyc +0 -0
- quarchpy/debug/__pycache__/versionCompare.cpython-312.pyc +0 -0
- quarchpy/device/__pycache__/__init__.cpython-312.pyc +0 -0
- quarchpy/device/__pycache__/device.cpython-312.pyc +0 -0
- quarchpy/device/__pycache__/quarchArray.cpython-312.pyc +0 -0
- quarchpy/device/__pycache__/quarchPPM.cpython-312.pyc +0 -0
- quarchpy/device/__pycache__/quarchQPS.cpython-312.pyc +0 -0
- quarchpy/device/__pycache__/scanDevices.cpython-312.pyc +0 -0
- quarchpy/disk_test/__pycache__/AbsDiskFinder.cpython-312.pyc +0 -0
- quarchpy/disk_test/__pycache__/DiskTargetSelection.cpython-312.pyc +0 -0
- quarchpy/disk_test/__pycache__/__init__.cpython-312.pyc +0 -0
- quarchpy/disk_test/__pycache__/iometerDiskFinder.cpython-312.pyc +0 -0
- quarchpy/fio/__pycache__/FIO_interface.cpython-312.pyc +0 -0
- quarchpy/fio/__pycache__/__init__.cpython-312.pyc +0 -0
- quarchpy/iometer/__pycache__/__init__.cpython-312.pyc +0 -0
- quarchpy/iometer/__pycache__/iometerFuncs.cpython-312.pyc +0 -0
- quarchpy/qis/__pycache__/StreamHeaderInfo.cpython-312.pyc +0 -0
- quarchpy/qis/__pycache__/__init__.cpython-312.pyc +0 -0
- quarchpy/qis/__pycache__/qisFuncs.cpython-312.pyc +0 -0
- quarchpy/qps/__pycache__/__init__.cpython-312.pyc +0 -0
- quarchpy/qps/__pycache__/qpsFuncs.cpython-312.pyc +0 -0
- quarchpy/user_interface/__pycache__/__init__.cpython-312.pyc +0 -0
- quarchpy/user_interface/__pycache__/user_interface.cpython-312.pyc +0 -0
- quarchpy/utilities/__pycache__/TestCenter.cpython-312.pyc +0 -0
- quarchpy/utilities/__pycache__/TimeValue.cpython-312.pyc +0 -0
- quarchpy/utilities/__pycache__/Version.cpython-312.pyc +0 -0
- quarchpy/utilities/__pycache__/__init__.cpython-312.pyc +0 -0
- /quarchpy/fio/{test_performance_class.py → HIDEtest_performance_class.py} +0 -0
- {quarchpy-2.2.8.dev1.dist-info → quarchpy-2.2.9.dev1.dist-info}/WHEEL +0 -0
- {quarchpy-2.2.8.dev1.dist-info → quarchpy-2.2.9.dev1.dist-info}/top_level.txt +0 -0
@@ -1,2360 +1,2360 @@
|
|
1
|
-
# Copyright (C) 2010-2015 Vincent Pelletier <plr.vincent@gmail.com>
|
2
|
-
#
|
3
|
-
# This library is free software; you can redistribute it and/or
|
4
|
-
# modify it under the terms of the GNU Lesser General Public
|
5
|
-
# License as published by the Free Software Foundation; either
|
6
|
-
# version 2.1 of the License, or (at your option) any later version.
|
7
|
-
#
|
8
|
-
# This library is distributed in the hope that it will be useful,
|
9
|
-
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
10
|
-
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
11
|
-
# Lesser General Public License for more details.
|
12
|
-
#
|
13
|
-
# You should have received a copy of the GNU Lesser General Public
|
14
|
-
# License along with this library; if not, write to the Free Software
|
15
|
-
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
16
|
-
|
17
|
-
# pylint: disable=invalid-name, too-many-locals, too-many-arguments
|
18
|
-
# pylint: disable=too-many-public-methods, too-many-instance-attributes
|
19
|
-
# pylint: disable=missing-docstring
|
20
|
-
"""
|
21
|
-
Pythonic wrapper for libusb-1.0.
|
22
|
-
|
23
|
-
The first thing you must do is to get an "USB context". To do so, create an
|
24
|
-
USBContext instance.
|
25
|
-
Then, you can use it to browse available USB devices and open the one you want
|
26
|
-
to talk to.
|
27
|
-
At this point, you should have a USBDeviceHandle instance (as returned by
|
28
|
-
USBContext or USBDevice instances), and you can start exchanging with the
|
29
|
-
device.
|
30
|
-
|
31
|
-
Features:
|
32
|
-
- Basic device settings (configuration & interface selection, ...)
|
33
|
-
- String descriptor lookups (ASCII & unicode), and list supported language
|
34
|
-
codes
|
35
|
-
- Synchronous I/O (control, bulk, interrupt)
|
36
|
-
- Asynchronous I/O (control, bulk, interrupt, isochronous)
|
37
|
-
Note: Isochronous support is not well tested.
|
38
|
-
See USBPoller, USBTransfer and USBTransferHelper.
|
39
|
-
|
40
|
-
All LIBUSB_* constants are available in this module, without the LIBUSB_
|
41
|
-
prefix - with one exception: LIBUSB_5GBPS_OPERATION is available as
|
42
|
-
SUPER_SPEED_OPERATION, so it is a valid python identifier.
|
43
|
-
|
44
|
-
All LIBUSB_ERROR_* constants are available in this module as exception classes,
|
45
|
-
subclassing USBError.
|
46
|
-
"""
|
47
|
-
|
48
|
-
import libusb1
|
49
|
-
from ctypes import byref, create_string_buffer, c_int, sizeof, POINTER, \
|
50
|
-
cast, c_uint8, c_uint16, c_ubyte, string_at, c_void_p, cdll, addressof
|
51
|
-
import sys
|
52
|
-
import threading
|
53
|
-
from ctypes.util import find_library
|
54
|
-
import warnings
|
55
|
-
import weakref
|
56
|
-
import collections
|
57
|
-
import functools
|
58
|
-
|
59
|
-
__all__ = [
|
60
|
-
'USBContext', 'USBDeviceHandle', 'USBDevice', 'hasCapability',
|
61
|
-
'USBPoller', 'USBTransfer', 'USBTransferHelper', 'EVENT_CALLBACK_SET',
|
62
|
-
'USBPollerThread', 'USBEndpoint', 'USBInterfaceSetting', 'USBInterface',
|
63
|
-
'USBConfiguration', 'DoomedTransferError', 'getVersion', 'USBError',
|
64
|
-
]
|
65
|
-
# Bind libusb1 constants and libusb1.USBError to this module, so user does not
|
66
|
-
# have to import two modules.
|
67
|
-
USBError = libusb1.USBError
|
68
|
-
STATUS_TO_EXCEPTION_DICT = {}
|
69
|
-
def __bindConstants():
|
70
|
-
global_dict = globals()
|
71
|
-
PREFIX = 'LIBUSB_'
|
72
|
-
for name, value in libusb1.__dict__.items():
|
73
|
-
if name.startswith(PREFIX):
|
74
|
-
name = name[len(PREFIX):]
|
75
|
-
# Gah.
|
76
|
-
if name == '5GBPS_OPERATION':
|
77
|
-
name = 'SUPER_SPEED_OPERATION'
|
78
|
-
assert name not in global_dict
|
79
|
-
global_dict[name] = value
|
80
|
-
__all__.append(name)
|
81
|
-
# Finer-grained exceptions.
|
82
|
-
for name, value in libusb1.libusb_error.forward_dict.items():
|
83
|
-
if value:
|
84
|
-
assert name.startswith(PREFIX + 'ERROR_'), name
|
85
|
-
if name == 'LIBUSB_ERROR_IO':
|
86
|
-
name = 'ErrorIO'
|
87
|
-
else:
|
88
|
-
name = ''.join(x.capitalize() for x in name.split('_')[1:])
|
89
|
-
name = 'USB' + name
|
90
|
-
assert name not in global_dict, name
|
91
|
-
assert value not in STATUS_TO_EXCEPTION_DICT
|
92
|
-
STATUS_TO_EXCEPTION_DICT[value] = global_dict[name] = type(
|
93
|
-
name,
|
94
|
-
(USBError, ),
|
95
|
-
{'value': value},
|
96
|
-
)
|
97
|
-
__all__.append(name)
|
98
|
-
__bindConstants()
|
99
|
-
del __bindConstants
|
100
|
-
|
101
|
-
def raiseUSBError(value):
|
102
|
-
raise STATUS_TO_EXCEPTION_DICT.get(value, USBError)(value)
|
103
|
-
|
104
|
-
def mayRaiseUSBError(value):
|
105
|
-
if value < 0:
|
106
|
-
raiseUSBError(value)
|
107
|
-
|
108
|
-
try:
|
109
|
-
namedtuple = collections.namedtuple
|
110
|
-
except AttributeError:
|
111
|
-
Version = lambda *x: x
|
112
|
-
else:
|
113
|
-
Version = namedtuple(
|
114
|
-
'Version',
|
115
|
-
['major', 'minor', 'micro', 'nano', 'rc', 'describe'],
|
116
|
-
)
|
117
|
-
|
118
|
-
if sys.version_info[0] == 3:
|
119
|
-
BYTE = bytes([0])
|
120
|
-
# pylint: disable=redefined-builtin
|
121
|
-
xrange = range
|
122
|
-
long = int
|
123
|
-
# pylint: enable=redefined-builtin
|
124
|
-
else:
|
125
|
-
BYTE = '\x00'
|
126
|
-
# pylint: disable=undefined-variable
|
127
|
-
CONTROL_SETUP = BYTE * CONTROL_SETUP_SIZE
|
128
|
-
# pylint: enable=undefined-variable
|
129
|
-
|
130
|
-
if sys.version_info[:2] >= (2, 6):
|
131
|
-
if sys.platform == 'win32':
|
132
|
-
from ctypes import get_last_error as get_errno
|
133
|
-
else:
|
134
|
-
from ctypes import get_errno
|
135
|
-
else:
|
136
|
-
def get_errno():
|
137
|
-
raise NotImplementedError(
|
138
|
-
'Your python version does not support errno/last_error'
|
139
|
-
)
|
140
|
-
|
141
|
-
__libc_name = find_library('c')
|
142
|
-
if __libc_name is None:
|
143
|
-
# Of course, will leak memory.
|
144
|
-
# Should we warn user ? How ?
|
145
|
-
_free = lambda x: None
|
146
|
-
else:
|
147
|
-
_free = getattr(cdll, __libc_name).free
|
148
|
-
del __libc_name
|
149
|
-
|
150
|
-
try:
|
151
|
-
WeakSet = weakref.WeakSet
|
152
|
-
except AttributeError:
|
153
|
-
# Python < 2.7: tiny wrapper around WeakKeyDictionary
|
154
|
-
class WeakSet(object):
|
155
|
-
def __init__(self):
|
156
|
-
self.__dict = weakref.WeakKeyDictionary()
|
157
|
-
|
158
|
-
def add(self, item):
|
159
|
-
self.__dict[item] = None
|
160
|
-
|
161
|
-
def pop(self):
|
162
|
-
return self.__dict.popitem()[0]
|
163
|
-
|
164
|
-
# Default string length
|
165
|
-
# From a comment in libusb-1.0: "Some devices choke on size > 255"
|
166
|
-
STRING_LENGTH = 255
|
167
|
-
|
168
|
-
# As of v3 of USB specs, there cannot be more than 7 hubs from controler to
|
169
|
-
# device.
|
170
|
-
PATH_MAX_DEPTH = 7
|
171
|
-
|
172
|
-
EVENT_CALLBACK_SET = frozenset((
|
173
|
-
# pylint: disable=undefined-variable
|
174
|
-
TRANSFER_COMPLETED,
|
175
|
-
TRANSFER_ERROR,
|
176
|
-
TRANSFER_TIMED_OUT,
|
177
|
-
TRANSFER_CANCELLED,
|
178
|
-
TRANSFER_STALL,
|
179
|
-
TRANSFER_NO_DEVICE,
|
180
|
-
TRANSFER_OVERFLOW,
|
181
|
-
# pylint: enable=undefined-variable
|
182
|
-
))
|
183
|
-
|
184
|
-
DEFAULT_ASYNC_TRANSFER_ERROR_CALLBACK = lambda x: False
|
185
|
-
|
186
|
-
def create_binary_buffer(init_or_size):
|
187
|
-
# Prevent ctypes from adding a trailing null char.
|
188
|
-
if isinstance(init_or_size, (int, long)):
|
189
|
-
result = create_string_buffer(init_or_size)
|
190
|
-
else:
|
191
|
-
result = create_string_buffer(init_or_size, len(init_or_size))
|
192
|
-
return result
|
193
|
-
|
194
|
-
class DoomedTransferError(Exception):
|
195
|
-
"""Exception raised when altering/submitting a doomed transfer."""
|
196
|
-
pass
|
197
|
-
|
198
|
-
class USBTransfer(object):
|
199
|
-
"""
|
200
|
-
USB asynchronous transfer control & data.
|
201
|
-
|
202
|
-
All modification methods will raise if called on a submitted transfer.
|
203
|
-
Methods noted as "should not be called on a submitted transfer" will not
|
204
|
-
prevent you from reading, but returned value is unspecified.
|
205
|
-
|
206
|
-
Note on user_data: because of pypy's current ctype restrictions, user_data
|
207
|
-
is not provided to C level, but is managed purely in python. It should
|
208
|
-
change nothing for you, unless you are looking at underlying C transfer
|
209
|
-
structure - which you should never have to.
|
210
|
-
"""
|
211
|
-
# Prevent garbage collector from freeing the free function before our
|
212
|
-
# instances, as we need it to property destruct them.
|
213
|
-
__libusb_free_transfer = libusb1.libusb_free_transfer
|
214
|
-
__libusb_cancel_transfer = libusb1.libusb_cancel_transfer
|
215
|
-
__USBError = USBError
|
216
|
-
# pylint: disable=undefined-variable
|
217
|
-
__USBErrorNotFound = USBErrorNotFound
|
218
|
-
# pylint: enable=undefined-variable
|
219
|
-
__transfer = None
|
220
|
-
__initialized = False
|
221
|
-
__submitted = False
|
222
|
-
__callback = None
|
223
|
-
__ctypesCallbackWrapper = None
|
224
|
-
__doomed = False
|
225
|
-
__user_data = None
|
226
|
-
__transfer_buffer = None
|
227
|
-
|
228
|
-
def __init__(self, handle, iso_packets, before_submit, after_completion):
|
229
|
-
"""
|
230
|
-
You should not instanciate this class directly.
|
231
|
-
Call "getTransfer" method on an USBDeviceHandle instance to get
|
232
|
-
instances of this class.
|
233
|
-
"""
|
234
|
-
if iso_packets < 0:
|
235
|
-
raise ValueError(
|
236
|
-
'Cannot request a negative number of iso packets.'
|
237
|
-
)
|
238
|
-
self.__handle = handle
|
239
|
-
self.__before_submit = before_submit
|
240
|
-
self.__after_completion = after_completion
|
241
|
-
self.__num_iso_packets = iso_packets
|
242
|
-
result = libusb1.libusb_alloc_transfer(iso_packets)
|
243
|
-
if not result:
|
244
|
-
# pylint: disable=undefined-variable
|
245
|
-
raise USBErrorNoMem
|
246
|
-
# pylint: enable=undefined-variable
|
247
|
-
self.__transfer = result
|
248
|
-
self.__ctypesCallbackWrapper = libusb1.libusb_transfer_cb_fn_p(
|
249
|
-
self.__callbackWrapper)
|
250
|
-
|
251
|
-
def close(self):
|
252
|
-
"""
|
253
|
-
Break reference cycles to allow instance to be garbage-collected.
|
254
|
-
Raises if called on a submitted transfer.
|
255
|
-
"""
|
256
|
-
if self.__submitted:
|
257
|
-
raise ValueError('Cannot close a submitted transfer')
|
258
|
-
self.doom()
|
259
|
-
self.__initialized = False
|
260
|
-
# Break possible external reference cycles
|
261
|
-
self.__callback = None
|
262
|
-
self.__user_data = None
|
263
|
-
# Break libusb_transfer reference cycles
|
264
|
-
self.__ctypesCallbackWrapper = None
|
265
|
-
# For some reason, overwriting callback is not enough to remove this
|
266
|
-
# reference cycle - though sometimes it works:
|
267
|
-
# self -> self.__dict__ -> libusb_transfer -> dict[x] -> dict[x] ->
|
268
|
-
# CThunkObject -> __callbackWrapper -> self
|
269
|
-
# So free transfer altogether.
|
270
|
-
if self.__transfer is not None:
|
271
|
-
self.__libusb_free_transfer(self.__transfer)
|
272
|
-
self.__transfer = None
|
273
|
-
self.__transfer_buffer = None
|
274
|
-
# Break USBDeviceHandle reference cycle
|
275
|
-
self.__before_submit = None
|
276
|
-
self.__after_completion = None
|
277
|
-
|
278
|
-
def doom(self):
|
279
|
-
"""
|
280
|
-
Prevent transfer from being submitted again.
|
281
|
-
"""
|
282
|
-
self.__doomed = True
|
283
|
-
|
284
|
-
def __del__(self):
|
285
|
-
if self.__transfer is not None:
|
286
|
-
try:
|
287
|
-
# If this doesn't raise, we're doomed; transfer was submitted,
|
288
|
-
# still python decided to garbage-collect this instance.
|
289
|
-
# Stick to libusb's documentation, and don't free the
|
290
|
-
# transfer. If interpreter is shutting down, kernel will
|
291
|
-
# reclaim memory anyway.
|
292
|
-
# Note: we can't prevent transfer's buffer from being
|
293
|
-
# garbage-collected as soon as there will be no remaining
|
294
|
-
# reference to transfer, so a segfault might happen anyway.
|
295
|
-
# Should we warn user ? How ?
|
296
|
-
self.cancel()
|
297
|
-
except self.__USBErrorNotFound:
|
298
|
-
# Transfer was not submitted, we can free it.
|
299
|
-
self.__libusb_free_transfer(self.__transfer)
|
300
|
-
|
301
|
-
# pylint: disable=unused-argument
|
302
|
-
def __callbackWrapper(self, transfer_p):
|
303
|
-
"""
|
304
|
-
Makes it possible for user-provided callback to alter transfer when
|
305
|
-
fired (ie, mark transfer as not submitted upon call).
|
306
|
-
"""
|
307
|
-
self.__submitted = False
|
308
|
-
self.__after_completion(self)
|
309
|
-
callback = self.__callback
|
310
|
-
if callback is not None:
|
311
|
-
callback(self)
|
312
|
-
if self.__doomed:
|
313
|
-
self.close()
|
314
|
-
# pylint: enable=unused-argument
|
315
|
-
|
316
|
-
def setCallback(self, callback):
|
317
|
-
"""
|
318
|
-
Change transfer's callback.
|
319
|
-
"""
|
320
|
-
self.__callback = callback
|
321
|
-
|
322
|
-
def getCallback(self):
|
323
|
-
"""
|
324
|
-
Get currently set callback.
|
325
|
-
"""
|
326
|
-
return self.__callback
|
327
|
-
|
328
|
-
def setControl(
|
329
|
-
self, request_type, request, value, index, buffer_or_len,
|
330
|
-
callback=None, user_data=None, timeout=0):
|
331
|
-
"""
|
332
|
-
Setup transfer for control use.
|
333
|
-
|
334
|
-
request_type, request, value, index
|
335
|
-
See USBDeviceHandle.controlWrite.
|
336
|
-
request_type defines transfer direction (see
|
337
|
-
ENDPOINT_OUT and ENDPOINT_IN)).
|
338
|
-
buffer_or_len
|
339
|
-
Either a string (when sending data), or expected data length (when
|
340
|
-
receiving data).
|
341
|
-
callback
|
342
|
-
Callback function to be invoked on transfer completion.
|
343
|
-
Called with transfer as parameter, return value ignored.
|
344
|
-
user_data
|
345
|
-
User data to pass to callback function.
|
346
|
-
timeout
|
347
|
-
Transfer timeout in milliseconds. 0 to disable.
|
348
|
-
"""
|
349
|
-
if self.__submitted:
|
350
|
-
raise ValueError('Cannot alter a submitted transfer')
|
351
|
-
if self.__doomed:
|
352
|
-
raise DoomedTransferError('Cannot reuse a doomed transfer')
|
353
|
-
if isinstance(buffer_or_len, (int, long)):
|
354
|
-
length = buffer_or_len
|
355
|
-
# pylint: disable=undefined-variable
|
356
|
-
string_buffer = create_binary_buffer(length + CONTROL_SETUP_SIZE)
|
357
|
-
# pylint: enable=undefined-variable
|
358
|
-
else:
|
359
|
-
length = len(buffer_or_len)
|
360
|
-
string_buffer = create_binary_buffer(CONTROL_SETUP + buffer_or_len)
|
361
|
-
self.__initialized = False
|
362
|
-
self.__transfer_buffer = string_buffer
|
363
|
-
self.__user_data = user_data
|
364
|
-
libusb1.libusb_fill_control_setup(
|
365
|
-
string_buffer, request_type, request, value, index, length)
|
366
|
-
libusb1.libusb_fill_control_transfer(
|
367
|
-
self.__transfer, self.__handle, string_buffer,
|
368
|
-
self.__ctypesCallbackWrapper, None, timeout)
|
369
|
-
self.__callback = callback
|
370
|
-
self.__initialized = True
|
371
|
-
|
372
|
-
def setBulk(
|
373
|
-
self, endpoint, buffer_or_len, callback=None, user_data=None,
|
374
|
-
timeout=0):
|
375
|
-
"""
|
376
|
-
Setup transfer for bulk use.
|
377
|
-
|
378
|
-
endpoint
|
379
|
-
Endpoint to submit transfer to. Defines transfer direction (see
|
380
|
-
ENDPOINT_OUT and ENDPOINT_IN)).
|
381
|
-
buffer_or_len
|
382
|
-
Either a string (when sending data), or expected data length (when
|
383
|
-
receiving data)
|
384
|
-
callback
|
385
|
-
Callback function to be invoked on transfer completion.
|
386
|
-
Called with transfer as parameter, return value ignored.
|
387
|
-
user_data
|
388
|
-
User data to pass to callback function.
|
389
|
-
timeout
|
390
|
-
Transfer timeout in milliseconds. 0 to disable.
|
391
|
-
"""
|
392
|
-
if self.__submitted:
|
393
|
-
raise ValueError('Cannot alter a submitted transfer')
|
394
|
-
if self.__doomed:
|
395
|
-
raise DoomedTransferError('Cannot reuse a doomed transfer')
|
396
|
-
string_buffer = create_binary_buffer(buffer_or_len)
|
397
|
-
self.__initialized = False
|
398
|
-
self.__transfer_buffer = string_buffer
|
399
|
-
self.__user_data = user_data
|
400
|
-
libusb1.libusb_fill_bulk_transfer(
|
401
|
-
self.__transfer, self.__handle, endpoint, string_buffer,
|
402
|
-
sizeof(string_buffer), self.__ctypesCallbackWrapper, None, timeout)
|
403
|
-
self.__callback = callback
|
404
|
-
self.__initialized = True
|
405
|
-
|
406
|
-
def setInterrupt(
|
407
|
-
self, endpoint, buffer_or_len, callback=None, user_data=None,
|
408
|
-
timeout=0):
|
409
|
-
"""
|
410
|
-
Setup transfer for interrupt use.
|
411
|
-
|
412
|
-
endpoint
|
413
|
-
Endpoint to submit transfer to. Defines transfer direction (see
|
414
|
-
ENDPOINT_OUT and ENDPOINT_IN)).
|
415
|
-
buffer_or_len
|
416
|
-
Either a string (when sending data), or expected data length (when
|
417
|
-
receiving data)
|
418
|
-
callback
|
419
|
-
Callback function to be invoked on transfer completion.
|
420
|
-
Called with transfer as parameter, return value ignored.
|
421
|
-
user_data
|
422
|
-
User data to pass to callback function.
|
423
|
-
timeout
|
424
|
-
Transfer timeout in milliseconds. 0 to disable.
|
425
|
-
"""
|
426
|
-
if self.__submitted:
|
427
|
-
raise ValueError('Cannot alter a submitted transfer')
|
428
|
-
if self.__doomed:
|
429
|
-
raise DoomedTransferError('Cannot reuse a doomed transfer')
|
430
|
-
string_buffer = create_binary_buffer(buffer_or_len)
|
431
|
-
self.__initialized = False
|
432
|
-
self.__transfer_buffer = string_buffer
|
433
|
-
self.__user_data = user_data
|
434
|
-
libusb1.libusb_fill_interrupt_transfer(
|
435
|
-
self.__transfer, self.__handle, endpoint, string_buffer,
|
436
|
-
sizeof(string_buffer), self.__ctypesCallbackWrapper, None, timeout)
|
437
|
-
self.__callback = callback
|
438
|
-
self.__initialized = True
|
439
|
-
|
440
|
-
def setIsochronous(
|
441
|
-
self, endpoint, buffer_or_len, callback=None,
|
442
|
-
user_data=None, timeout=0, iso_transfer_length_list=None):
|
443
|
-
"""
|
444
|
-
Setup transfer for isochronous use.
|
445
|
-
|
446
|
-
endpoint
|
447
|
-
Endpoint to submit transfer to. Defines transfer direction (see
|
448
|
-
ENDPOINT_OUT and ENDPOINT_IN)).
|
449
|
-
buffer_or_len
|
450
|
-
Either a string (when sending data), or expected data length (when
|
451
|
-
receiving data)
|
452
|
-
callback
|
453
|
-
Callback function to be invoked on transfer completion.
|
454
|
-
Called with transfer as parameter, return value ignored.
|
455
|
-
user_data
|
456
|
-
User data to pass to callback function.
|
457
|
-
timeout
|
458
|
-
Transfer timeout in milliseconds. 0 to disable.
|
459
|
-
iso_transfer_length_list
|
460
|
-
List of individual transfer sizes. If not provided, buffer_or_len
|
461
|
-
will be divided evenly among available transfers if possible, and
|
462
|
-
raise ValueError otherwise.
|
463
|
-
"""
|
464
|
-
if self.__submitted:
|
465
|
-
raise ValueError('Cannot alter a submitted transfer')
|
466
|
-
num_iso_packets = self.__num_iso_packets
|
467
|
-
if num_iso_packets == 0:
|
468
|
-
raise TypeError(
|
469
|
-
'This transfer canot be used for isochronous I/O. '
|
470
|
-
'You must get another one with a non-zero iso_packets '
|
471
|
-
'parameter.'
|
472
|
-
)
|
473
|
-
if self.__doomed:
|
474
|
-
raise DoomedTransferError('Cannot reuse a doomed transfer')
|
475
|
-
string_buffer = create_binary_buffer(buffer_or_len)
|
476
|
-
buffer_length = sizeof(string_buffer)
|
477
|
-
if iso_transfer_length_list is None:
|
478
|
-
iso_length, remainder = divmod(buffer_length, num_iso_packets)
|
479
|
-
if remainder:
|
480
|
-
raise ValueError(
|
481
|
-
'Buffer size %i cannot be evenly distributed among %i '
|
482
|
-
'transfers' % (
|
483
|
-
buffer_length,
|
484
|
-
num_iso_packets,
|
485
|
-
)
|
486
|
-
)
|
487
|
-
iso_transfer_length_list = [iso_length] * num_iso_packets
|
488
|
-
configured_iso_packets = len(iso_transfer_length_list)
|
489
|
-
if configured_iso_packets > num_iso_packets:
|
490
|
-
raise ValueError(
|
491
|
-
'Too many ISO transfer lengths (%i), there are '
|
492
|
-
'only %i ISO transfers available' % (
|
493
|
-
configured_iso_packets,
|
494
|
-
num_iso_packets,
|
495
|
-
)
|
496
|
-
)
|
497
|
-
if sum(iso_transfer_length_list) > buffer_length:
|
498
|
-
raise ValueError(
|
499
|
-
'ISO transfers too long (%i), there are only '
|
500
|
-
'%i bytes available' % (
|
501
|
-
sum(iso_transfer_length_list),
|
502
|
-
buffer_length,
|
503
|
-
)
|
504
|
-
)
|
505
|
-
transfer_p = self.__transfer
|
506
|
-
self.__initialized = False
|
507
|
-
self.__transfer_buffer = string_buffer
|
508
|
-
self.__user_data = user_data
|
509
|
-
libusb1.libusb_fill_iso_transfer(
|
510
|
-
transfer_p, self.__handle, endpoint, string_buffer, buffer_length,
|
511
|
-
configured_iso_packets, self.__ctypesCallbackWrapper, None,
|
512
|
-
timeout)
|
513
|
-
for length, iso_packet_desc in zip(
|
514
|
-
iso_transfer_length_list,
|
515
|
-
libusb1.get_iso_packet_list(transfer_p)):
|
516
|
-
if length <= 0:
|
517
|
-
raise ValueError(
|
518
|
-
'Negative/null length transfers are not possible.'
|
519
|
-
)
|
520
|
-
iso_packet_desc.length = length
|
521
|
-
self.__callback = callback
|
522
|
-
self.__initialized = True
|
523
|
-
|
524
|
-
def getType(self):
|
525
|
-
"""
|
526
|
-
Get transfer type.
|
527
|
-
|
528
|
-
Returns one of:
|
529
|
-
TRANSFER_TYPE_CONTROL
|
530
|
-
TRANSFER_TYPE_ISOCHRONOUS
|
531
|
-
TRANSFER_TYPE_BULK
|
532
|
-
TRANSFER_TYPE_INTERRUPT
|
533
|
-
"""
|
534
|
-
return self.__transfer.contents.type
|
535
|
-
|
536
|
-
def getEndpoint(self):
|
537
|
-
"""
|
538
|
-
Get endpoint.
|
539
|
-
"""
|
540
|
-
return self.__transfer.contents.endpoint
|
541
|
-
|
542
|
-
def getStatus(self):
|
543
|
-
"""
|
544
|
-
Get transfer status.
|
545
|
-
Should not be called on a submitted transfer.
|
546
|
-
"""
|
547
|
-
return self.__transfer.contents.status
|
548
|
-
|
549
|
-
def getActualLength(self):
|
550
|
-
"""
|
551
|
-
Get actually transfered data length.
|
552
|
-
Should not be called on a submitted transfer.
|
553
|
-
"""
|
554
|
-
return self.__transfer.contents.actual_length
|
555
|
-
|
556
|
-
def getBuffer(self):
|
557
|
-
"""
|
558
|
-
Get data buffer content.
|
559
|
-
Should not be called on a submitted transfer.
|
560
|
-
"""
|
561
|
-
transfer_p = self.__transfer
|
562
|
-
transfer = transfer_p.contents
|
563
|
-
# pylint: disable=undefined-variable
|
564
|
-
if transfer.type == TRANSFER_TYPE_CONTROL:
|
565
|
-
# pylint: enable=undefined-variable
|
566
|
-
result = libusb1.libusb_control_transfer_get_data(transfer_p)
|
567
|
-
else:
|
568
|
-
result = string_at(transfer.buffer, transfer.length)
|
569
|
-
return result
|
570
|
-
|
571
|
-
def getUserData(self):
|
572
|
-
"""
|
573
|
-
Retrieve user data provided on setup.
|
574
|
-
"""
|
575
|
-
return self.__user_data
|
576
|
-
|
577
|
-
def setUserData(self, user_data):
|
578
|
-
"""
|
579
|
-
Change user data.
|
580
|
-
"""
|
581
|
-
self.__user_data = user_data
|
582
|
-
|
583
|
-
def getISOBufferList(self):
|
584
|
-
"""
|
585
|
-
Get individual ISO transfer's buffer.
|
586
|
-
Returns a list with one item per ISO transfer, with their
|
587
|
-
individually-configured sizes.
|
588
|
-
Returned list is consistent with getISOSetupList return value.
|
589
|
-
Should not be called on a submitted transfer.
|
590
|
-
|
591
|
-
See also iterISO.
|
592
|
-
"""
|
593
|
-
transfer_p = self.__transfer
|
594
|
-
transfer = transfer_p.contents
|
595
|
-
# pylint: disable=undefined-variable
|
596
|
-
if transfer.type != TRANSFER_TYPE_ISOCHRONOUS:
|
597
|
-
# pylint: enable=undefined-variable
|
598
|
-
raise TypeError(
|
599
|
-
'This method cannot be called on non-iso transfers.'
|
600
|
-
)
|
601
|
-
return libusb1.get_iso_packet_buffer_list(transfer_p)
|
602
|
-
|
603
|
-
def getISOSetupList(self):
|
604
|
-
"""
|
605
|
-
Get individual ISO transfer's setup.
|
606
|
-
Returns a list of dicts, each containing an individual ISO transfer
|
607
|
-
parameters:
|
608
|
-
- length
|
609
|
-
- actual_length
|
610
|
-
- status
|
611
|
-
(see libusb1's API documentation for their signification)
|
612
|
-
Returned list is consistent with getISOBufferList return value.
|
613
|
-
Should not be called on a submitted transfer (except for 'length'
|
614
|
-
values).
|
615
|
-
"""
|
616
|
-
transfer_p = self.__transfer
|
617
|
-
transfer = transfer_p.contents
|
618
|
-
# pylint: disable=undefined-variable
|
619
|
-
if transfer.type != TRANSFER_TYPE_ISOCHRONOUS:
|
620
|
-
# pylint: enable=undefined-variable
|
621
|
-
raise TypeError(
|
622
|
-
'This method cannot be called on non-iso transfers.'
|
623
|
-
)
|
624
|
-
return [
|
625
|
-
{
|
626
|
-
'length': x.length,
|
627
|
-
'actual_length': x.actual_length,
|
628
|
-
'status': x.status,
|
629
|
-
}
|
630
|
-
for x in libusb1.get_iso_packet_list(transfer_p)
|
631
|
-
]
|
632
|
-
|
633
|
-
def iterISO(self):
|
634
|
-
"""
|
635
|
-
Generator yielding (status, buffer) for each isochornous transfer.
|
636
|
-
buffer is truncated to actual_length.
|
637
|
-
This is more efficient than calling both getISOBufferList and
|
638
|
-
getISOSetupList when receiving data.
|
639
|
-
Should not be called on a submitted transfer.
|
640
|
-
"""
|
641
|
-
transfer_p = self.__transfer
|
642
|
-
transfer = transfer_p.contents
|
643
|
-
# pylint: disable=undefined-variable
|
644
|
-
if transfer.type != TRANSFER_TYPE_ISOCHRONOUS:
|
645
|
-
# pylint: enable=undefined-variable
|
646
|
-
raise TypeError(
|
647
|
-
'This method cannot be called on non-iso transfers.'
|
648
|
-
)
|
649
|
-
buffer_position = transfer.buffer
|
650
|
-
for iso_transfer in libusb1.get_iso_packet_list(transfer_p):
|
651
|
-
yield (
|
652
|
-
iso_transfer.status,
|
653
|
-
string_at(buffer_position, iso_transfer.actual_length),
|
654
|
-
)
|
655
|
-
buffer_position += iso_transfer.length
|
656
|
-
|
657
|
-
def setBuffer(self, buffer_or_len):
|
658
|
-
"""
|
659
|
-
Replace buffer with a new one.
|
660
|
-
Allows resizing read buffer and replacing data sent.
|
661
|
-
Note: resizing is not allowed for isochronous buffer (use
|
662
|
-
setIsochronous).
|
663
|
-
Note: disallowed on control transfers (use setControl).
|
664
|
-
"""
|
665
|
-
if self.__submitted:
|
666
|
-
raise ValueError('Cannot alter a submitted transfer')
|
667
|
-
transfer = self.__transfer.contents
|
668
|
-
# pylint: disable=undefined-variable
|
669
|
-
if transfer.type == TRANSFER_TYPE_CONTROL:
|
670
|
-
# pylint: enable=undefined-variable
|
671
|
-
raise ValueError(
|
672
|
-
'To alter control transfer buffer, use setControl'
|
673
|
-
)
|
674
|
-
buff = create_binary_buffer(buffer_or_len)
|
675
|
-
# pylint: disable=undefined-variable
|
676
|
-
if transfer.type == TRANSFER_TYPE_ISOCHRONOUS and \
|
677
|
-
sizeof(buff) != transfer.length:
|
678
|
-
# pylint: enable=undefined-variable
|
679
|
-
raise ValueError(
|
680
|
-
'To alter isochronous transfer buffer length, use '
|
681
|
-
'setIsochronous'
|
682
|
-
)
|
683
|
-
self.__transfer_buffer = buff
|
684
|
-
transfer.buffer = cast(buff, c_void_p)
|
685
|
-
transfer.length = sizeof(buff)
|
686
|
-
|
687
|
-
def isSubmitted(self):
|
688
|
-
"""
|
689
|
-
Tells if this transfer is submitted and still pending.
|
690
|
-
"""
|
691
|
-
return self.__submitted
|
692
|
-
|
693
|
-
def submit(self):
|
694
|
-
"""
|
695
|
-
Submit transfer for asynchronous handling.
|
696
|
-
"""
|
697
|
-
if self.__submitted:
|
698
|
-
raise ValueError('Cannot submit a submitted transfer')
|
699
|
-
if not self.__initialized:
|
700
|
-
raise ValueError(
|
701
|
-
'Cannot submit a transfer until it has been initialized'
|
702
|
-
)
|
703
|
-
if self.__doomed:
|
704
|
-
raise DoomedTransferError('Cannot submit doomed transfer')
|
705
|
-
self.__before_submit(self)
|
706
|
-
self.__submitted = True
|
707
|
-
result = libusb1.libusb_submit_transfer(self.__transfer)
|
708
|
-
if result:
|
709
|
-
self.__after_completion(self)
|
710
|
-
self.__submitted = False
|
711
|
-
raiseUSBError(result)
|
712
|
-
|
713
|
-
def cancel(self):
|
714
|
-
"""
|
715
|
-
Cancel transfer.
|
716
|
-
Note: cancellation happens asynchronously, so you must wait for
|
717
|
-
TRANSFER_CANCELLED.
|
718
|
-
"""
|
719
|
-
if not self.__submitted:
|
720
|
-
# XXX: Workaround for a bug reported on libusb 1.0.8: calling
|
721
|
-
# libusb_cancel_transfer on a non-submitted transfer might
|
722
|
-
# trigger a segfault.
|
723
|
-
raise self.__USBErrorNotFound
|
724
|
-
result = self.__libusb_cancel_transfer(self.__transfer)
|
725
|
-
if result:
|
726
|
-
raise self.__USBError(result)
|
727
|
-
|
728
|
-
class USBTransferHelper(object):
|
729
|
-
"""
|
730
|
-
Simplifies subscribing to the same transfer over and over, and callback
|
731
|
-
handling:
|
732
|
-
- no need to read event status to execute apropriate code, just setup
|
733
|
-
different functions for each status code
|
734
|
-
- just return True instead of calling submit
|
735
|
-
- no need to check if transfer is doomed before submitting it again,
|
736
|
-
DoomedTransferError is caught.
|
737
|
-
|
738
|
-
Callbacks used in this class must follow the callback API described in
|
739
|
-
USBTransfer, and are expected to return a boolean:
|
740
|
-
- True if transfer is to be submitted again (to receive/send more data)
|
741
|
-
- False otherwise
|
742
|
-
|
743
|
-
Note: as per libusb1 specifications, isochronous transfer global state
|
744
|
-
might be TRANSFER_COMPLETED although some individual packets might
|
745
|
-
have an error status. You can check individual packet status by calling
|
746
|
-
getISOSetupList on transfer object in your callback.
|
747
|
-
"""
|
748
|
-
def __init__(self, transfer=None):
|
749
|
-
"""
|
750
|
-
Create a transfer callback dispatcher.
|
751
|
-
|
752
|
-
transfer parameter is deprecated. If provided, it will be equivalent
|
753
|
-
to:
|
754
|
-
helper = USBTransferHelper()
|
755
|
-
transfer.setCallback(helper)
|
756
|
-
and also allows using deprecated methods on this class (otherwise,
|
757
|
-
they raise AttributeError).
|
758
|
-
"""
|
759
|
-
if transfer is not None:
|
760
|
-
# Deprecated: to drop
|
761
|
-
self.__transfer = transfer
|
762
|
-
transfer.setCallback(self)
|
763
|
-
self.__event_callback_dict = {}
|
764
|
-
self.__errorCallback = DEFAULT_ASYNC_TRANSFER_ERROR_CALLBACK
|
765
|
-
|
766
|
-
def submit(self):
|
767
|
-
"""
|
768
|
-
Submit the asynchronous read request.
|
769
|
-
Deprecated. Use submit on transfer.
|
770
|
-
"""
|
771
|
-
# Deprecated: to drop
|
772
|
-
self.__transfer.submit()
|
773
|
-
|
774
|
-
def cancel(self):
|
775
|
-
"""
|
776
|
-
Cancel a pending read request.
|
777
|
-
Deprecated. Use cancel on transfer.
|
778
|
-
"""
|
779
|
-
# Deprecated: to drop
|
780
|
-
self.__transfer.cancel()
|
781
|
-
|
782
|
-
def setEventCallback(self, event, callback):
|
783
|
-
"""
|
784
|
-
Set a function to call for a given event.
|
785
|
-
event must be one of:
|
786
|
-
TRANSFER_COMPLETED
|
787
|
-
TRANSFER_ERROR
|
788
|
-
TRANSFER_TIMED_OUT
|
789
|
-
TRANSFER_CANCELLED
|
790
|
-
TRANSFER_STALL
|
791
|
-
TRANSFER_NO_DEVICE
|
792
|
-
TRANSFER_OVERFLOW
|
793
|
-
"""
|
794
|
-
if event not in EVENT_CALLBACK_SET:
|
795
|
-
raise ValueError('Unknown event %r.' % (event, ))
|
796
|
-
self.__event_callback_dict[event] = callback
|
797
|
-
|
798
|
-
def setDefaultCallback(self, callback):
|
799
|
-
"""
|
800
|
-
Set the function to call for event which don't have a specific callback
|
801
|
-
registered.
|
802
|
-
The initial default callback does nothing and returns False.
|
803
|
-
"""
|
804
|
-
self.__errorCallback = callback
|
805
|
-
|
806
|
-
def getEventCallback(self, event, default=None):
|
807
|
-
"""
|
808
|
-
Return the function registered to be called for given event identifier.
|
809
|
-
"""
|
810
|
-
return self.__event_callback_dict.get(event, default)
|
811
|
-
|
812
|
-
def __call__(self, transfer):
|
813
|
-
"""
|
814
|
-
Callback to set on transfers.
|
815
|
-
"""
|
816
|
-
if self.getEventCallback(transfer.getStatus(), self.__errorCallback)(
|
817
|
-
transfer):
|
818
|
-
try:
|
819
|
-
transfer.submit()
|
820
|
-
except DoomedTransferError:
|
821
|
-
pass
|
822
|
-
|
823
|
-
def isSubmited(self):
|
824
|
-
"""
|
825
|
-
Returns whether this reader is currently waiting for an event.
|
826
|
-
Deprecatd. Use isSubmitted on transfer.
|
827
|
-
"""
|
828
|
-
# Deprecated: to drop
|
829
|
-
return self.__transfer.isSubmitted()
|
830
|
-
|
831
|
-
class USBPollerThread(threading.Thread):
|
832
|
-
"""
|
833
|
-
Implements libusb1 documentation about threaded, asynchronous
|
834
|
-
applications.
|
835
|
-
In short, instanciate this class once (...per USBContext instance), call
|
836
|
-
start() on the instance, and do whatever you need.
|
837
|
-
This thread will be used to execute transfer completion callbacks, and you
|
838
|
-
are free to use libusb1's synchronous API in another thread, and can forget
|
839
|
-
about libusb1 file descriptors.
|
840
|
-
|
841
|
-
See http://libusb.sourceforge.net/api-1.0/mtasync.html .
|
842
|
-
"""
|
843
|
-
def __init__(self, context, poller, exc_callback=None):
|
844
|
-
"""
|
845
|
-
Create a poller thread for given context.
|
846
|
-
Warning: it will not check if another poller instance was already
|
847
|
-
present for that context, and will replace it.
|
848
|
-
|
849
|
-
poller
|
850
|
-
(same as USBPoller.__init__ "poller" parameter)
|
851
|
-
|
852
|
-
exc_callback (callable)
|
853
|
-
Called with a libusb_error value as single parameter when event
|
854
|
-
handling fails.
|
855
|
-
If not given, an USBError will be raised, interrupting the thread.
|
856
|
-
"""
|
857
|
-
super(USBPollerThread, self).__init__()
|
858
|
-
self.daemon = True
|
859
|
-
self.__context = context
|
860
|
-
self.__poller = poller
|
861
|
-
self.__fd_set = set()
|
862
|
-
context.setPollFDNotifiers(self._registerFD, self._unregisterFD)
|
863
|
-
for fd, events in context.getPollFDList():
|
864
|
-
self._registerFD(fd, events, None)
|
865
|
-
if exc_callback is not None:
|
866
|
-
self.exceptionHandler = exc_callback
|
867
|
-
|
868
|
-
def __del__(self):
|
869
|
-
self.__context.setPollFDNotifiers(None, None)
|
870
|
-
|
871
|
-
# pylint: disable=method-hidden
|
872
|
-
@staticmethod
|
873
|
-
def exceptionHandler(exc):
|
874
|
-
raise exc
|
875
|
-
# pylint: enable=method-hidden
|
876
|
-
|
877
|
-
def run(self):
|
878
|
-
# We expect quite some spinning in below loop, so move any unneeded
|
879
|
-
# operation out of it.
|
880
|
-
context = self.__context
|
881
|
-
poll = self.__poller.poll
|
882
|
-
try_lock_events = context.tryLockEvents
|
883
|
-
lock_event_waiters = context.lockEventWaiters
|
884
|
-
wait_for_event = context.waitForEvent
|
885
|
-
unlock_event_waiters = context.unlockEventWaiters
|
886
|
-
event_handling_ok = context.eventHandlingOK
|
887
|
-
unlock_events = context.unlockEvents
|
888
|
-
handle_events_locked = context.handleEventsLocked
|
889
|
-
event_handler_active = context.eventHandlerActive
|
890
|
-
getNextTimeout = context.getNextTimeout
|
891
|
-
exceptionHandler = self.exceptionHandler
|
892
|
-
fd_set = self.__fd_set
|
893
|
-
while fd_set:
|
894
|
-
if try_lock_events():
|
895
|
-
lock_event_waiters()
|
896
|
-
while event_handler_active():
|
897
|
-
wait_for_event()
|
898
|
-
unlock_event_waiters()
|
899
|
-
else:
|
900
|
-
try:
|
901
|
-
while event_handling_ok():
|
902
|
-
if poll(getNextTimeout()):
|
903
|
-
try:
|
904
|
-
handle_events_locked()
|
905
|
-
except USBError:
|
906
|
-
exceptionHandler(sys.exc_info()[1])
|
907
|
-
finally:
|
908
|
-
unlock_events()
|
909
|
-
|
910
|
-
def _registerFD(self, fd, events, _):
|
911
|
-
self.__poller.register(fd, events)
|
912
|
-
self.__fd_set.add(fd)
|
913
|
-
|
914
|
-
def _unregisterFD(self, fd, _):
|
915
|
-
self.__fd_set.discard(fd)
|
916
|
-
self.__poller.unregister(fd)
|
917
|
-
|
918
|
-
class USBPoller(object):
|
919
|
-
"""
|
920
|
-
Class allowing integration of USB event polling in a file-descriptor
|
921
|
-
monitoring event loop.
|
922
|
-
|
923
|
-
WARNING: Do not call "poll" from several threads concurently. Do not use
|
924
|
-
synchronous USB transfers in a thread while "poll" is running. Doing so
|
925
|
-
will result in unnecessarily long pauses in some threads. Opening and/or
|
926
|
-
closing devices while polling can cause race conditions to occur.
|
927
|
-
"""
|
928
|
-
def __init__(self, context, poller):
|
929
|
-
"""
|
930
|
-
Create a poller for given context.
|
931
|
-
Warning: it will not check if another poller instance was already
|
932
|
-
present for that context, and will replace it.
|
933
|
-
|
934
|
-
poller is a polling instance implementing the following methods:
|
935
|
-
- register(fd, event_flags)
|
936
|
-
event_flags have the same meaning as in poll API (POLLIN & POLLOUT)
|
937
|
-
- unregister(fd)
|
938
|
-
- poll(timeout)
|
939
|
-
timeout being a float in seconds, or negative/None if there is no
|
940
|
-
timeout.
|
941
|
-
It must return a list of (descriptor, event) pairs.
|
942
|
-
Note: USBPoller is itself a valid poller.
|
943
|
-
Note2: select.poll uses a timeout in milliseconds, for some reason
|
944
|
-
(all other select.* classes use seconds for timeout), so you should
|
945
|
-
wrap it to convert & round/truncate timeout.
|
946
|
-
"""
|
947
|
-
self.__context = context
|
948
|
-
self.__poller = poller
|
949
|
-
self.__fd_set = set()
|
950
|
-
context.setPollFDNotifiers(self._registerFD, self._unregisterFD)
|
951
|
-
for fd, events in context.getPollFDList():
|
952
|
-
self._registerFD(fd, events)
|
953
|
-
|
954
|
-
def __del__(self):
|
955
|
-
self.__context.setPollFDNotifiers(None, None)
|
956
|
-
|
957
|
-
def poll(self, timeout=None):
|
958
|
-
"""
|
959
|
-
Poll for events.
|
960
|
-
timeout can be a float in seconds, or None for no timeout.
|
961
|
-
Returns a list of (descriptor, event) pairs.
|
962
|
-
"""
|
963
|
-
next_usb_timeout = self.__context.getNextTimeout()
|
964
|
-
if timeout is None or timeout < 0:
|
965
|
-
usb_timeout = next_usb_timeout
|
966
|
-
elif next_usb_timeout:
|
967
|
-
usb_timeout = min(next_usb_timeout, timeout)
|
968
|
-
else:
|
969
|
-
usb_timeout = timeout
|
970
|
-
event_list = self.__poller.poll(usb_timeout)
|
971
|
-
if event_list:
|
972
|
-
fd_set = self.__fd_set
|
973
|
-
result = [(x, y) for x, y in event_list if x not in fd_set]
|
974
|
-
if len(result) != len(event_list):
|
975
|
-
self.__context.handleEventsTimeout()
|
976
|
-
else:
|
977
|
-
result = event_list
|
978
|
-
self.__context.handleEventsTimeout()
|
979
|
-
return result
|
980
|
-
|
981
|
-
def register(self, fd, events):
|
982
|
-
"""
|
983
|
-
Register an USB-unrelated fd to poller.
|
984
|
-
Convenience method.
|
985
|
-
"""
|
986
|
-
if fd in self.__fd_set:
|
987
|
-
raise ValueError(
|
988
|
-
'This fd is a special USB event fd, it cannot be polled.'
|
989
|
-
)
|
990
|
-
self.__poller.register(fd, events)
|
991
|
-
|
992
|
-
def unregister(self, fd):
|
993
|
-
"""
|
994
|
-
Unregister an USB-unrelated fd from poller.
|
995
|
-
Convenience method.
|
996
|
-
"""
|
997
|
-
if fd in self.__fd_set:
|
998
|
-
raise ValueError(
|
999
|
-
'This fd is a special USB event fd, it must stay registered.'
|
1000
|
-
)
|
1001
|
-
self.__poller.unregister(fd)
|
1002
|
-
|
1003
|
-
# pylint: disable=unused-argument
|
1004
|
-
def _registerFD(self, fd, events, user_data=None):
|
1005
|
-
self.register(fd, events)
|
1006
|
-
self.__fd_set.add(fd)
|
1007
|
-
# pylint: enable=unused-argument
|
1008
|
-
|
1009
|
-
# pylint: disable=unused-argument
|
1010
|
-
def _unregisterFD(self, fd, user_data=None):
|
1011
|
-
self.__fd_set.discard(fd)
|
1012
|
-
self.unregister(fd)
|
1013
|
-
# pylint: enable=unused-argument
|
1014
|
-
|
1015
|
-
class USBDeviceHandle(object):
|
1016
|
-
"""
|
1017
|
-
Represents an opened USB device.
|
1018
|
-
"""
|
1019
|
-
__handle = None
|
1020
|
-
__libusb_close = libusb1.libusb_close
|
1021
|
-
__USBError = USBError
|
1022
|
-
# pylint: disable=undefined-variable
|
1023
|
-
__USBErrorNoDevice = USBErrorNoDevice
|
1024
|
-
__USBErrorNotFound = USBErrorNotFound
|
1025
|
-
__USBErrorInterrupted = USBErrorInterrupted
|
1026
|
-
# pylint: enable=undefined-variable
|
1027
|
-
__set = set
|
1028
|
-
__KeyError = KeyError
|
1029
|
-
__sys = sys
|
1030
|
-
|
1031
|
-
def __init__(self, context, handle, device):
|
1032
|
-
"""
|
1033
|
-
You should not instanciate this class directly.
|
1034
|
-
Call "open" method on an USBDevice instance to get an USBDeviceHandle
|
1035
|
-
instance.
|
1036
|
-
"""
|
1037
|
-
self.__context = context
|
1038
|
-
# Weak reference to transfers about this device so we can clean up
|
1039
|
-
# before closing device.
|
1040
|
-
self.__transfer_set = WeakSet()
|
1041
|
-
# Strong references to inflight transfers so they do not get freed
|
1042
|
-
# even if user drops all strong references to them. If this instance
|
1043
|
-
# is garbage-collected, we close all transfers, so it's fine.
|
1044
|
-
self.__inflight = inflight = set()
|
1045
|
-
# XXX: For some reason, doing self.__inflight.{add|remove} inside
|
1046
|
-
# getTransfer causes extra intermediate python objects for each
|
1047
|
-
# allocated transfer. Storing them as properties solves this. Found
|
1048
|
-
# with objgraph.
|
1049
|
-
self.__inflight_add = inflight.add
|
1050
|
-
self.__inflight_remove = inflight.remove
|
1051
|
-
self.__handle = handle
|
1052
|
-
self.__device = device
|
1053
|
-
|
1054
|
-
def __del__(self):
|
1055
|
-
self.close()
|
1056
|
-
|
1057
|
-
def close(self):
|
1058
|
-
"""
|
1059
|
-
Close this handle. If not called explicitely, will be called by
|
1060
|
-
destructor.
|
1061
|
-
|
1062
|
-
This method cancels any in-flight transfer when it is called. As
|
1063
|
-
cancellation is not immediate, this method needs to let libusb handle
|
1064
|
-
events until transfers are actually cancelled.
|
1065
|
-
In multi-threaded programs, this can lead to stalls. To avoid this,
|
1066
|
-
do not close nor let GC collect a USBDeviceHandle which has in-flight
|
1067
|
-
transfers.
|
1068
|
-
"""
|
1069
|
-
handle = self.__handle
|
1070
|
-
if handle is not None:
|
1071
|
-
# Build a strong set from weak self.__transfer_set so we can doom
|
1072
|
-
# and close all contained transfers.
|
1073
|
-
# Because of backward compatibility, self.__transfer_set might be a
|
1074
|
-
# wrapper around WeakKeyDictionary. As it might be modified by gc,
|
1075
|
-
# we must pop until there is not key left instead of iterating over
|
1076
|
-
# it.
|
1077
|
-
weak_transfer_set = self.__transfer_set
|
1078
|
-
transfer_set = self.__set()
|
1079
|
-
while True:
|
1080
|
-
try:
|
1081
|
-
transfer = weak_transfer_set.pop()
|
1082
|
-
except self.__KeyError:
|
1083
|
-
break
|
1084
|
-
transfer_set.add(transfer)
|
1085
|
-
transfer.doom()
|
1086
|
-
inflight = self.__inflight
|
1087
|
-
for transfer in inflight:
|
1088
|
-
try:
|
1089
|
-
transfer.cancel()
|
1090
|
-
except (self.__USBErrorNotFound, self.__USBErrorNoDevice):
|
1091
|
-
pass
|
1092
|
-
while inflight:
|
1093
|
-
try:
|
1094
|
-
self.__context.handleEvents()
|
1095
|
-
except self.__USBErrorInterrupted:
|
1096
|
-
pass
|
1097
|
-
for transfer in transfer_set:
|
1098
|
-
transfer.close()
|
1099
|
-
self.__libusb_close(handle)
|
1100
|
-
self.__handle = None
|
1101
|
-
|
1102
|
-
def getDevice(self):
|
1103
|
-
"""
|
1104
|
-
Get an USBDevice instance for the device accessed through this handle.
|
1105
|
-
Useful for example to query its configurations.
|
1106
|
-
"""
|
1107
|
-
return self.__device
|
1108
|
-
|
1109
|
-
def getConfiguration(self):
|
1110
|
-
"""
|
1111
|
-
Get the current configuration number for this device.
|
1112
|
-
"""
|
1113
|
-
configuration = c_int()
|
1114
|
-
result = libusb1.libusb_get_configuration(
|
1115
|
-
self.__handle, byref(configuration),
|
1116
|
-
)
|
1117
|
-
mayRaiseUSBError(result)
|
1118
|
-
return configuration.value
|
1119
|
-
|
1120
|
-
def setConfiguration(self, configuration):
|
1121
|
-
"""
|
1122
|
-
Set the configuration number for this device.
|
1123
|
-
"""
|
1124
|
-
result = libusb1.libusb_set_configuration(self.__handle, configuration)
|
1125
|
-
mayRaiseUSBError(result)
|
1126
|
-
|
1127
|
-
def claimInterface(self, interface):
|
1128
|
-
"""
|
1129
|
-
Claim (= get exclusive access to) given interface number. Required to
|
1130
|
-
receive/send data.
|
1131
|
-
"""
|
1132
|
-
result = libusb1.libusb_claim_interface(self.__handle, interface)
|
1133
|
-
mayRaiseUSBError(result)
|
1134
|
-
|
1135
|
-
def releaseInterface(self, interface):
|
1136
|
-
"""
|
1137
|
-
Release interface, allowing another process to use it.
|
1138
|
-
"""
|
1139
|
-
result = libusb1.libusb_release_interface(self.__handle, interface)
|
1140
|
-
mayRaiseUSBError(result)
|
1141
|
-
|
1142
|
-
def setInterfaceAltSetting(self, interface, alt_setting):
|
1143
|
-
"""
|
1144
|
-
Set interface's alternative setting (both parameters are integers).
|
1145
|
-
"""
|
1146
|
-
result = libusb1.libusb_set_interface_alt_setting(
|
1147
|
-
self.__handle, interface, alt_setting,
|
1148
|
-
)
|
1149
|
-
mayRaiseUSBError(result)
|
1150
|
-
|
1151
|
-
def clearHalt(self, endpoint):
|
1152
|
-
"""
|
1153
|
-
Clear a halt state on given endpoint number.
|
1154
|
-
"""
|
1155
|
-
result = libusb1.libusb_clear_halt(self.__handle, endpoint)
|
1156
|
-
mayRaiseUSBError(result)
|
1157
|
-
|
1158
|
-
def resetDevice(self):
|
1159
|
-
"""
|
1160
|
-
Reinitialise current device.
|
1161
|
-
Attempts to restore current configuration & alt settings.
|
1162
|
-
If this fails, will result in a device disconnect & reconnect, so you
|
1163
|
-
have to close current device and rediscover it (notified by a
|
1164
|
-
ERROR_NOT_FOUND error code).
|
1165
|
-
"""
|
1166
|
-
result = libusb1.libusb_reset_device(self.__handle)
|
1167
|
-
mayRaiseUSBError(result)
|
1168
|
-
|
1169
|
-
def kernelDriverActive(self, interface):
|
1170
|
-
"""
|
1171
|
-
Tell whether a kernel driver is active on given interface number.
|
1172
|
-
"""
|
1173
|
-
result = libusb1.libusb_kernel_driver_active(self.__handle, interface)
|
1174
|
-
if result == 0:
|
1175
|
-
return False
|
1176
|
-
elif result == 1:
|
1177
|
-
return True
|
1178
|
-
raiseUSBError(result)
|
1179
|
-
|
1180
|
-
def detachKernelDriver(self, interface):
|
1181
|
-
"""
|
1182
|
-
Ask kernel driver to detach from given interface number.
|
1183
|
-
"""
|
1184
|
-
result = libusb1.libusb_detach_kernel_driver(self.__handle, interface)
|
1185
|
-
mayRaiseUSBError(result)
|
1186
|
-
|
1187
|
-
def attachKernelDriver(self, interface):
|
1188
|
-
"""
|
1189
|
-
Ask kernel driver to re-attach to given interface number.
|
1190
|
-
"""
|
1191
|
-
result = libusb1.libusb_attach_kernel_driver(self.__handle, interface)
|
1192
|
-
mayRaiseUSBError(result)
|
1193
|
-
|
1194
|
-
def setAutoDetachKernelDriver(self, enable):
|
1195
|
-
"""
|
1196
|
-
Control automatic kernel driver detach.
|
1197
|
-
enable (bool)
|
1198
|
-
True to enable auto-detach, False to disable it.
|
1199
|
-
"""
|
1200
|
-
result = libusb1.libusb_set_auto_detach_kernel_driver(
|
1201
|
-
self.__handle, bool(enable),
|
1202
|
-
)
|
1203
|
-
mayRaiseUSBError(result)
|
1204
|
-
|
1205
|
-
def getSupportedLanguageList(self):
|
1206
|
-
"""
|
1207
|
-
Return a list of USB language identifiers (as integers) supported by
|
1208
|
-
current device for its string descriptors.
|
1209
|
-
|
1210
|
-
Note: language identifiers seem (I didn't check them all...) very
|
1211
|
-
similar to windows language identifiers, so you may want to use
|
1212
|
-
locales.windows_locale to get an rfc3066 representation. The 5 standard
|
1213
|
-
HID language codes are missing though.
|
1214
|
-
"""
|
1215
|
-
descriptor_string = create_binary_buffer(STRING_LENGTH)
|
1216
|
-
result = libusb1.libusb_get_string_descriptor(
|
1217
|
-
self.__handle, 0, 0, descriptor_string, sizeof(descriptor_string),
|
1218
|
-
)
|
1219
|
-
# pylint: disable=undefined-variable
|
1220
|
-
if result == ERROR_PIPE:
|
1221
|
-
# pylint: enable=undefined-variable
|
1222
|
-
# From libusb_control_transfer doc:
|
1223
|
-
# control request not supported by the device
|
1224
|
-
return []
|
1225
|
-
mayRaiseUSBError(result)
|
1226
|
-
length = cast(descriptor_string, POINTER(c_ubyte))[0]
|
1227
|
-
langid_list = cast(descriptor_string, POINTER(c_uint16))
|
1228
|
-
result = []
|
1229
|
-
append = result.append
|
1230
|
-
for offset in xrange(1, length / 2):
|
1231
|
-
append(libusb1.libusb_le16_to_cpu(langid_list[offset]))
|
1232
|
-
return result
|
1233
|
-
|
1234
|
-
def getStringDescriptor(self, descriptor, lang_id):
|
1235
|
-
"""
|
1236
|
-
Fetch description string for given descriptor and in given language.
|
1237
|
-
Use getSupportedLanguageList to know which languages are available.
|
1238
|
-
Return value is an unicode string.
|
1239
|
-
Return None if there is no such descriptor on device.
|
1240
|
-
"""
|
1241
|
-
descriptor_string = create_binary_buffer(STRING_LENGTH)
|
1242
|
-
result = libusb1.libusb_get_string_descriptor(
|
1243
|
-
self.__handle, descriptor, lang_id, descriptor_string,
|
1244
|
-
sizeof(descriptor_string),
|
1245
|
-
)
|
1246
|
-
# pylint: disable=undefined-variable
|
1247
|
-
if result == ERROR_NOT_FOUND:
|
1248
|
-
# pylint: enable=undefined-variable
|
1249
|
-
return None
|
1250
|
-
mayRaiseUSBError(result)
|
1251
|
-
return descriptor_string.value.decode('UTF-16-LE')
|
1252
|
-
|
1253
|
-
def getASCIIStringDescriptor(self, descriptor):
|
1254
|
-
"""
|
1255
|
-
Fetch description string for given descriptor in first available
|
1256
|
-
language.
|
1257
|
-
Return value is an ASCII string.
|
1258
|
-
Return None if there is no such descriptor on device.
|
1259
|
-
"""
|
1260
|
-
descriptor_string = create_binary_buffer(STRING_LENGTH)
|
1261
|
-
result = libusb1.libusb_get_string_descriptor_ascii(
|
1262
|
-
self.__handle, descriptor, descriptor_string,
|
1263
|
-
sizeof(descriptor_string),
|
1264
|
-
)
|
1265
|
-
# pylint: disable=undefined-variable
|
1266
|
-
if result == ERROR_NOT_FOUND:
|
1267
|
-
# pylint: enable=undefined-variable
|
1268
|
-
return None
|
1269
|
-
mayRaiseUSBError(result)
|
1270
|
-
return descriptor_string.value.decode('ASCII')
|
1271
|
-
|
1272
|
-
# Sync I/O
|
1273
|
-
|
1274
|
-
def _controlTransfer(
|
1275
|
-
self, request_type, request, value, index, data, length, timeout):
|
1276
|
-
result = libusb1.libusb_control_transfer(
|
1277
|
-
self.__handle, request_type, request, value, index, data, length,
|
1278
|
-
timeout,
|
1279
|
-
)
|
1280
|
-
mayRaiseUSBError(result)
|
1281
|
-
return result
|
1282
|
-
|
1283
|
-
def controlWrite(
|
1284
|
-
self, request_type, request, value, index, data, timeout=0):
|
1285
|
-
"""
|
1286
|
-
Synchronous control write.
|
1287
|
-
request_type: request type bitmask (bmRequestType), see
|
1288
|
-
constants TYPE_* and RECIPIENT_*.
|
1289
|
-
request: request id (some values are standard).
|
1290
|
-
value, index, data: meaning is request-dependent.
|
1291
|
-
timeout: in milliseconds, how long to wait for device acknowledgement.
|
1292
|
-
Set to 0 to disable.
|
1293
|
-
|
1294
|
-
Returns the number of bytes actually sent.
|
1295
|
-
"""
|
1296
|
-
# pylint: disable=undefined-variable
|
1297
|
-
request_type = (request_type & ~ENDPOINT_DIR_MASK) | ENDPOINT_OUT
|
1298
|
-
# pylint: enable=undefined-variable
|
1299
|
-
data = create_binary_buffer(data)
|
1300
|
-
return self._controlTransfer(request_type, request, value, index, data,
|
1301
|
-
sizeof(data), timeout)
|
1302
|
-
|
1303
|
-
def controlRead(
|
1304
|
-
self, request_type, request, value, index, length, timeout=0):
|
1305
|
-
"""
|
1306
|
-
Synchronous control read.
|
1307
|
-
timeout: in milliseconds, how long to wait for data. Set to 0 to
|
1308
|
-
disable.
|
1309
|
-
See controlWrite for other parameters description.
|
1310
|
-
|
1311
|
-
Returns received data.
|
1312
|
-
"""
|
1313
|
-
# pylint: disable=undefined-variable
|
1314
|
-
request_type = (request_type & ~ENDPOINT_DIR_MASK) | ENDPOINT_IN
|
1315
|
-
# pylint: enable=undefined-variable
|
1316
|
-
data = create_binary_buffer(length)
|
1317
|
-
transferred = self._controlTransfer(
|
1318
|
-
request_type, request, value, index, data, length, timeout,
|
1319
|
-
)
|
1320
|
-
return data.raw[:transferred]
|
1321
|
-
|
1322
|
-
def _bulkTransfer(self, endpoint, data, length, timeout):
|
1323
|
-
transferred = c_int()
|
1324
|
-
result = libusb1.libusb_bulk_transfer(
|
1325
|
-
self.__handle, endpoint, data, length, byref(transferred), timeout,
|
1326
|
-
)
|
1327
|
-
mayRaiseUSBError(result)
|
1328
|
-
return transferred.value
|
1329
|
-
|
1330
|
-
def bulkWrite(self, endpoint, data, timeout=0):
|
1331
|
-
"""
|
1332
|
-
Synchronous bulk write.
|
1333
|
-
endpoint: endpoint to send data to.
|
1334
|
-
data: data to send.
|
1335
|
-
timeout: in milliseconds, how long to wait for device acknowledgement.
|
1336
|
-
Set to 0 to disable.
|
1337
|
-
|
1338
|
-
Returns the number of bytes actually sent.
|
1339
|
-
"""
|
1340
|
-
# pylint: disable=undefined-variable
|
1341
|
-
endpoint = (endpoint & ~ENDPOINT_DIR_MASK) | ENDPOINT_OUT
|
1342
|
-
# pylint: enable=undefined-variable
|
1343
|
-
data = create_binary_buffer(data)
|
1344
|
-
return self._bulkTransfer(endpoint, data, sizeof(data), timeout)
|
1345
|
-
|
1346
|
-
def bulkRead(self, endpoint, length, timeout=0):
|
1347
|
-
"""
|
1348
|
-
Synchronous bulk read.
|
1349
|
-
timeout: in milliseconds, how long to wait for data. Set to 0 to
|
1350
|
-
disable.
|
1351
|
-
See bulkWrite for other parameters description.
|
1352
|
-
|
1353
|
-
Returns received data.
|
1354
|
-
"""
|
1355
|
-
# pylint: disable=undefined-variable
|
1356
|
-
endpoint = (endpoint & ~ENDPOINT_DIR_MASK) | ENDPOINT_IN
|
1357
|
-
# pylint: enable=undefined-variable
|
1358
|
-
data = create_binary_buffer(length)
|
1359
|
-
transferred = self._bulkTransfer(endpoint, data, length, timeout)
|
1360
|
-
# pylint: disable=invalid-slice-index
|
1361
|
-
return data.raw[:transferred]
|
1362
|
-
# pylint: enable=invalid-slice-index
|
1363
|
-
|
1364
|
-
def _interruptTransfer(self, endpoint, data, length, timeout):
|
1365
|
-
transferred = c_int()
|
1366
|
-
result = libusb1.libusb_interrupt_transfer(
|
1367
|
-
self.__handle, endpoint, data, length, byref(transferred), timeout,
|
1368
|
-
)
|
1369
|
-
mayRaiseUSBError(result)
|
1370
|
-
return transferred.value
|
1371
|
-
|
1372
|
-
def interruptWrite(self, endpoint, data, timeout=0):
|
1373
|
-
"""
|
1374
|
-
Synchronous interrupt write.
|
1375
|
-
endpoint: endpoint to send data to.
|
1376
|
-
data: data to send.
|
1377
|
-
timeout: in milliseconds, how long to wait for device acknowledgement.
|
1378
|
-
Set to 0 to disable.
|
1379
|
-
|
1380
|
-
Returns the number of bytes actually sent.
|
1381
|
-
"""
|
1382
|
-
# pylint: disable=undefined-variable
|
1383
|
-
endpoint = (endpoint & ~ENDPOINT_DIR_MASK) | ENDPOINT_OUT
|
1384
|
-
# pylint: enable=undefined-variable
|
1385
|
-
data = create_binary_buffer(data)
|
1386
|
-
return self._interruptTransfer(endpoint, data, sizeof(data), timeout)
|
1387
|
-
|
1388
|
-
def interruptRead(self, endpoint, length, timeout=0):
|
1389
|
-
"""
|
1390
|
-
Synchronous interrupt write.
|
1391
|
-
timeout: in milliseconds, how long to wait for data. Set to 0 to
|
1392
|
-
disable.
|
1393
|
-
See interruptRead for other parameters description.
|
1394
|
-
|
1395
|
-
Returns received data.
|
1396
|
-
"""
|
1397
|
-
# pylint: disable=undefined-variable
|
1398
|
-
endpoint = (endpoint & ~ENDPOINT_DIR_MASK) | ENDPOINT_IN
|
1399
|
-
# pylint: enable=undefined-variable
|
1400
|
-
data = create_binary_buffer(length)
|
1401
|
-
transferred = self._interruptTransfer(endpoint, data, length, timeout)
|
1402
|
-
# pylint: disable=invalid-slice-index
|
1403
|
-
return data.raw[:transferred]
|
1404
|
-
# pylint: enable=invalid-slice-index
|
1405
|
-
|
1406
|
-
def getTransfer(self, iso_packets=0):
|
1407
|
-
"""
|
1408
|
-
Get an USBTransfer instance for asynchronous use.
|
1409
|
-
iso_packets: the number of isochronous transfer descriptors to
|
1410
|
-
allocate.
|
1411
|
-
"""
|
1412
|
-
result = USBTransfer(
|
1413
|
-
self.__handle, iso_packets,
|
1414
|
-
self.__inflight_add, self.__inflight_remove,
|
1415
|
-
)
|
1416
|
-
self.__transfer_set.add(result)
|
1417
|
-
return result
|
1418
|
-
|
1419
|
-
class USBConfiguration(object):
|
1420
|
-
def __init__(self, context, config):
|
1421
|
-
"""
|
1422
|
-
You should not instanciate this class directly.
|
1423
|
-
Call USBDevice methods to get instances of this class.
|
1424
|
-
"""
|
1425
|
-
if not isinstance(config, libusb1.libusb_config_descriptor):
|
1426
|
-
raise TypeError('Unexpected descriptor type.')
|
1427
|
-
self.__config = config
|
1428
|
-
self.__context = context
|
1429
|
-
|
1430
|
-
def getNumInterfaces(self):
|
1431
|
-
return self.__config.bNumInterfaces
|
1432
|
-
|
1433
|
-
__len__ = getNumInterfaces
|
1434
|
-
|
1435
|
-
def getConfigurationValue(self):
|
1436
|
-
return self.__config.bConfigurationValue
|
1437
|
-
|
1438
|
-
def getDescriptor(self):
|
1439
|
-
return self.__config.iConfiguration
|
1440
|
-
|
1441
|
-
def getAttributes(self):
|
1442
|
-
return self.__config.bmAttributes
|
1443
|
-
|
1444
|
-
def getMaxPower(self):
|
1445
|
-
"""
|
1446
|
-
Returns device's power consumption in mW.
|
1447
|
-
Beware of unit: USB descriptor uses 2mW increments, this method
|
1448
|
-
converts it to mW units.
|
1449
|
-
"""
|
1450
|
-
return self.__config.MaxPower * 2
|
1451
|
-
|
1452
|
-
def getExtra(self):
|
1453
|
-
"""
|
1454
|
-
Returns a list of extra (non-basic) descriptors (DFU, HID, ...).
|
1455
|
-
"""
|
1456
|
-
return libusb1.get_extra(self.__config)
|
1457
|
-
|
1458
|
-
def __iter__(self):
|
1459
|
-
"""
|
1460
|
-
Iterates over interfaces available in this configuration, yielding
|
1461
|
-
USBInterface instances.
|
1462
|
-
"""
|
1463
|
-
context = self.__context
|
1464
|
-
interface_list = self.__config.interface
|
1465
|
-
for interface_num in xrange(self.getNumInterfaces()):
|
1466
|
-
yield USBInterface(context, interface_list[interface_num])
|
1467
|
-
|
1468
|
-
# BBB
|
1469
|
-
iterInterfaces = __iter__
|
1470
|
-
|
1471
|
-
def __getitem__(self, interface):
|
1472
|
-
"""
|
1473
|
-
Returns an USBInterface instance.
|
1474
|
-
"""
|
1475
|
-
if not isinstance(interface, int):
|
1476
|
-
raise TypeError('interface parameter must be an integer')
|
1477
|
-
if not 0 <= interface < self.getNumInterfaces():
|
1478
|
-
raise IndexError('No such interface: %r' % (interface, ))
|
1479
|
-
return USBInterface(self.__context, self.__config.interface[interface])
|
1480
|
-
|
1481
|
-
# pylint: disable=interface-not-implemented
|
1482
|
-
class USBInterface(object):
|
1483
|
-
def __init__(self, context, interface):
|
1484
|
-
"""
|
1485
|
-
You should not instanciate this class directly.
|
1486
|
-
Call USBConfiguration methods to get instances of this class.
|
1487
|
-
"""
|
1488
|
-
if not isinstance(interface, libusb1.libusb_interface):
|
1489
|
-
raise TypeError('Unexpected descriptor type.')
|
1490
|
-
self.__interface = interface
|
1491
|
-
self.__context = context
|
1492
|
-
|
1493
|
-
def getNumSettings(self):
|
1494
|
-
return self.__interface.num_altsetting
|
1495
|
-
|
1496
|
-
__len__ = getNumSettings
|
1497
|
-
|
1498
|
-
def __iter__(self):
|
1499
|
-
"""
|
1500
|
-
Iterates over settings in this insterface, yielding
|
1501
|
-
USBInterfaceSetting instances.
|
1502
|
-
"""
|
1503
|
-
context = self.__context
|
1504
|
-
alt_setting_list = self.__interface.altsetting
|
1505
|
-
for alt_setting_num in xrange(self.getNumSettings()):
|
1506
|
-
yield USBInterfaceSetting(
|
1507
|
-
context, alt_setting_list[alt_setting_num])
|
1508
|
-
|
1509
|
-
# BBB
|
1510
|
-
iterSettings = __iter__
|
1511
|
-
|
1512
|
-
def __getitem__(self, alt_setting):
|
1513
|
-
"""
|
1514
|
-
Returns an USBInterfaceSetting instance.
|
1515
|
-
"""
|
1516
|
-
if not isinstance(alt_setting, int):
|
1517
|
-
raise TypeError('alt_setting parameter must be an integer')
|
1518
|
-
if not 0 <= alt_setting < self.getNumSettings():
|
1519
|
-
raise IndexError('No such setting: %r' % (alt_setting, ))
|
1520
|
-
return USBInterfaceSetting(
|
1521
|
-
self.__context, self.__interface.altsetting[alt_setting])
|
1522
|
-
# pylint: enable=interface-not-implemented
|
1523
|
-
|
1524
|
-
class USBInterfaceSetting(object):
|
1525
|
-
def __init__(self, context, alt_setting):
|
1526
|
-
"""
|
1527
|
-
You should not instanciate this class directly.
|
1528
|
-
Call USBDevice or USBInterface methods to get instances of this class.
|
1529
|
-
"""
|
1530
|
-
if not isinstance(alt_setting, libusb1.libusb_interface_descriptor):
|
1531
|
-
raise TypeError('Unexpected descriptor type.')
|
1532
|
-
self.__alt_setting = alt_setting
|
1533
|
-
self.__context = context
|
1534
|
-
|
1535
|
-
def getNumber(self):
|
1536
|
-
return self.__alt_setting.bInterfaceNumber
|
1537
|
-
|
1538
|
-
def getAlternateSetting(self):
|
1539
|
-
return self.__alt_setting.bAlternateSetting
|
1540
|
-
|
1541
|
-
def getNumEndpoints(self):
|
1542
|
-
return self.__alt_setting.bNumEndpoints
|
1543
|
-
|
1544
|
-
__len__ = getNumEndpoints
|
1545
|
-
|
1546
|
-
def getClass(self):
|
1547
|
-
return self.__alt_setting.bInterfaceClass
|
1548
|
-
|
1549
|
-
def getSubClass(self):
|
1550
|
-
return self.__alt_setting.bInterfaceSubClass
|
1551
|
-
|
1552
|
-
def getClassTuple(self):
|
1553
|
-
"""
|
1554
|
-
For convenience: class and subclass are probably often matched
|
1555
|
-
simultaneously.
|
1556
|
-
"""
|
1557
|
-
alt_setting = self.__alt_setting
|
1558
|
-
return (alt_setting.bInterfaceClass, alt_setting.bInterfaceSubClass)
|
1559
|
-
|
1560
|
-
# BBB
|
1561
|
-
getClassTupple = getClassTuple
|
1562
|
-
|
1563
|
-
def getProtocol(self):
|
1564
|
-
return self.__alt_setting.bInterfaceProtocol
|
1565
|
-
|
1566
|
-
def getDescriptor(self):
|
1567
|
-
return self.__alt_setting.iInterface
|
1568
|
-
|
1569
|
-
def getExtra(self):
|
1570
|
-
return libusb1.get_extra(self.__alt_setting)
|
1571
|
-
|
1572
|
-
def __iter__(self):
|
1573
|
-
"""
|
1574
|
-
Iterates over endpoints in this interface setting , yielding
|
1575
|
-
USBEndpoint instances.
|
1576
|
-
"""
|
1577
|
-
context = self.__context
|
1578
|
-
endpoint_list = self.__alt_setting.endpoint
|
1579
|
-
for endpoint_num in xrange(self.getNumEndpoints()):
|
1580
|
-
yield USBEndpoint(context, endpoint_list[endpoint_num])
|
1581
|
-
|
1582
|
-
# BBB
|
1583
|
-
iterEndpoints = __iter__
|
1584
|
-
|
1585
|
-
def __getitem__(self, endpoint):
|
1586
|
-
"""
|
1587
|
-
Returns an USBEndpoint instance.
|
1588
|
-
"""
|
1589
|
-
if not isinstance(endpoint, int):
|
1590
|
-
raise TypeError('endpoint parameter must be an integer')
|
1591
|
-
if not 0 <= endpoint < self.getNumEndpoints():
|
1592
|
-
raise ValueError('No such endpoint: %r' % (endpoint, ))
|
1593
|
-
return USBEndpoint(
|
1594
|
-
self.__context, self.__alt_setting.endpoint[endpoint])
|
1595
|
-
|
1596
|
-
class USBEndpoint(object):
|
1597
|
-
def __init__(self, context, endpoint):
|
1598
|
-
if not isinstance(endpoint, libusb1.libusb_endpoint_descriptor):
|
1599
|
-
raise TypeError('Unexpected descriptor type.')
|
1600
|
-
self.__endpoint = endpoint
|
1601
|
-
self.__context = context
|
1602
|
-
|
1603
|
-
def getAddress(self):
|
1604
|
-
return self.__endpoint.bEndpointAddress
|
1605
|
-
|
1606
|
-
def getAttributes(self):
|
1607
|
-
return self.__endpoint.bmAttributes
|
1608
|
-
|
1609
|
-
def getMaxPacketSize(self):
|
1610
|
-
return self.__endpoint.wMaxPacketSize
|
1611
|
-
|
1612
|
-
def getInterval(self):
|
1613
|
-
return self.__endpoint.bInterval
|
1614
|
-
|
1615
|
-
def getRefresh(self):
|
1616
|
-
return self.__endpoint.bRefresh
|
1617
|
-
|
1618
|
-
def getSyncAddress(self):
|
1619
|
-
return self.__endpoint.bSynchAddress
|
1620
|
-
|
1621
|
-
def getExtra(self):
|
1622
|
-
return libusb1.get_extra(self.__endpoint)
|
1623
|
-
|
1624
|
-
class USBDevice(object):
|
1625
|
-
"""
|
1626
|
-
Represents a USB device.
|
1627
|
-
"""
|
1628
|
-
|
1629
|
-
__configuration_descriptor_list = ()
|
1630
|
-
__libusb_unref_device = libusb1.libusb_unref_device
|
1631
|
-
__libusb_free_config_descriptor = libusb1.libusb_free_config_descriptor
|
1632
|
-
__byref = byref
|
1633
|
-
|
1634
|
-
def __init__(self, context, device_p, can_load_configuration=True):
|
1635
|
-
"""
|
1636
|
-
You should not instanciate this class directly.
|
1637
|
-
Call USBContext methods to receive instances of this class.
|
1638
|
-
"""
|
1639
|
-
self.__context = context
|
1640
|
-
libusb1.libusb_ref_device(device_p)
|
1641
|
-
self.device_p = device_p
|
1642
|
-
# Fetch device descriptor
|
1643
|
-
device_descriptor = libusb1.libusb_device_descriptor()
|
1644
|
-
result = libusb1.libusb_get_device_descriptor(
|
1645
|
-
device_p, byref(device_descriptor))
|
1646
|
-
mayRaiseUSBError(result)
|
1647
|
-
self.device_descriptor = device_descriptor
|
1648
|
-
if can_load_configuration:
|
1649
|
-
self.__configuration_descriptor_list = descriptor_list = []
|
1650
|
-
append = descriptor_list.append
|
1651
|
-
device_p = self.device_p
|
1652
|
-
for configuration_id in xrange(
|
1653
|
-
self.device_descriptor.bNumConfigurations):
|
1654
|
-
config = libusb1.libusb_config_descriptor_p()
|
1655
|
-
result = libusb1.libusb_get_config_descriptor(
|
1656
|
-
device_p, configuration_id, byref(config))
|
1657
|
-
# pylint: disable=undefined-variable
|
1658
|
-
if result == ERROR_NOT_FOUND:
|
1659
|
-
# pylint: enable=undefined-variable
|
1660
|
-
# Some devices (ex windows' root hubs) tell they have
|
1661
|
-
# one configuration, but they have no configuration
|
1662
|
-
# descriptor.
|
1663
|
-
continue
|
1664
|
-
mayRaiseUSBError(result)
|
1665
|
-
append(config.contents)
|
1666
|
-
|
1667
|
-
def __del__(self):
|
1668
|
-
self.__libusb_unref_device(self.device_p)
|
1669
|
-
# pylint: disable=redefined-outer-name
|
1670
|
-
byref = self.__byref
|
1671
|
-
# pylint: enable=redefined-outer-name
|
1672
|
-
for config in self.__configuration_descriptor_list:
|
1673
|
-
self.__libusb_free_config_descriptor(byref(config))
|
1674
|
-
|
1675
|
-
def __str__(self):
|
1676
|
-
return 'Bus %03i Device %03i: ID %04x:%04x' % (
|
1677
|
-
self.getBusNumber(),
|
1678
|
-
self.getDeviceAddress(),
|
1679
|
-
self.getVendorID(),
|
1680
|
-
self.getProductID(),
|
1681
|
-
)
|
1682
|
-
|
1683
|
-
def __len__(self):
|
1684
|
-
return len(self.__configuration_descriptor_list)
|
1685
|
-
|
1686
|
-
def __getitem__(self, index):
|
1687
|
-
return USBConfiguration(
|
1688
|
-
self.__context, self.__configuration_descriptor_list[index])
|
1689
|
-
|
1690
|
-
def __key(self):
|
1691
|
-
return (
|
1692
|
-
id(self.__context), self.getBusNumber(),
|
1693
|
-
self.getDeviceAddress(), self.getVendorID(),
|
1694
|
-
self.getProductID(),
|
1695
|
-
)
|
1696
|
-
|
1697
|
-
def __hash__(self):
|
1698
|
-
return hash(self.__key())
|
1699
|
-
|
1700
|
-
def __eq__(self, other):
|
1701
|
-
return type(self) == type(other) and (
|
1702
|
-
self.device_p == other.device_p or
|
1703
|
-
# pylint: disable=protected-access
|
1704
|
-
self.__key() == other.__key()
|
1705
|
-
# pylint: enable=protected-access
|
1706
|
-
)
|
1707
|
-
|
1708
|
-
def iterConfigurations(self):
|
1709
|
-
context = self.__context
|
1710
|
-
for config in self.__configuration_descriptor_list:
|
1711
|
-
yield USBConfiguration(context, config)
|
1712
|
-
|
1713
|
-
# BBB
|
1714
|
-
iterConfiguations = iterConfigurations
|
1715
|
-
|
1716
|
-
def iterSettings(self):
|
1717
|
-
for config in self.iterConfigurations():
|
1718
|
-
for interface in config:
|
1719
|
-
for setting in interface:
|
1720
|
-
yield setting
|
1721
|
-
|
1722
|
-
def getBusNumber(self):
|
1723
|
-
"""
|
1724
|
-
Get device's bus number.
|
1725
|
-
"""
|
1726
|
-
return libusb1.libusb_get_bus_number(self.device_p)
|
1727
|
-
|
1728
|
-
def getPortNumber(self):
|
1729
|
-
"""
|
1730
|
-
Get device's port number.
|
1731
|
-
"""
|
1732
|
-
return libusb1.libusb_get_port_number(self.device_p)
|
1733
|
-
|
1734
|
-
def getPortNumberList(self):
|
1735
|
-
"""
|
1736
|
-
Get the port number of each hub toward device.
|
1737
|
-
"""
|
1738
|
-
port_list = (c_uint8 * PATH_MAX_DEPTH)()
|
1739
|
-
result = libusb1.libusb_get_port_numbers(
|
1740
|
-
self.device_p, port_list, len(port_list))
|
1741
|
-
mayRaiseUSBError(result)
|
1742
|
-
return list(port_list[:result])
|
1743
|
-
|
1744
|
-
# TODO: wrap libusb_get_parent when/if libusb removes the need to be inside
|
1745
|
-
# a libusb_(get|free)_device_list block.
|
1746
|
-
|
1747
|
-
def getDeviceAddress(self):
|
1748
|
-
"""
|
1749
|
-
Get device's address on its bus.
|
1750
|
-
"""
|
1751
|
-
return libusb1.libusb_get_device_address(self.device_p)
|
1752
|
-
|
1753
|
-
def getbcdUSB(self):
|
1754
|
-
"""
|
1755
|
-
Get the USB spec version device complies to, in BCD format.
|
1756
|
-
"""
|
1757
|
-
return self.device_descriptor.bcdUSB
|
1758
|
-
|
1759
|
-
def getDeviceClass(self):
|
1760
|
-
"""
|
1761
|
-
Get device's class id.
|
1762
|
-
"""
|
1763
|
-
return self.device_descriptor.bDeviceClass
|
1764
|
-
|
1765
|
-
def getDeviceSubClass(self):
|
1766
|
-
"""
|
1767
|
-
Get device's subclass id.
|
1768
|
-
"""
|
1769
|
-
return self.device_descriptor.bDeviceSubClass
|
1770
|
-
|
1771
|
-
def getDeviceProtocol(self):
|
1772
|
-
"""
|
1773
|
-
Get device's protocol id.
|
1774
|
-
"""
|
1775
|
-
return self.device_descriptor.bDeviceProtocol
|
1776
|
-
|
1777
|
-
def getMaxPacketSize0(self):
|
1778
|
-
"""
|
1779
|
-
Get device's max packet size for endpoint 0 (control).
|
1780
|
-
"""
|
1781
|
-
return self.device_descriptor.bMaxPacketSize0
|
1782
|
-
|
1783
|
-
def getMaxPacketSize(self, endpoint):
|
1784
|
-
"""
|
1785
|
-
Get device's max packet size for given endpoint.
|
1786
|
-
|
1787
|
-
Warning: this function will not always give you the expected result.
|
1788
|
-
See https://libusb.org/ticket/77 . You should instead consult the
|
1789
|
-
endpoint descriptor of current configuration and alternate setting.
|
1790
|
-
"""
|
1791
|
-
result = libusb1.libusb_get_max_packet_size(self.device_p, endpoint)
|
1792
|
-
mayRaiseUSBError(result)
|
1793
|
-
return result
|
1794
|
-
|
1795
|
-
def getMaxISOPacketSize(self, endpoint):
|
1796
|
-
"""
|
1797
|
-
Get the maximum size for a single isochronous packet for given
|
1798
|
-
endpoint.
|
1799
|
-
|
1800
|
-
Warning: this function will not always give you the expected result.
|
1801
|
-
See https://libusb.org/ticket/77 . You should instead consult the
|
1802
|
-
endpoint descriptor of current configuration and alternate setting.
|
1803
|
-
"""
|
1804
|
-
result = libusb1.libusb_get_max_iso_packet_size(self.device_p, endpoint)
|
1805
|
-
mayRaiseUSBError(result)
|
1806
|
-
return result
|
1807
|
-
|
1808
|
-
def getVendorID(self):
|
1809
|
-
"""
|
1810
|
-
Get device's vendor id.
|
1811
|
-
"""
|
1812
|
-
return self.device_descriptor.idVendor
|
1813
|
-
|
1814
|
-
def getProductID(self):
|
1815
|
-
"""
|
1816
|
-
Get device's product id.
|
1817
|
-
"""
|
1818
|
-
return self.device_descriptor.idProduct
|
1819
|
-
|
1820
|
-
def getbcdDevice(self):
|
1821
|
-
"""
|
1822
|
-
Get device's release number.
|
1823
|
-
"""
|
1824
|
-
return self.device_descriptor.bcdDevice
|
1825
|
-
|
1826
|
-
def getSupportedLanguageList(self):
|
1827
|
-
"""
|
1828
|
-
Get the list of language ids device has string descriptors for.
|
1829
|
-
"""
|
1830
|
-
temp_handle = self.open()
|
1831
|
-
return temp_handle.getSupportedLanguageList()
|
1832
|
-
|
1833
|
-
def _getStringDescriptor(self, descriptor, lang_id):
|
1834
|
-
if descriptor == 0:
|
1835
|
-
result = None
|
1836
|
-
else:
|
1837
|
-
temp_handle = self.open()
|
1838
|
-
result = temp_handle.getStringDescriptor(descriptor, lang_id)
|
1839
|
-
return result
|
1840
|
-
|
1841
|
-
def _getASCIIStringDescriptor(self, descriptor):
|
1842
|
-
if descriptor == 0:
|
1843
|
-
result = None
|
1844
|
-
else:
|
1845
|
-
temp_handle = self.open()
|
1846
|
-
result = temp_handle.getASCIIStringDescriptor(descriptor)
|
1847
|
-
return result
|
1848
|
-
|
1849
|
-
def getManufacturer(self):
|
1850
|
-
"""
|
1851
|
-
Get device's manufaturer name.
|
1852
|
-
Note: opens the device temporarily.
|
1853
|
-
"""
|
1854
|
-
return self._getASCIIStringDescriptor(
|
1855
|
-
self.device_descriptor.iManufacturer)
|
1856
|
-
|
1857
|
-
def getProduct(self):
|
1858
|
-
"""
|
1859
|
-
Get device's product name.
|
1860
|
-
Note: opens the device temporarily.
|
1861
|
-
"""
|
1862
|
-
return self._getASCIIStringDescriptor(self.device_descriptor.iProduct)
|
1863
|
-
|
1864
|
-
def getSerialNumber(self):
|
1865
|
-
"""
|
1866
|
-
Get device's serial number.
|
1867
|
-
Note: opens the device temporarily.
|
1868
|
-
"""
|
1869
|
-
return self._getASCIIStringDescriptor(
|
1870
|
-
self.device_descriptor.iSerialNumber)
|
1871
|
-
|
1872
|
-
def getNumConfigurations(self):
|
1873
|
-
"""
|
1874
|
-
Get device's number of possible configurations.
|
1875
|
-
"""
|
1876
|
-
return self.device_descriptor.bNumConfigurations
|
1877
|
-
|
1878
|
-
def getDeviceSpeed(self):
|
1879
|
-
"""
|
1880
|
-
Get device's speed.
|
1881
|
-
|
1882
|
-
Returns one of:
|
1883
|
-
SPEED_UNKNOWN
|
1884
|
-
SPEED_LOW
|
1885
|
-
SPEED_FULL
|
1886
|
-
SPEED_HIGH
|
1887
|
-
SPEED_SUPER
|
1888
|
-
"""
|
1889
|
-
return libusb1.libusb_get_device_speed(self.device_p)
|
1890
|
-
|
1891
|
-
def open(self):
|
1892
|
-
"""
|
1893
|
-
Open device.
|
1894
|
-
Returns an USBDeviceHandle instance.
|
1895
|
-
"""
|
1896
|
-
handle = libusb1.libusb_device_handle_p()
|
1897
|
-
result = libusb1.libusb_open(self.device_p, byref(handle))
|
1898
|
-
mayRaiseUSBError(result)
|
1899
|
-
return USBDeviceHandle(self.__context, handle, self)
|
1900
|
-
|
1901
|
-
_zero_tv = libusb1.timeval(0, 0)
|
1902
|
-
_zero_tv_p = byref(_zero_tv)
|
1903
|
-
|
1904
|
-
class USBContext(object):
|
1905
|
-
"""
|
1906
|
-
libusb1 USB context.
|
1907
|
-
|
1908
|
-
Provides methods to enumerate & look up USB devices.
|
1909
|
-
Also provides access to global (device-independent) libusb1 functions.
|
1910
|
-
"""
|
1911
|
-
__libusb_exit = libusb1.libusb_exit
|
1912
|
-
__context_p = None
|
1913
|
-
__added_cb = None
|
1914
|
-
__removed_cb = None
|
1915
|
-
__libusb_set_pollfd_notifiers = libusb1.libusb_set_pollfd_notifiers
|
1916
|
-
|
1917
|
-
#pylint: disable=no-self-argument
|
1918
|
-
def _validContext(func):
|
1919
|
-
# Defined inside USBContext so we can access "self.__*".
|
1920
|
-
@functools.wraps(func)
|
1921
|
-
def wrapper(self, *args, **kw):
|
1922
|
-
# pylint: disable=protected-access
|
1923
|
-
self.__context_cond.acquire()
|
1924
|
-
self.__context_refcount += 1
|
1925
|
-
self.__context_cond.release()
|
1926
|
-
try:
|
1927
|
-
if self.__context_p is not None:
|
1928
|
-
#pylint: disable=not-callable
|
1929
|
-
return func(self, *args, **kw)
|
1930
|
-
#pylint: enable=not-callable
|
1931
|
-
finally:
|
1932
|
-
self.__context_cond.acquire()
|
1933
|
-
self.__context_refcount -= 1
|
1934
|
-
if not self.__context_refcount:
|
1935
|
-
self.__context_cond.notifyAll()
|
1936
|
-
self.__context_cond.release()
|
1937
|
-
# pylint: enable=protected-access
|
1938
|
-
return wrapper
|
1939
|
-
#pylint: enable=no-self-argument
|
1940
|
-
|
1941
|
-
def __init__(self):
|
1942
|
-
"""
|
1943
|
-
Create a new USB context.
|
1944
|
-
"""
|
1945
|
-
# Used to prevent an exit to cause a segfault if a concurrent thread
|
1946
|
-
# is still in libusb.
|
1947
|
-
self.__context_refcount = 0
|
1948
|
-
self.__context_cond = threading.Condition()
|
1949
|
-
context_p = libusb1.libusb_context_p()
|
1950
|
-
result = libusb1.libusb_init(byref(context_p))
|
1951
|
-
mayRaiseUSBError(result)
|
1952
|
-
self.__context_p = context_p
|
1953
|
-
self.__hotplug_callback_dict = {}
|
1954
|
-
|
1955
|
-
def __del__(self):
|
1956
|
-
# Avoid locking.
|
1957
|
-
# XXX: Assumes __del__ should not normally be called while any
|
1958
|
-
# instance's method is being executed. It seems unlikely (they hold a
|
1959
|
-
# reference to their instance).
|
1960
|
-
self._exit()
|
1961
|
-
|
1962
|
-
def exit(self):
|
1963
|
-
"""
|
1964
|
-
Close (destroy) this USB context.
|
1965
|
-
|
1966
|
-
When this method has been called, methods on its instance will
|
1967
|
-
become mosty no-ops, returning None.
|
1968
|
-
"""
|
1969
|
-
self.__context_cond.acquire()
|
1970
|
-
try:
|
1971
|
-
while self.__context_refcount and self.__context_p is not None:
|
1972
|
-
self.__context_cond.wait()
|
1973
|
-
self._exit()
|
1974
|
-
finally:
|
1975
|
-
self.__context_cond.notifyAll()
|
1976
|
-
self.__context_cond.release()
|
1977
|
-
|
1978
|
-
def _exit(self):
|
1979
|
-
context_p = self.__context_p
|
1980
|
-
if context_p is not None:
|
1981
|
-
self.__libusb_exit(context_p)
|
1982
|
-
self.__context_p = None
|
1983
|
-
self.__added_cb = None
|
1984
|
-
self.__removed_cb = None
|
1985
|
-
|
1986
|
-
@_validContext
|
1987
|
-
def getDeviceList(self, skip_on_access_error=False, skip_on_error=False):
|
1988
|
-
"""
|
1989
|
-
Return a list of all USB devices currently plugged in, as USBDevice
|
1990
|
-
instances.
|
1991
|
-
|
1992
|
-
skip_on_error (bool)
|
1993
|
-
If True, ignore devices which raise USBError.
|
1994
|
-
|
1995
|
-
skip_on_access_error (bool)
|
1996
|
-
DEPRECATED. Alias for skip_on_error.
|
1997
|
-
"""
|
1998
|
-
skip_on_error = skip_on_error or skip_on_access_error
|
1999
|
-
device_p_p = libusb1.libusb_device_p_p()
|
2000
|
-
libusb_device_p = libusb1.libusb_device_p
|
2001
|
-
device_list_len = libusb1.libusb_get_device_list(self.__context_p,
|
2002
|
-
byref(device_p_p))
|
2003
|
-
mayRaiseUSBError(device_list_len)
|
2004
|
-
try:
|
2005
|
-
result = []
|
2006
|
-
append = result.append
|
2007
|
-
for device_p in device_p_p[:device_list_len]:
|
2008
|
-
try:
|
2009
|
-
# Instanciate our own libusb_device_p object so we can free
|
2010
|
-
# libusb-provided device list. Is this a bug in ctypes that
|
2011
|
-
# it doesn't copy pointer value (=pointed memory address) ?
|
2012
|
-
# At least, it's not so convenient and forces using such
|
2013
|
-
# weird code.
|
2014
|
-
device = USBDevice(self, libusb_device_p(device_p.contents))
|
2015
|
-
except USBError:
|
2016
|
-
if not skip_on_error:
|
2017
|
-
raise
|
2018
|
-
else:
|
2019
|
-
append(device)
|
2020
|
-
finally:
|
2021
|
-
libusb1.libusb_free_device_list(device_p_p, 1)
|
2022
|
-
return result
|
2023
|
-
|
2024
|
-
def getByVendorIDAndProductID(
|
2025
|
-
self, vendor_id, product_id,
|
2026
|
-
skip_on_access_error=False, skip_on_error=False):
|
2027
|
-
"""
|
2028
|
-
Get the first USB device matching given vendor and product ids.
|
2029
|
-
Returns an USBDevice instance, or None if no present device match.
|
2030
|
-
skip_on_error (bool)
|
2031
|
-
(see getDeviceList)
|
2032
|
-
skip_on_access_error (bool)
|
2033
|
-
(see getDeviceList)
|
2034
|
-
"""
|
2035
|
-
for device in self.getDeviceList(
|
2036
|
-
skip_on_access_error=skip_on_access_error,
|
2037
|
-
skip_on_error=skip_on_error):
|
2038
|
-
if device.getVendorID() == vendor_id and \
|
2039
|
-
device.getProductID() == product_id:
|
2040
|
-
return device
|
2041
|
-
|
2042
|
-
def openByVendorIDAndProductID(
|
2043
|
-
self, vendor_id, product_id,
|
2044
|
-
skip_on_access_error=False, skip_on_error=False):
|
2045
|
-
"""
|
2046
|
-
Get the first USB device matching given vendor and product ids.
|
2047
|
-
Returns an USBDeviceHandle instance, or None if no present device
|
2048
|
-
match.
|
2049
|
-
skip_on_error (bool)
|
2050
|
-
(see getDeviceList)
|
2051
|
-
skip_on_access_error (bool)
|
2052
|
-
(see getDeviceList)
|
2053
|
-
"""
|
2054
|
-
result = self.getByVendorIDAndProductID(
|
2055
|
-
vendor_id, product_id,
|
2056
|
-
skip_on_access_error=skip_on_access_error,
|
2057
|
-
skip_on_error=skip_on_error)
|
2058
|
-
if result is not None:
|
2059
|
-
return result.open()
|
2060
|
-
|
2061
|
-
@_validContext
|
2062
|
-
def getPollFDList(self):
|
2063
|
-
"""
|
2064
|
-
Return file descriptors to be used to poll USB events.
|
2065
|
-
You should not have to call this method, unless you are integrating
|
2066
|
-
this class with a polling mechanism.
|
2067
|
-
"""
|
2068
|
-
pollfd_p_p = libusb1.libusb_get_pollfds(self.__context_p)
|
2069
|
-
if not pollfd_p_p:
|
2070
|
-
errno = get_errno()
|
2071
|
-
if errno:
|
2072
|
-
raise OSError(errno)
|
2073
|
-
else:
|
2074
|
-
# Assume not implemented
|
2075
|
-
raise NotImplementedError(
|
2076
|
-
'Your libusb does not seem to implement pollable FDs')
|
2077
|
-
try:
|
2078
|
-
result = []
|
2079
|
-
append = result.append
|
2080
|
-
fd_index = 0
|
2081
|
-
while pollfd_p_p[fd_index]:
|
2082
|
-
append((
|
2083
|
-
pollfd_p_p[fd_index].contents.fd,
|
2084
|
-
pollfd_p_p[fd_index].contents.events,
|
2085
|
-
))
|
2086
|
-
fd_index += 1
|
2087
|
-
finally:
|
2088
|
-
_free(pollfd_p_p)
|
2089
|
-
return result
|
2090
|
-
|
2091
|
-
@_validContext
|
2092
|
-
def handleEvents(self):
|
2093
|
-
"""
|
2094
|
-
Handle any pending event (blocking).
|
2095
|
-
See libusb1 documentation for details (there is a timeout, so it's
|
2096
|
-
not "really" blocking).
|
2097
|
-
"""
|
2098
|
-
result = libusb1.libusb_handle_events(self.__context_p)
|
2099
|
-
mayRaiseUSBError(result)
|
2100
|
-
|
2101
|
-
# TODO: handleEventsCompleted
|
2102
|
-
|
2103
|
-
@_validContext
|
2104
|
-
def handleEventsTimeout(self, tv=0):
|
2105
|
-
"""
|
2106
|
-
Handle any pending event.
|
2107
|
-
If tv is 0, will return immediately after handling already-pending
|
2108
|
-
events.
|
2109
|
-
Otherwise, defines the maximum amount of time to wait for events, in
|
2110
|
-
seconds.
|
2111
|
-
"""
|
2112
|
-
if tv is None:
|
2113
|
-
tv = 0
|
2114
|
-
tv_s = int(tv)
|
2115
|
-
tv = libusb1.timeval(tv_s, int((tv - tv_s) * 1000000))
|
2116
|
-
result = libusb1.libusb_handle_events_timeout(
|
2117
|
-
self.__context_p, byref(tv))
|
2118
|
-
mayRaiseUSBError(result)
|
2119
|
-
|
2120
|
-
# TODO: handleEventsTimeoutCompleted
|
2121
|
-
|
2122
|
-
@_validContext
|
2123
|
-
def setPollFDNotifiers(
|
2124
|
-
self, added_cb=None, removed_cb=None, user_data=None):
|
2125
|
-
"""
|
2126
|
-
Give libusb1 methods to call when it should add/remove file descriptor
|
2127
|
-
for polling.
|
2128
|
-
You should not have to call this method, unless you are integrating
|
2129
|
-
this class with a polling mechanism.
|
2130
|
-
"""
|
2131
|
-
if added_cb is None:
|
2132
|
-
added_cb = POINTER(None)
|
2133
|
-
else:
|
2134
|
-
added_cb = libusb1.libusb_pollfd_added_cb_p(added_cb)
|
2135
|
-
if removed_cb is None:
|
2136
|
-
removed_cb = POINTER(None)
|
2137
|
-
else:
|
2138
|
-
removed_cb = libusb1.libusb_pollfd_removed_cb_p(removed_cb)
|
2139
|
-
self.__added_cb = added_cb
|
2140
|
-
self.__removed_cb = removed_cb
|
2141
|
-
self.__libusb_set_pollfd_notifiers(
|
2142
|
-
self.__context_p, added_cb, removed_cb, user_data)
|
2143
|
-
|
2144
|
-
@_validContext
|
2145
|
-
def getNextTimeout(self):
|
2146
|
-
"""
|
2147
|
-
Returns the next internal timeout that libusb needs to handle, in
|
2148
|
-
seconds, or None if no timeout is needed.
|
2149
|
-
You should not have to call this method, unless you are integrating
|
2150
|
-
this class with a polling mechanism.
|
2151
|
-
"""
|
2152
|
-
timeval = libusb1.timeval()
|
2153
|
-
result = libusb1.libusb_get_next_timeout(
|
2154
|
-
self.__context_p, byref(timeval))
|
2155
|
-
if result == 0:
|
2156
|
-
return None
|
2157
|
-
elif result == 1:
|
2158
|
-
return timeval.tv_sec + (timeval.tv_usec * 0.000001)
|
2159
|
-
raiseUSBError(result)
|
2160
|
-
|
2161
|
-
@_validContext
|
2162
|
-
def setDebug(self, level):
|
2163
|
-
"""
|
2164
|
-
Set debugging level.
|
2165
|
-
Note: depending on libusb compilation settings, this might have no
|
2166
|
-
effect.
|
2167
|
-
"""
|
2168
|
-
libusb1.libusb_set_debug(self.__context_p, level)
|
2169
|
-
|
2170
|
-
@_validContext
|
2171
|
-
def tryLockEvents(self):
|
2172
|
-
"""
|
2173
|
-
See libusb_try_lock_events doc.
|
2174
|
-
"""
|
2175
|
-
return libusb1.libusb_try_lock_events(self.__context_p)
|
2176
|
-
|
2177
|
-
@_validContext
|
2178
|
-
def lockEvents(self):
|
2179
|
-
"""
|
2180
|
-
See libusb_lock_events doc.
|
2181
|
-
"""
|
2182
|
-
libusb1.libusb_lock_events(self.__context_p)
|
2183
|
-
|
2184
|
-
@_validContext
|
2185
|
-
def lockEventWaiters(self):
|
2186
|
-
"""
|
2187
|
-
See libusb_lock_event_waiters doc.
|
2188
|
-
"""
|
2189
|
-
libusb1.libusb_lock_event_waiters(self.__context_p)
|
2190
|
-
|
2191
|
-
@_validContext
|
2192
|
-
def waitForEvent(self, tv=0):
|
2193
|
-
"""
|
2194
|
-
See libusb_wait_for_event doc.
|
2195
|
-
"""
|
2196
|
-
if tv is None:
|
2197
|
-
tv = 0
|
2198
|
-
tv_s = int(tv)
|
2199
|
-
tv = libusb1.timeval(tv_s, int((tv - tv_s) * 1000000))
|
2200
|
-
libusb1.libusb_wait_for_event(self.__context_p, byref(tv))
|
2201
|
-
|
2202
|
-
@_validContext
|
2203
|
-
def unlockEventWaiters(self):
|
2204
|
-
"""
|
2205
|
-
See libusb_unlock_event_waiters doc.
|
2206
|
-
"""
|
2207
|
-
libusb1.libusb_unlock_event_waiters(self.__context_p)
|
2208
|
-
|
2209
|
-
@_validContext
|
2210
|
-
def eventHandlingOK(self):
|
2211
|
-
"""
|
2212
|
-
See libusb_event_handling_ok doc.
|
2213
|
-
"""
|
2214
|
-
return libusb1.libusb_event_handling_ok(self.__context_p)
|
2215
|
-
|
2216
|
-
@_validContext
|
2217
|
-
def unlockEvents(self):
|
2218
|
-
"""
|
2219
|
-
See libusb_unlock_events doc.
|
2220
|
-
"""
|
2221
|
-
libusb1.libusb_unlock_events(self.__context_p)
|
2222
|
-
|
2223
|
-
@_validContext
|
2224
|
-
def handleEventsLocked(self):
|
2225
|
-
"""
|
2226
|
-
See libusb_handle_events_locked doc.
|
2227
|
-
"""
|
2228
|
-
# XXX: does tv parameter need to be exposed ?
|
2229
|
-
result = libusb1.libusb_handle_events_locked(
|
2230
|
-
self.__context_p, _zero_tv_p)
|
2231
|
-
mayRaiseUSBError(result)
|
2232
|
-
|
2233
|
-
@_validContext
|
2234
|
-
def eventHandlerActive(self):
|
2235
|
-
"""
|
2236
|
-
See libusb_event_handler_active doc.
|
2237
|
-
"""
|
2238
|
-
return libusb1.libusb_event_handler_active(self.__context_p)
|
2239
|
-
|
2240
|
-
@staticmethod
|
2241
|
-
def hasCapability(capability):
|
2242
|
-
"""
|
2243
|
-
Backward-compatibility alias for module-level hasCapability.
|
2244
|
-
"""
|
2245
|
-
return hasCapability(capability)
|
2246
|
-
|
2247
|
-
@_validContext
|
2248
|
-
def hotplugRegisterCallback(
|
2249
|
-
self, callback,
|
2250
|
-
# pylint: disable=undefined-variable
|
2251
|
-
events=HOTPLUG_EVENT_DEVICE_ARRIVED | HOTPLUG_EVENT_DEVICE_LEFT,
|
2252
|
-
flags=HOTPLUG_ENUMERATE,
|
2253
|
-
vendor_id=HOTPLUG_MATCH_ANY,
|
2254
|
-
product_id=HOTPLUG_MATCH_ANY,
|
2255
|
-
dev_class=HOTPLUG_MATCH_ANY,
|
2256
|
-
# pylint: enable=undefined-variable
|
2257
|
-
):
|
2258
|
-
"""
|
2259
|
-
Registers an hotplug callback.
|
2260
|
-
On success, returns an opaque value which can be passed to
|
2261
|
-
hotplugDeregisterCallback.
|
2262
|
-
Callback must accept the following positional arguments:
|
2263
|
-
- this USBContext instance
|
2264
|
-
- an USBDevice instance
|
2265
|
-
If device has left, configuration descriptors may not be
|
2266
|
-
available. Its device descriptor will be available.
|
2267
|
-
- event type, one of:
|
2268
|
-
HOTPLUG_EVENT_DEVICE_ARRIVED
|
2269
|
-
HOTPLUG_EVENT_DEVICE_LEFT
|
2270
|
-
Callback must return whether it must be unregistered (any true value
|
2271
|
-
to be unregistered, any false value to be kept registered).
|
2272
|
-
"""
|
2273
|
-
def wrapped_callback(context_p, device_p, event, _):
|
2274
|
-
assert addressof(context_p.contents) == addressof(
|
2275
|
-
self.__context_p.contents), (context_p, self.__context_p)
|
2276
|
-
unregister = bool(callback(
|
2277
|
-
self,
|
2278
|
-
USBDevice(
|
2279
|
-
self,
|
2280
|
-
device_p,
|
2281
|
-
# pylint: disable=undefined-variable
|
2282
|
-
event != HOTPLUG_EVENT_DEVICE_LEFT,
|
2283
|
-
# pylint: enable=undefined-variable
|
2284
|
-
),
|
2285
|
-
event,
|
2286
|
-
))
|
2287
|
-
if unregister:
|
2288
|
-
del self.__hotplug_callback_dict[handle]
|
2289
|
-
return unregister
|
2290
|
-
handle = c_int()
|
2291
|
-
callback_p = libusb1.libusb_hotplug_callback_fn_p(wrapped_callback)
|
2292
|
-
result = libusb1.libusb_hotplug_register_callback(
|
2293
|
-
self.__context_p, events, flags, vendor_id, product_id, dev_class,
|
2294
|
-
callback_p, None, byref(handle))
|
2295
|
-
mayRaiseUSBError(result)
|
2296
|
-
handle = handle.value
|
2297
|
-
# Keep strong references
|
2298
|
-
assert handle not in self.__hotplug_callback_dict, (
|
2299
|
-
handle,
|
2300
|
-
self.__hotplug_callback_dict,
|
2301
|
-
)
|
2302
|
-
self.__hotplug_callback_dict[handle] = (callback_p, wrapped_callback)
|
2303
|
-
return handle
|
2304
|
-
|
2305
|
-
@_validContext
|
2306
|
-
def hotplugDeregisterCallback(self, handle):
|
2307
|
-
"""
|
2308
|
-
Deregisters an hotplug callback.
|
2309
|
-
handle (opaque)
|
2310
|
-
Return value of a former hotplugRegisterCallback call.
|
2311
|
-
"""
|
2312
|
-
del self.__hotplug_callback_dict[handle]
|
2313
|
-
libusb1.libusb_hotplug_deregister_callback(self.__context_p, handle)
|
2314
|
-
|
2315
|
-
del USBContext._validContext
|
2316
|
-
|
2317
|
-
def getVersion():
|
2318
|
-
"""
|
2319
|
-
Returns underlying libusb's version information as a 6-namedtuple (or
|
2320
|
-
6-tuple if namedtuples are not avaiable):
|
2321
|
-
- major
|
2322
|
-
- minor
|
2323
|
-
- micro
|
2324
|
-
- nano
|
2325
|
-
- rc
|
2326
|
-
- describe
|
2327
|
-
Returns (0, 0, 0, 0, '', '') if libusb doesn't have required entry point.
|
2328
|
-
"""
|
2329
|
-
version = libusb1.libusb_get_version().contents
|
2330
|
-
return Version(
|
2331
|
-
version.major,
|
2332
|
-
version.minor,
|
2333
|
-
version.micro,
|
2334
|
-
version.nano,
|
2335
|
-
version.rc,
|
2336
|
-
version.describe,
|
2337
|
-
)
|
2338
|
-
|
2339
|
-
def hasCapability(capability):
|
2340
|
-
"""
|
2341
|
-
Tests feature presence.
|
2342
|
-
|
2343
|
-
capability should be one of:
|
2344
|
-
CAP_HAS_CAPABILITY
|
2345
|
-
CAP_HAS_HOTPLUG
|
2346
|
-
CAP_HAS_HID_ACCESS
|
2347
|
-
CAP_SUPPORTS_DETACH_KERNEL_DRIVER
|
2348
|
-
"""
|
2349
|
-
return libusb1.libusb_has_capability(capability)
|
2350
|
-
|
2351
|
-
class LibUSBContext(USBContext):
|
2352
|
-
"""
|
2353
|
-
Backward-compatibility alias for USBContext.
|
2354
|
-
"""
|
2355
|
-
def __init__(self):
|
2356
|
-
warnings.warn(
|
2357
|
-
'LibUSBContext is being renamed to USBContext',
|
2358
|
-
DeprecationWarning,
|
2359
|
-
)
|
2360
|
-
super(LibUSBContext, self).__init__()
|
1
|
+
# Copyright (C) 2010-2015 Vincent Pelletier <plr.vincent@gmail.com>
|
2
|
+
#
|
3
|
+
# This library is free software; you can redistribute it and/or
|
4
|
+
# modify it under the terms of the GNU Lesser General Public
|
5
|
+
# License as published by the Free Software Foundation; either
|
6
|
+
# version 2.1 of the License, or (at your option) any later version.
|
7
|
+
#
|
8
|
+
# This library is distributed in the hope that it will be useful,
|
9
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
10
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
11
|
+
# Lesser General Public License for more details.
|
12
|
+
#
|
13
|
+
# You should have received a copy of the GNU Lesser General Public
|
14
|
+
# License along with this library; if not, write to the Free Software
|
15
|
+
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
16
|
+
|
17
|
+
# pylint: disable=invalid-name, too-many-locals, too-many-arguments
|
18
|
+
# pylint: disable=too-many-public-methods, too-many-instance-attributes
|
19
|
+
# pylint: disable=missing-docstring
|
20
|
+
"""
|
21
|
+
Pythonic wrapper for libusb-1.0.
|
22
|
+
|
23
|
+
The first thing you must do is to get an "USB context". To do so, create an
|
24
|
+
USBContext instance.
|
25
|
+
Then, you can use it to browse available USB devices and open the one you want
|
26
|
+
to talk to.
|
27
|
+
At this point, you should have a USBDeviceHandle instance (as returned by
|
28
|
+
USBContext or USBDevice instances), and you can start exchanging with the
|
29
|
+
device.
|
30
|
+
|
31
|
+
Features:
|
32
|
+
- Basic device settings (configuration & interface selection, ...)
|
33
|
+
- String descriptor lookups (ASCII & unicode), and list supported language
|
34
|
+
codes
|
35
|
+
- Synchronous I/O (control, bulk, interrupt)
|
36
|
+
- Asynchronous I/O (control, bulk, interrupt, isochronous)
|
37
|
+
Note: Isochronous support is not well tested.
|
38
|
+
See USBPoller, USBTransfer and USBTransferHelper.
|
39
|
+
|
40
|
+
All LIBUSB_* constants are available in this module, without the LIBUSB_
|
41
|
+
prefix - with one exception: LIBUSB_5GBPS_OPERATION is available as
|
42
|
+
SUPER_SPEED_OPERATION, so it is a valid python identifier.
|
43
|
+
|
44
|
+
All LIBUSB_ERROR_* constants are available in this module as exception classes,
|
45
|
+
subclassing USBError.
|
46
|
+
"""
|
47
|
+
|
48
|
+
import libusb1
|
49
|
+
from ctypes import byref, create_string_buffer, c_int, sizeof, POINTER, \
|
50
|
+
cast, c_uint8, c_uint16, c_ubyte, string_at, c_void_p, cdll, addressof
|
51
|
+
import sys
|
52
|
+
import threading
|
53
|
+
from ctypes.util import find_library
|
54
|
+
import warnings
|
55
|
+
import weakref
|
56
|
+
import collections
|
57
|
+
import functools
|
58
|
+
|
59
|
+
__all__ = [
|
60
|
+
'USBContext', 'USBDeviceHandle', 'USBDevice', 'hasCapability',
|
61
|
+
'USBPoller', 'USBTransfer', 'USBTransferHelper', 'EVENT_CALLBACK_SET',
|
62
|
+
'USBPollerThread', 'USBEndpoint', 'USBInterfaceSetting', 'USBInterface',
|
63
|
+
'USBConfiguration', 'DoomedTransferError', 'getVersion', 'USBError',
|
64
|
+
]
|
65
|
+
# Bind libusb1 constants and libusb1.USBError to this module, so user does not
|
66
|
+
# have to import two modules.
|
67
|
+
USBError = libusb1.USBError
|
68
|
+
STATUS_TO_EXCEPTION_DICT = {}
|
69
|
+
def __bindConstants():
|
70
|
+
global_dict = globals()
|
71
|
+
PREFIX = 'LIBUSB_'
|
72
|
+
for name, value in libusb1.__dict__.items():
|
73
|
+
if name.startswith(PREFIX):
|
74
|
+
name = name[len(PREFIX):]
|
75
|
+
# Gah.
|
76
|
+
if name == '5GBPS_OPERATION':
|
77
|
+
name = 'SUPER_SPEED_OPERATION'
|
78
|
+
assert name not in global_dict
|
79
|
+
global_dict[name] = value
|
80
|
+
__all__.append(name)
|
81
|
+
# Finer-grained exceptions.
|
82
|
+
for name, value in libusb1.libusb_error.forward_dict.items():
|
83
|
+
if value:
|
84
|
+
assert name.startswith(PREFIX + 'ERROR_'), name
|
85
|
+
if name == 'LIBUSB_ERROR_IO':
|
86
|
+
name = 'ErrorIO'
|
87
|
+
else:
|
88
|
+
name = ''.join(x.capitalize() for x in name.split('_')[1:])
|
89
|
+
name = 'USB' + name
|
90
|
+
assert name not in global_dict, name
|
91
|
+
assert value not in STATUS_TO_EXCEPTION_DICT
|
92
|
+
STATUS_TO_EXCEPTION_DICT[value] = global_dict[name] = type(
|
93
|
+
name,
|
94
|
+
(USBError, ),
|
95
|
+
{'value': value},
|
96
|
+
)
|
97
|
+
__all__.append(name)
|
98
|
+
__bindConstants()
|
99
|
+
del __bindConstants
|
100
|
+
|
101
|
+
def raiseUSBError(value):
|
102
|
+
raise STATUS_TO_EXCEPTION_DICT.get(value, USBError)(value)
|
103
|
+
|
104
|
+
def mayRaiseUSBError(value):
|
105
|
+
if value < 0:
|
106
|
+
raiseUSBError(value)
|
107
|
+
|
108
|
+
try:
|
109
|
+
namedtuple = collections.namedtuple
|
110
|
+
except AttributeError:
|
111
|
+
Version = lambda *x: x
|
112
|
+
else:
|
113
|
+
Version = namedtuple(
|
114
|
+
'Version',
|
115
|
+
['major', 'minor', 'micro', 'nano', 'rc', 'describe'],
|
116
|
+
)
|
117
|
+
|
118
|
+
if sys.version_info[0] == 3:
|
119
|
+
BYTE = bytes([0])
|
120
|
+
# pylint: disable=redefined-builtin
|
121
|
+
xrange = range
|
122
|
+
long = int
|
123
|
+
# pylint: enable=redefined-builtin
|
124
|
+
else:
|
125
|
+
BYTE = '\x00'
|
126
|
+
# pylint: disable=undefined-variable
|
127
|
+
CONTROL_SETUP = BYTE * CONTROL_SETUP_SIZE
|
128
|
+
# pylint: enable=undefined-variable
|
129
|
+
|
130
|
+
if sys.version_info[:2] >= (2, 6):
|
131
|
+
if sys.platform == 'win32':
|
132
|
+
from ctypes import get_last_error as get_errno
|
133
|
+
else:
|
134
|
+
from ctypes import get_errno
|
135
|
+
else:
|
136
|
+
def get_errno():
|
137
|
+
raise NotImplementedError(
|
138
|
+
'Your python version does not support errno/last_error'
|
139
|
+
)
|
140
|
+
|
141
|
+
__libc_name = find_library('c')
|
142
|
+
if __libc_name is None:
|
143
|
+
# Of course, will leak memory.
|
144
|
+
# Should we warn user ? How ?
|
145
|
+
_free = lambda x: None
|
146
|
+
else:
|
147
|
+
_free = getattr(cdll, __libc_name).free
|
148
|
+
del __libc_name
|
149
|
+
|
150
|
+
try:
|
151
|
+
WeakSet = weakref.WeakSet
|
152
|
+
except AttributeError:
|
153
|
+
# Python < 2.7: tiny wrapper around WeakKeyDictionary
|
154
|
+
class WeakSet(object):
|
155
|
+
def __init__(self):
|
156
|
+
self.__dict = weakref.WeakKeyDictionary()
|
157
|
+
|
158
|
+
def add(self, item):
|
159
|
+
self.__dict[item] = None
|
160
|
+
|
161
|
+
def pop(self):
|
162
|
+
return self.__dict.popitem()[0]
|
163
|
+
|
164
|
+
# Default string length
|
165
|
+
# From a comment in libusb-1.0: "Some devices choke on size > 255"
|
166
|
+
STRING_LENGTH = 255
|
167
|
+
|
168
|
+
# As of v3 of USB specs, there cannot be more than 7 hubs from controler to
|
169
|
+
# device.
|
170
|
+
PATH_MAX_DEPTH = 7
|
171
|
+
|
172
|
+
EVENT_CALLBACK_SET = frozenset((
|
173
|
+
# pylint: disable=undefined-variable
|
174
|
+
TRANSFER_COMPLETED,
|
175
|
+
TRANSFER_ERROR,
|
176
|
+
TRANSFER_TIMED_OUT,
|
177
|
+
TRANSFER_CANCELLED,
|
178
|
+
TRANSFER_STALL,
|
179
|
+
TRANSFER_NO_DEVICE,
|
180
|
+
TRANSFER_OVERFLOW,
|
181
|
+
# pylint: enable=undefined-variable
|
182
|
+
))
|
183
|
+
|
184
|
+
DEFAULT_ASYNC_TRANSFER_ERROR_CALLBACK = lambda x: False
|
185
|
+
|
186
|
+
def create_binary_buffer(init_or_size):
|
187
|
+
# Prevent ctypes from adding a trailing null char.
|
188
|
+
if isinstance(init_or_size, (int, long)):
|
189
|
+
result = create_string_buffer(init_or_size)
|
190
|
+
else:
|
191
|
+
result = create_string_buffer(init_or_size, len(init_or_size))
|
192
|
+
return result
|
193
|
+
|
194
|
+
class DoomedTransferError(Exception):
|
195
|
+
"""Exception raised when altering/submitting a doomed transfer."""
|
196
|
+
pass
|
197
|
+
|
198
|
+
class USBTransfer(object):
|
199
|
+
"""
|
200
|
+
USB asynchronous transfer control & data.
|
201
|
+
|
202
|
+
All modification methods will raise if called on a submitted transfer.
|
203
|
+
Methods noted as "should not be called on a submitted transfer" will not
|
204
|
+
prevent you from reading, but returned value is unspecified.
|
205
|
+
|
206
|
+
Note on user_data: because of pypy's current ctype restrictions, user_data
|
207
|
+
is not provided to C level, but is managed purely in python. It should
|
208
|
+
change nothing for you, unless you are looking at underlying C transfer
|
209
|
+
structure - which you should never have to.
|
210
|
+
"""
|
211
|
+
# Prevent garbage collector from freeing the free function before our
|
212
|
+
# instances, as we need it to property destruct them.
|
213
|
+
__libusb_free_transfer = libusb1.libusb_free_transfer
|
214
|
+
__libusb_cancel_transfer = libusb1.libusb_cancel_transfer
|
215
|
+
__USBError = USBError
|
216
|
+
# pylint: disable=undefined-variable
|
217
|
+
__USBErrorNotFound = USBErrorNotFound
|
218
|
+
# pylint: enable=undefined-variable
|
219
|
+
__transfer = None
|
220
|
+
__initialized = False
|
221
|
+
__submitted = False
|
222
|
+
__callback = None
|
223
|
+
__ctypesCallbackWrapper = None
|
224
|
+
__doomed = False
|
225
|
+
__user_data = None
|
226
|
+
__transfer_buffer = None
|
227
|
+
|
228
|
+
def __init__(self, handle, iso_packets, before_submit, after_completion):
|
229
|
+
"""
|
230
|
+
You should not instanciate this class directly.
|
231
|
+
Call "getTransfer" method on an USBDeviceHandle instance to get
|
232
|
+
instances of this class.
|
233
|
+
"""
|
234
|
+
if iso_packets < 0:
|
235
|
+
raise ValueError(
|
236
|
+
'Cannot request a negative number of iso packets.'
|
237
|
+
)
|
238
|
+
self.__handle = handle
|
239
|
+
self.__before_submit = before_submit
|
240
|
+
self.__after_completion = after_completion
|
241
|
+
self.__num_iso_packets = iso_packets
|
242
|
+
result = libusb1.libusb_alloc_transfer(iso_packets)
|
243
|
+
if not result:
|
244
|
+
# pylint: disable=undefined-variable
|
245
|
+
raise USBErrorNoMem
|
246
|
+
# pylint: enable=undefined-variable
|
247
|
+
self.__transfer = result
|
248
|
+
self.__ctypesCallbackWrapper = libusb1.libusb_transfer_cb_fn_p(
|
249
|
+
self.__callbackWrapper)
|
250
|
+
|
251
|
+
def close(self):
|
252
|
+
"""
|
253
|
+
Break reference cycles to allow instance to be garbage-collected.
|
254
|
+
Raises if called on a submitted transfer.
|
255
|
+
"""
|
256
|
+
if self.__submitted:
|
257
|
+
raise ValueError('Cannot close a submitted transfer')
|
258
|
+
self.doom()
|
259
|
+
self.__initialized = False
|
260
|
+
# Break possible external reference cycles
|
261
|
+
self.__callback = None
|
262
|
+
self.__user_data = None
|
263
|
+
# Break libusb_transfer reference cycles
|
264
|
+
self.__ctypesCallbackWrapper = None
|
265
|
+
# For some reason, overwriting callback is not enough to remove this
|
266
|
+
# reference cycle - though sometimes it works:
|
267
|
+
# self -> self.__dict__ -> libusb_transfer -> dict[x] -> dict[x] ->
|
268
|
+
# CThunkObject -> __callbackWrapper -> self
|
269
|
+
# So free transfer altogether.
|
270
|
+
if self.__transfer is not None:
|
271
|
+
self.__libusb_free_transfer(self.__transfer)
|
272
|
+
self.__transfer = None
|
273
|
+
self.__transfer_buffer = None
|
274
|
+
# Break USBDeviceHandle reference cycle
|
275
|
+
self.__before_submit = None
|
276
|
+
self.__after_completion = None
|
277
|
+
|
278
|
+
def doom(self):
|
279
|
+
"""
|
280
|
+
Prevent transfer from being submitted again.
|
281
|
+
"""
|
282
|
+
self.__doomed = True
|
283
|
+
|
284
|
+
def __del__(self):
|
285
|
+
if self.__transfer is not None:
|
286
|
+
try:
|
287
|
+
# If this doesn't raise, we're doomed; transfer was submitted,
|
288
|
+
# still python decided to garbage-collect this instance.
|
289
|
+
# Stick to libusb's documentation, and don't free the
|
290
|
+
# transfer. If interpreter is shutting down, kernel will
|
291
|
+
# reclaim memory anyway.
|
292
|
+
# Note: we can't prevent transfer's buffer from being
|
293
|
+
# garbage-collected as soon as there will be no remaining
|
294
|
+
# reference to transfer, so a segfault might happen anyway.
|
295
|
+
# Should we warn user ? How ?
|
296
|
+
self.cancel()
|
297
|
+
except self.__USBErrorNotFound:
|
298
|
+
# Transfer was not submitted, we can free it.
|
299
|
+
self.__libusb_free_transfer(self.__transfer)
|
300
|
+
|
301
|
+
# pylint: disable=unused-argument
|
302
|
+
def __callbackWrapper(self, transfer_p):
|
303
|
+
"""
|
304
|
+
Makes it possible for user-provided callback to alter transfer when
|
305
|
+
fired (ie, mark transfer as not submitted upon call).
|
306
|
+
"""
|
307
|
+
self.__submitted = False
|
308
|
+
self.__after_completion(self)
|
309
|
+
callback = self.__callback
|
310
|
+
if callback is not None:
|
311
|
+
callback(self)
|
312
|
+
if self.__doomed:
|
313
|
+
self.close()
|
314
|
+
# pylint: enable=unused-argument
|
315
|
+
|
316
|
+
def setCallback(self, callback):
|
317
|
+
"""
|
318
|
+
Change transfer's callback.
|
319
|
+
"""
|
320
|
+
self.__callback = callback
|
321
|
+
|
322
|
+
def getCallback(self):
|
323
|
+
"""
|
324
|
+
Get currently set callback.
|
325
|
+
"""
|
326
|
+
return self.__callback
|
327
|
+
|
328
|
+
def setControl(
|
329
|
+
self, request_type, request, value, index, buffer_or_len,
|
330
|
+
callback=None, user_data=None, timeout=0):
|
331
|
+
"""
|
332
|
+
Setup transfer for control use.
|
333
|
+
|
334
|
+
request_type, request, value, index
|
335
|
+
See USBDeviceHandle.controlWrite.
|
336
|
+
request_type defines transfer direction (see
|
337
|
+
ENDPOINT_OUT and ENDPOINT_IN)).
|
338
|
+
buffer_or_len
|
339
|
+
Either a string (when sending data), or expected data length (when
|
340
|
+
receiving data).
|
341
|
+
callback
|
342
|
+
Callback function to be invoked on transfer completion.
|
343
|
+
Called with transfer as parameter, return value ignored.
|
344
|
+
user_data
|
345
|
+
User data to pass to callback function.
|
346
|
+
timeout
|
347
|
+
Transfer timeout in milliseconds. 0 to disable.
|
348
|
+
"""
|
349
|
+
if self.__submitted:
|
350
|
+
raise ValueError('Cannot alter a submitted transfer')
|
351
|
+
if self.__doomed:
|
352
|
+
raise DoomedTransferError('Cannot reuse a doomed transfer')
|
353
|
+
if isinstance(buffer_or_len, (int, long)):
|
354
|
+
length = buffer_or_len
|
355
|
+
# pylint: disable=undefined-variable
|
356
|
+
string_buffer = create_binary_buffer(length + CONTROL_SETUP_SIZE)
|
357
|
+
# pylint: enable=undefined-variable
|
358
|
+
else:
|
359
|
+
length = len(buffer_or_len)
|
360
|
+
string_buffer = create_binary_buffer(CONTROL_SETUP + buffer_or_len)
|
361
|
+
self.__initialized = False
|
362
|
+
self.__transfer_buffer = string_buffer
|
363
|
+
self.__user_data = user_data
|
364
|
+
libusb1.libusb_fill_control_setup(
|
365
|
+
string_buffer, request_type, request, value, index, length)
|
366
|
+
libusb1.libusb_fill_control_transfer(
|
367
|
+
self.__transfer, self.__handle, string_buffer,
|
368
|
+
self.__ctypesCallbackWrapper, None, timeout)
|
369
|
+
self.__callback = callback
|
370
|
+
self.__initialized = True
|
371
|
+
|
372
|
+
def setBulk(
|
373
|
+
self, endpoint, buffer_or_len, callback=None, user_data=None,
|
374
|
+
timeout=0):
|
375
|
+
"""
|
376
|
+
Setup transfer for bulk use.
|
377
|
+
|
378
|
+
endpoint
|
379
|
+
Endpoint to submit transfer to. Defines transfer direction (see
|
380
|
+
ENDPOINT_OUT and ENDPOINT_IN)).
|
381
|
+
buffer_or_len
|
382
|
+
Either a string (when sending data), or expected data length (when
|
383
|
+
receiving data)
|
384
|
+
callback
|
385
|
+
Callback function to be invoked on transfer completion.
|
386
|
+
Called with transfer as parameter, return value ignored.
|
387
|
+
user_data
|
388
|
+
User data to pass to callback function.
|
389
|
+
timeout
|
390
|
+
Transfer timeout in milliseconds. 0 to disable.
|
391
|
+
"""
|
392
|
+
if self.__submitted:
|
393
|
+
raise ValueError('Cannot alter a submitted transfer')
|
394
|
+
if self.__doomed:
|
395
|
+
raise DoomedTransferError('Cannot reuse a doomed transfer')
|
396
|
+
string_buffer = create_binary_buffer(buffer_or_len)
|
397
|
+
self.__initialized = False
|
398
|
+
self.__transfer_buffer = string_buffer
|
399
|
+
self.__user_data = user_data
|
400
|
+
libusb1.libusb_fill_bulk_transfer(
|
401
|
+
self.__transfer, self.__handle, endpoint, string_buffer,
|
402
|
+
sizeof(string_buffer), self.__ctypesCallbackWrapper, None, timeout)
|
403
|
+
self.__callback = callback
|
404
|
+
self.__initialized = True
|
405
|
+
|
406
|
+
def setInterrupt(
|
407
|
+
self, endpoint, buffer_or_len, callback=None, user_data=None,
|
408
|
+
timeout=0):
|
409
|
+
"""
|
410
|
+
Setup transfer for interrupt use.
|
411
|
+
|
412
|
+
endpoint
|
413
|
+
Endpoint to submit transfer to. Defines transfer direction (see
|
414
|
+
ENDPOINT_OUT and ENDPOINT_IN)).
|
415
|
+
buffer_or_len
|
416
|
+
Either a string (when sending data), or expected data length (when
|
417
|
+
receiving data)
|
418
|
+
callback
|
419
|
+
Callback function to be invoked on transfer completion.
|
420
|
+
Called with transfer as parameter, return value ignored.
|
421
|
+
user_data
|
422
|
+
User data to pass to callback function.
|
423
|
+
timeout
|
424
|
+
Transfer timeout in milliseconds. 0 to disable.
|
425
|
+
"""
|
426
|
+
if self.__submitted:
|
427
|
+
raise ValueError('Cannot alter a submitted transfer')
|
428
|
+
if self.__doomed:
|
429
|
+
raise DoomedTransferError('Cannot reuse a doomed transfer')
|
430
|
+
string_buffer = create_binary_buffer(buffer_or_len)
|
431
|
+
self.__initialized = False
|
432
|
+
self.__transfer_buffer = string_buffer
|
433
|
+
self.__user_data = user_data
|
434
|
+
libusb1.libusb_fill_interrupt_transfer(
|
435
|
+
self.__transfer, self.__handle, endpoint, string_buffer,
|
436
|
+
sizeof(string_buffer), self.__ctypesCallbackWrapper, None, timeout)
|
437
|
+
self.__callback = callback
|
438
|
+
self.__initialized = True
|
439
|
+
|
440
|
+
def setIsochronous(
|
441
|
+
self, endpoint, buffer_or_len, callback=None,
|
442
|
+
user_data=None, timeout=0, iso_transfer_length_list=None):
|
443
|
+
"""
|
444
|
+
Setup transfer for isochronous use.
|
445
|
+
|
446
|
+
endpoint
|
447
|
+
Endpoint to submit transfer to. Defines transfer direction (see
|
448
|
+
ENDPOINT_OUT and ENDPOINT_IN)).
|
449
|
+
buffer_or_len
|
450
|
+
Either a string (when sending data), or expected data length (when
|
451
|
+
receiving data)
|
452
|
+
callback
|
453
|
+
Callback function to be invoked on transfer completion.
|
454
|
+
Called with transfer as parameter, return value ignored.
|
455
|
+
user_data
|
456
|
+
User data to pass to callback function.
|
457
|
+
timeout
|
458
|
+
Transfer timeout in milliseconds. 0 to disable.
|
459
|
+
iso_transfer_length_list
|
460
|
+
List of individual transfer sizes. If not provided, buffer_or_len
|
461
|
+
will be divided evenly among available transfers if possible, and
|
462
|
+
raise ValueError otherwise.
|
463
|
+
"""
|
464
|
+
if self.__submitted:
|
465
|
+
raise ValueError('Cannot alter a submitted transfer')
|
466
|
+
num_iso_packets = self.__num_iso_packets
|
467
|
+
if num_iso_packets == 0:
|
468
|
+
raise TypeError(
|
469
|
+
'This transfer canot be used for isochronous I/O. '
|
470
|
+
'You must get another one with a non-zero iso_packets '
|
471
|
+
'parameter.'
|
472
|
+
)
|
473
|
+
if self.__doomed:
|
474
|
+
raise DoomedTransferError('Cannot reuse a doomed transfer')
|
475
|
+
string_buffer = create_binary_buffer(buffer_or_len)
|
476
|
+
buffer_length = sizeof(string_buffer)
|
477
|
+
if iso_transfer_length_list is None:
|
478
|
+
iso_length, remainder = divmod(buffer_length, num_iso_packets)
|
479
|
+
if remainder:
|
480
|
+
raise ValueError(
|
481
|
+
'Buffer size %i cannot be evenly distributed among %i '
|
482
|
+
'transfers' % (
|
483
|
+
buffer_length,
|
484
|
+
num_iso_packets,
|
485
|
+
)
|
486
|
+
)
|
487
|
+
iso_transfer_length_list = [iso_length] * num_iso_packets
|
488
|
+
configured_iso_packets = len(iso_transfer_length_list)
|
489
|
+
if configured_iso_packets > num_iso_packets:
|
490
|
+
raise ValueError(
|
491
|
+
'Too many ISO transfer lengths (%i), there are '
|
492
|
+
'only %i ISO transfers available' % (
|
493
|
+
configured_iso_packets,
|
494
|
+
num_iso_packets,
|
495
|
+
)
|
496
|
+
)
|
497
|
+
if sum(iso_transfer_length_list) > buffer_length:
|
498
|
+
raise ValueError(
|
499
|
+
'ISO transfers too long (%i), there are only '
|
500
|
+
'%i bytes available' % (
|
501
|
+
sum(iso_transfer_length_list),
|
502
|
+
buffer_length,
|
503
|
+
)
|
504
|
+
)
|
505
|
+
transfer_p = self.__transfer
|
506
|
+
self.__initialized = False
|
507
|
+
self.__transfer_buffer = string_buffer
|
508
|
+
self.__user_data = user_data
|
509
|
+
libusb1.libusb_fill_iso_transfer(
|
510
|
+
transfer_p, self.__handle, endpoint, string_buffer, buffer_length,
|
511
|
+
configured_iso_packets, self.__ctypesCallbackWrapper, None,
|
512
|
+
timeout)
|
513
|
+
for length, iso_packet_desc in zip(
|
514
|
+
iso_transfer_length_list,
|
515
|
+
libusb1.get_iso_packet_list(transfer_p)):
|
516
|
+
if length <= 0:
|
517
|
+
raise ValueError(
|
518
|
+
'Negative/null length transfers are not possible.'
|
519
|
+
)
|
520
|
+
iso_packet_desc.length = length
|
521
|
+
self.__callback = callback
|
522
|
+
self.__initialized = True
|
523
|
+
|
524
|
+
def getType(self):
|
525
|
+
"""
|
526
|
+
Get transfer type.
|
527
|
+
|
528
|
+
Returns one of:
|
529
|
+
TRANSFER_TYPE_CONTROL
|
530
|
+
TRANSFER_TYPE_ISOCHRONOUS
|
531
|
+
TRANSFER_TYPE_BULK
|
532
|
+
TRANSFER_TYPE_INTERRUPT
|
533
|
+
"""
|
534
|
+
return self.__transfer.contents.type
|
535
|
+
|
536
|
+
def getEndpoint(self):
|
537
|
+
"""
|
538
|
+
Get endpoint.
|
539
|
+
"""
|
540
|
+
return self.__transfer.contents.endpoint
|
541
|
+
|
542
|
+
def getStatus(self):
|
543
|
+
"""
|
544
|
+
Get transfer status.
|
545
|
+
Should not be called on a submitted transfer.
|
546
|
+
"""
|
547
|
+
return self.__transfer.contents.status
|
548
|
+
|
549
|
+
def getActualLength(self):
|
550
|
+
"""
|
551
|
+
Get actually transfered data length.
|
552
|
+
Should not be called on a submitted transfer.
|
553
|
+
"""
|
554
|
+
return self.__transfer.contents.actual_length
|
555
|
+
|
556
|
+
def getBuffer(self):
|
557
|
+
"""
|
558
|
+
Get data buffer content.
|
559
|
+
Should not be called on a submitted transfer.
|
560
|
+
"""
|
561
|
+
transfer_p = self.__transfer
|
562
|
+
transfer = transfer_p.contents
|
563
|
+
# pylint: disable=undefined-variable
|
564
|
+
if transfer.type == TRANSFER_TYPE_CONTROL:
|
565
|
+
# pylint: enable=undefined-variable
|
566
|
+
result = libusb1.libusb_control_transfer_get_data(transfer_p)
|
567
|
+
else:
|
568
|
+
result = string_at(transfer.buffer, transfer.length)
|
569
|
+
return result
|
570
|
+
|
571
|
+
def getUserData(self):
|
572
|
+
"""
|
573
|
+
Retrieve user data provided on setup.
|
574
|
+
"""
|
575
|
+
return self.__user_data
|
576
|
+
|
577
|
+
def setUserData(self, user_data):
|
578
|
+
"""
|
579
|
+
Change user data.
|
580
|
+
"""
|
581
|
+
self.__user_data = user_data
|
582
|
+
|
583
|
+
def getISOBufferList(self):
|
584
|
+
"""
|
585
|
+
Get individual ISO transfer's buffer.
|
586
|
+
Returns a list with one item per ISO transfer, with their
|
587
|
+
individually-configured sizes.
|
588
|
+
Returned list is consistent with getISOSetupList return value.
|
589
|
+
Should not be called on a submitted transfer.
|
590
|
+
|
591
|
+
See also iterISO.
|
592
|
+
"""
|
593
|
+
transfer_p = self.__transfer
|
594
|
+
transfer = transfer_p.contents
|
595
|
+
# pylint: disable=undefined-variable
|
596
|
+
if transfer.type != TRANSFER_TYPE_ISOCHRONOUS:
|
597
|
+
# pylint: enable=undefined-variable
|
598
|
+
raise TypeError(
|
599
|
+
'This method cannot be called on non-iso transfers.'
|
600
|
+
)
|
601
|
+
return libusb1.get_iso_packet_buffer_list(transfer_p)
|
602
|
+
|
603
|
+
def getISOSetupList(self):
|
604
|
+
"""
|
605
|
+
Get individual ISO transfer's setup.
|
606
|
+
Returns a list of dicts, each containing an individual ISO transfer
|
607
|
+
parameters:
|
608
|
+
- length
|
609
|
+
- actual_length
|
610
|
+
- status
|
611
|
+
(see libusb1's API documentation for their signification)
|
612
|
+
Returned list is consistent with getISOBufferList return value.
|
613
|
+
Should not be called on a submitted transfer (except for 'length'
|
614
|
+
values).
|
615
|
+
"""
|
616
|
+
transfer_p = self.__transfer
|
617
|
+
transfer = transfer_p.contents
|
618
|
+
# pylint: disable=undefined-variable
|
619
|
+
if transfer.type != TRANSFER_TYPE_ISOCHRONOUS:
|
620
|
+
# pylint: enable=undefined-variable
|
621
|
+
raise TypeError(
|
622
|
+
'This method cannot be called on non-iso transfers.'
|
623
|
+
)
|
624
|
+
return [
|
625
|
+
{
|
626
|
+
'length': x.length,
|
627
|
+
'actual_length': x.actual_length,
|
628
|
+
'status': x.status,
|
629
|
+
}
|
630
|
+
for x in libusb1.get_iso_packet_list(transfer_p)
|
631
|
+
]
|
632
|
+
|
633
|
+
def iterISO(self):
|
634
|
+
"""
|
635
|
+
Generator yielding (status, buffer) for each isochornous transfer.
|
636
|
+
buffer is truncated to actual_length.
|
637
|
+
This is more efficient than calling both getISOBufferList and
|
638
|
+
getISOSetupList when receiving data.
|
639
|
+
Should not be called on a submitted transfer.
|
640
|
+
"""
|
641
|
+
transfer_p = self.__transfer
|
642
|
+
transfer = transfer_p.contents
|
643
|
+
# pylint: disable=undefined-variable
|
644
|
+
if transfer.type != TRANSFER_TYPE_ISOCHRONOUS:
|
645
|
+
# pylint: enable=undefined-variable
|
646
|
+
raise TypeError(
|
647
|
+
'This method cannot be called on non-iso transfers.'
|
648
|
+
)
|
649
|
+
buffer_position = transfer.buffer
|
650
|
+
for iso_transfer in libusb1.get_iso_packet_list(transfer_p):
|
651
|
+
yield (
|
652
|
+
iso_transfer.status,
|
653
|
+
string_at(buffer_position, iso_transfer.actual_length),
|
654
|
+
)
|
655
|
+
buffer_position += iso_transfer.length
|
656
|
+
|
657
|
+
def setBuffer(self, buffer_or_len):
|
658
|
+
"""
|
659
|
+
Replace buffer with a new one.
|
660
|
+
Allows resizing read buffer and replacing data sent.
|
661
|
+
Note: resizing is not allowed for isochronous buffer (use
|
662
|
+
setIsochronous).
|
663
|
+
Note: disallowed on control transfers (use setControl).
|
664
|
+
"""
|
665
|
+
if self.__submitted:
|
666
|
+
raise ValueError('Cannot alter a submitted transfer')
|
667
|
+
transfer = self.__transfer.contents
|
668
|
+
# pylint: disable=undefined-variable
|
669
|
+
if transfer.type == TRANSFER_TYPE_CONTROL:
|
670
|
+
# pylint: enable=undefined-variable
|
671
|
+
raise ValueError(
|
672
|
+
'To alter control transfer buffer, use setControl'
|
673
|
+
)
|
674
|
+
buff = create_binary_buffer(buffer_or_len)
|
675
|
+
# pylint: disable=undefined-variable
|
676
|
+
if transfer.type == TRANSFER_TYPE_ISOCHRONOUS and \
|
677
|
+
sizeof(buff) != transfer.length:
|
678
|
+
# pylint: enable=undefined-variable
|
679
|
+
raise ValueError(
|
680
|
+
'To alter isochronous transfer buffer length, use '
|
681
|
+
'setIsochronous'
|
682
|
+
)
|
683
|
+
self.__transfer_buffer = buff
|
684
|
+
transfer.buffer = cast(buff, c_void_p)
|
685
|
+
transfer.length = sizeof(buff)
|
686
|
+
|
687
|
+
def isSubmitted(self):
|
688
|
+
"""
|
689
|
+
Tells if this transfer is submitted and still pending.
|
690
|
+
"""
|
691
|
+
return self.__submitted
|
692
|
+
|
693
|
+
def submit(self):
|
694
|
+
"""
|
695
|
+
Submit transfer for asynchronous handling.
|
696
|
+
"""
|
697
|
+
if self.__submitted:
|
698
|
+
raise ValueError('Cannot submit a submitted transfer')
|
699
|
+
if not self.__initialized:
|
700
|
+
raise ValueError(
|
701
|
+
'Cannot submit a transfer until it has been initialized'
|
702
|
+
)
|
703
|
+
if self.__doomed:
|
704
|
+
raise DoomedTransferError('Cannot submit doomed transfer')
|
705
|
+
self.__before_submit(self)
|
706
|
+
self.__submitted = True
|
707
|
+
result = libusb1.libusb_submit_transfer(self.__transfer)
|
708
|
+
if result:
|
709
|
+
self.__after_completion(self)
|
710
|
+
self.__submitted = False
|
711
|
+
raiseUSBError(result)
|
712
|
+
|
713
|
+
def cancel(self):
|
714
|
+
"""
|
715
|
+
Cancel transfer.
|
716
|
+
Note: cancellation happens asynchronously, so you must wait for
|
717
|
+
TRANSFER_CANCELLED.
|
718
|
+
"""
|
719
|
+
if not self.__submitted:
|
720
|
+
# XXX: Workaround for a bug reported on libusb 1.0.8: calling
|
721
|
+
# libusb_cancel_transfer on a non-submitted transfer might
|
722
|
+
# trigger a segfault.
|
723
|
+
raise self.__USBErrorNotFound
|
724
|
+
result = self.__libusb_cancel_transfer(self.__transfer)
|
725
|
+
if result:
|
726
|
+
raise self.__USBError(result)
|
727
|
+
|
728
|
+
class USBTransferHelper(object):
|
729
|
+
"""
|
730
|
+
Simplifies subscribing to the same transfer over and over, and callback
|
731
|
+
handling:
|
732
|
+
- no need to read event status to execute apropriate code, just setup
|
733
|
+
different functions for each status code
|
734
|
+
- just return True instead of calling submit
|
735
|
+
- no need to check if transfer is doomed before submitting it again,
|
736
|
+
DoomedTransferError is caught.
|
737
|
+
|
738
|
+
Callbacks used in this class must follow the callback API described in
|
739
|
+
USBTransfer, and are expected to return a boolean:
|
740
|
+
- True if transfer is to be submitted again (to receive/send more data)
|
741
|
+
- False otherwise
|
742
|
+
|
743
|
+
Note: as per libusb1 specifications, isochronous transfer global state
|
744
|
+
might be TRANSFER_COMPLETED although some individual packets might
|
745
|
+
have an error status. You can check individual packet status by calling
|
746
|
+
getISOSetupList on transfer object in your callback.
|
747
|
+
"""
|
748
|
+
def __init__(self, transfer=None):
|
749
|
+
"""
|
750
|
+
Create a transfer callback dispatcher.
|
751
|
+
|
752
|
+
transfer parameter is deprecated. If provided, it will be equivalent
|
753
|
+
to:
|
754
|
+
helper = USBTransferHelper()
|
755
|
+
transfer.setCallback(helper)
|
756
|
+
and also allows using deprecated methods on this class (otherwise,
|
757
|
+
they raise AttributeError).
|
758
|
+
"""
|
759
|
+
if transfer is not None:
|
760
|
+
# Deprecated: to drop
|
761
|
+
self.__transfer = transfer
|
762
|
+
transfer.setCallback(self)
|
763
|
+
self.__event_callback_dict = {}
|
764
|
+
self.__errorCallback = DEFAULT_ASYNC_TRANSFER_ERROR_CALLBACK
|
765
|
+
|
766
|
+
def submit(self):
|
767
|
+
"""
|
768
|
+
Submit the asynchronous read request.
|
769
|
+
Deprecated. Use submit on transfer.
|
770
|
+
"""
|
771
|
+
# Deprecated: to drop
|
772
|
+
self.__transfer.submit()
|
773
|
+
|
774
|
+
def cancel(self):
|
775
|
+
"""
|
776
|
+
Cancel a pending read request.
|
777
|
+
Deprecated. Use cancel on transfer.
|
778
|
+
"""
|
779
|
+
# Deprecated: to drop
|
780
|
+
self.__transfer.cancel()
|
781
|
+
|
782
|
+
def setEventCallback(self, event, callback):
|
783
|
+
"""
|
784
|
+
Set a function to call for a given event.
|
785
|
+
event must be one of:
|
786
|
+
TRANSFER_COMPLETED
|
787
|
+
TRANSFER_ERROR
|
788
|
+
TRANSFER_TIMED_OUT
|
789
|
+
TRANSFER_CANCELLED
|
790
|
+
TRANSFER_STALL
|
791
|
+
TRANSFER_NO_DEVICE
|
792
|
+
TRANSFER_OVERFLOW
|
793
|
+
"""
|
794
|
+
if event not in EVENT_CALLBACK_SET:
|
795
|
+
raise ValueError('Unknown event %r.' % (event, ))
|
796
|
+
self.__event_callback_dict[event] = callback
|
797
|
+
|
798
|
+
def setDefaultCallback(self, callback):
|
799
|
+
"""
|
800
|
+
Set the function to call for event which don't have a specific callback
|
801
|
+
registered.
|
802
|
+
The initial default callback does nothing and returns False.
|
803
|
+
"""
|
804
|
+
self.__errorCallback = callback
|
805
|
+
|
806
|
+
def getEventCallback(self, event, default=None):
|
807
|
+
"""
|
808
|
+
Return the function registered to be called for given event identifier.
|
809
|
+
"""
|
810
|
+
return self.__event_callback_dict.get(event, default)
|
811
|
+
|
812
|
+
def __call__(self, transfer):
|
813
|
+
"""
|
814
|
+
Callback to set on transfers.
|
815
|
+
"""
|
816
|
+
if self.getEventCallback(transfer.getStatus(), self.__errorCallback)(
|
817
|
+
transfer):
|
818
|
+
try:
|
819
|
+
transfer.submit()
|
820
|
+
except DoomedTransferError:
|
821
|
+
pass
|
822
|
+
|
823
|
+
def isSubmited(self):
|
824
|
+
"""
|
825
|
+
Returns whether this reader is currently waiting for an event.
|
826
|
+
Deprecatd. Use isSubmitted on transfer.
|
827
|
+
"""
|
828
|
+
# Deprecated: to drop
|
829
|
+
return self.__transfer.isSubmitted()
|
830
|
+
|
831
|
+
class USBPollerThread(threading.Thread):
|
832
|
+
"""
|
833
|
+
Implements libusb1 documentation about threaded, asynchronous
|
834
|
+
applications.
|
835
|
+
In short, instanciate this class once (...per USBContext instance), call
|
836
|
+
start() on the instance, and do whatever you need.
|
837
|
+
This thread will be used to execute transfer completion callbacks, and you
|
838
|
+
are free to use libusb1's synchronous API in another thread, and can forget
|
839
|
+
about libusb1 file descriptors.
|
840
|
+
|
841
|
+
See http://libusb.sourceforge.net/api-1.0/mtasync.html .
|
842
|
+
"""
|
843
|
+
def __init__(self, context, poller, exc_callback=None):
|
844
|
+
"""
|
845
|
+
Create a poller thread for given context.
|
846
|
+
Warning: it will not check if another poller instance was already
|
847
|
+
present for that context, and will replace it.
|
848
|
+
|
849
|
+
poller
|
850
|
+
(same as USBPoller.__init__ "poller" parameter)
|
851
|
+
|
852
|
+
exc_callback (callable)
|
853
|
+
Called with a libusb_error value as single parameter when event
|
854
|
+
handling fails.
|
855
|
+
If not given, an USBError will be raised, interrupting the thread.
|
856
|
+
"""
|
857
|
+
super(USBPollerThread, self).__init__()
|
858
|
+
self.daemon = True
|
859
|
+
self.__context = context
|
860
|
+
self.__poller = poller
|
861
|
+
self.__fd_set = set()
|
862
|
+
context.setPollFDNotifiers(self._registerFD, self._unregisterFD)
|
863
|
+
for fd, events in context.getPollFDList():
|
864
|
+
self._registerFD(fd, events, None)
|
865
|
+
if exc_callback is not None:
|
866
|
+
self.exceptionHandler = exc_callback
|
867
|
+
|
868
|
+
def __del__(self):
|
869
|
+
self.__context.setPollFDNotifiers(None, None)
|
870
|
+
|
871
|
+
# pylint: disable=method-hidden
|
872
|
+
@staticmethod
|
873
|
+
def exceptionHandler(exc):
|
874
|
+
raise exc
|
875
|
+
# pylint: enable=method-hidden
|
876
|
+
|
877
|
+
def run(self):
|
878
|
+
# We expect quite some spinning in below loop, so move any unneeded
|
879
|
+
# operation out of it.
|
880
|
+
context = self.__context
|
881
|
+
poll = self.__poller.poll
|
882
|
+
try_lock_events = context.tryLockEvents
|
883
|
+
lock_event_waiters = context.lockEventWaiters
|
884
|
+
wait_for_event = context.waitForEvent
|
885
|
+
unlock_event_waiters = context.unlockEventWaiters
|
886
|
+
event_handling_ok = context.eventHandlingOK
|
887
|
+
unlock_events = context.unlockEvents
|
888
|
+
handle_events_locked = context.handleEventsLocked
|
889
|
+
event_handler_active = context.eventHandlerActive
|
890
|
+
getNextTimeout = context.getNextTimeout
|
891
|
+
exceptionHandler = self.exceptionHandler
|
892
|
+
fd_set = self.__fd_set
|
893
|
+
while fd_set:
|
894
|
+
if try_lock_events():
|
895
|
+
lock_event_waiters()
|
896
|
+
while event_handler_active():
|
897
|
+
wait_for_event()
|
898
|
+
unlock_event_waiters()
|
899
|
+
else:
|
900
|
+
try:
|
901
|
+
while event_handling_ok():
|
902
|
+
if poll(getNextTimeout()):
|
903
|
+
try:
|
904
|
+
handle_events_locked()
|
905
|
+
except USBError:
|
906
|
+
exceptionHandler(sys.exc_info()[1])
|
907
|
+
finally:
|
908
|
+
unlock_events()
|
909
|
+
|
910
|
+
def _registerFD(self, fd, events, _):
|
911
|
+
self.__poller.register(fd, events)
|
912
|
+
self.__fd_set.add(fd)
|
913
|
+
|
914
|
+
def _unregisterFD(self, fd, _):
|
915
|
+
self.__fd_set.discard(fd)
|
916
|
+
self.__poller.unregister(fd)
|
917
|
+
|
918
|
+
class USBPoller(object):
|
919
|
+
"""
|
920
|
+
Class allowing integration of USB event polling in a file-descriptor
|
921
|
+
monitoring event loop.
|
922
|
+
|
923
|
+
WARNING: Do not call "poll" from several threads concurently. Do not use
|
924
|
+
synchronous USB transfers in a thread while "poll" is running. Doing so
|
925
|
+
will result in unnecessarily long pauses in some threads. Opening and/or
|
926
|
+
closing devices while polling can cause race conditions to occur.
|
927
|
+
"""
|
928
|
+
def __init__(self, context, poller):
|
929
|
+
"""
|
930
|
+
Create a poller for given context.
|
931
|
+
Warning: it will not check if another poller instance was already
|
932
|
+
present for that context, and will replace it.
|
933
|
+
|
934
|
+
poller is a polling instance implementing the following methods:
|
935
|
+
- register(fd, event_flags)
|
936
|
+
event_flags have the same meaning as in poll API (POLLIN & POLLOUT)
|
937
|
+
- unregister(fd)
|
938
|
+
- poll(timeout)
|
939
|
+
timeout being a float in seconds, or negative/None if there is no
|
940
|
+
timeout.
|
941
|
+
It must return a list of (descriptor, event) pairs.
|
942
|
+
Note: USBPoller is itself a valid poller.
|
943
|
+
Note2: select.poll uses a timeout in milliseconds, for some reason
|
944
|
+
(all other select.* classes use seconds for timeout), so you should
|
945
|
+
wrap it to convert & round/truncate timeout.
|
946
|
+
"""
|
947
|
+
self.__context = context
|
948
|
+
self.__poller = poller
|
949
|
+
self.__fd_set = set()
|
950
|
+
context.setPollFDNotifiers(self._registerFD, self._unregisterFD)
|
951
|
+
for fd, events in context.getPollFDList():
|
952
|
+
self._registerFD(fd, events)
|
953
|
+
|
954
|
+
def __del__(self):
|
955
|
+
self.__context.setPollFDNotifiers(None, None)
|
956
|
+
|
957
|
+
def poll(self, timeout=None):
|
958
|
+
"""
|
959
|
+
Poll for events.
|
960
|
+
timeout can be a float in seconds, or None for no timeout.
|
961
|
+
Returns a list of (descriptor, event) pairs.
|
962
|
+
"""
|
963
|
+
next_usb_timeout = self.__context.getNextTimeout()
|
964
|
+
if timeout is None or timeout < 0:
|
965
|
+
usb_timeout = next_usb_timeout
|
966
|
+
elif next_usb_timeout:
|
967
|
+
usb_timeout = min(next_usb_timeout, timeout)
|
968
|
+
else:
|
969
|
+
usb_timeout = timeout
|
970
|
+
event_list = self.__poller.poll(usb_timeout)
|
971
|
+
if event_list:
|
972
|
+
fd_set = self.__fd_set
|
973
|
+
result = [(x, y) for x, y in event_list if x not in fd_set]
|
974
|
+
if len(result) != len(event_list):
|
975
|
+
self.__context.handleEventsTimeout()
|
976
|
+
else:
|
977
|
+
result = event_list
|
978
|
+
self.__context.handleEventsTimeout()
|
979
|
+
return result
|
980
|
+
|
981
|
+
def register(self, fd, events):
|
982
|
+
"""
|
983
|
+
Register an USB-unrelated fd to poller.
|
984
|
+
Convenience method.
|
985
|
+
"""
|
986
|
+
if fd in self.__fd_set:
|
987
|
+
raise ValueError(
|
988
|
+
'This fd is a special USB event fd, it cannot be polled.'
|
989
|
+
)
|
990
|
+
self.__poller.register(fd, events)
|
991
|
+
|
992
|
+
def unregister(self, fd):
|
993
|
+
"""
|
994
|
+
Unregister an USB-unrelated fd from poller.
|
995
|
+
Convenience method.
|
996
|
+
"""
|
997
|
+
if fd in self.__fd_set:
|
998
|
+
raise ValueError(
|
999
|
+
'This fd is a special USB event fd, it must stay registered.'
|
1000
|
+
)
|
1001
|
+
self.__poller.unregister(fd)
|
1002
|
+
|
1003
|
+
# pylint: disable=unused-argument
|
1004
|
+
def _registerFD(self, fd, events, user_data=None):
|
1005
|
+
self.register(fd, events)
|
1006
|
+
self.__fd_set.add(fd)
|
1007
|
+
# pylint: enable=unused-argument
|
1008
|
+
|
1009
|
+
# pylint: disable=unused-argument
|
1010
|
+
def _unregisterFD(self, fd, user_data=None):
|
1011
|
+
self.__fd_set.discard(fd)
|
1012
|
+
self.unregister(fd)
|
1013
|
+
# pylint: enable=unused-argument
|
1014
|
+
|
1015
|
+
class USBDeviceHandle(object):
|
1016
|
+
"""
|
1017
|
+
Represents an opened USB device.
|
1018
|
+
"""
|
1019
|
+
__handle = None
|
1020
|
+
__libusb_close = libusb1.libusb_close
|
1021
|
+
__USBError = USBError
|
1022
|
+
# pylint: disable=undefined-variable
|
1023
|
+
__USBErrorNoDevice = USBErrorNoDevice
|
1024
|
+
__USBErrorNotFound = USBErrorNotFound
|
1025
|
+
__USBErrorInterrupted = USBErrorInterrupted
|
1026
|
+
# pylint: enable=undefined-variable
|
1027
|
+
__set = set
|
1028
|
+
__KeyError = KeyError
|
1029
|
+
__sys = sys
|
1030
|
+
|
1031
|
+
def __init__(self, context, handle, device):
|
1032
|
+
"""
|
1033
|
+
You should not instanciate this class directly.
|
1034
|
+
Call "open" method on an USBDevice instance to get an USBDeviceHandle
|
1035
|
+
instance.
|
1036
|
+
"""
|
1037
|
+
self.__context = context
|
1038
|
+
# Weak reference to transfers about this device so we can clean up
|
1039
|
+
# before closing device.
|
1040
|
+
self.__transfer_set = WeakSet()
|
1041
|
+
# Strong references to inflight transfers so they do not get freed
|
1042
|
+
# even if user drops all strong references to them. If this instance
|
1043
|
+
# is garbage-collected, we close all transfers, so it's fine.
|
1044
|
+
self.__inflight = inflight = set()
|
1045
|
+
# XXX: For some reason, doing self.__inflight.{add|remove} inside
|
1046
|
+
# getTransfer causes extra intermediate python objects for each
|
1047
|
+
# allocated transfer. Storing them as properties solves this. Found
|
1048
|
+
# with objgraph.
|
1049
|
+
self.__inflight_add = inflight.add
|
1050
|
+
self.__inflight_remove = inflight.remove
|
1051
|
+
self.__handle = handle
|
1052
|
+
self.__device = device
|
1053
|
+
|
1054
|
+
def __del__(self):
|
1055
|
+
self.close()
|
1056
|
+
|
1057
|
+
def close(self):
|
1058
|
+
"""
|
1059
|
+
Close this handle. If not called explicitely, will be called by
|
1060
|
+
destructor.
|
1061
|
+
|
1062
|
+
This method cancels any in-flight transfer when it is called. As
|
1063
|
+
cancellation is not immediate, this method needs to let libusb handle
|
1064
|
+
events until transfers are actually cancelled.
|
1065
|
+
In multi-threaded programs, this can lead to stalls. To avoid this,
|
1066
|
+
do not close nor let GC collect a USBDeviceHandle which has in-flight
|
1067
|
+
transfers.
|
1068
|
+
"""
|
1069
|
+
handle = self.__handle
|
1070
|
+
if handle is not None:
|
1071
|
+
# Build a strong set from weak self.__transfer_set so we can doom
|
1072
|
+
# and close all contained transfers.
|
1073
|
+
# Because of backward compatibility, self.__transfer_set might be a
|
1074
|
+
# wrapper around WeakKeyDictionary. As it might be modified by gc,
|
1075
|
+
# we must pop until there is not key left instead of iterating over
|
1076
|
+
# it.
|
1077
|
+
weak_transfer_set = self.__transfer_set
|
1078
|
+
transfer_set = self.__set()
|
1079
|
+
while True:
|
1080
|
+
try:
|
1081
|
+
transfer = weak_transfer_set.pop()
|
1082
|
+
except self.__KeyError:
|
1083
|
+
break
|
1084
|
+
transfer_set.add(transfer)
|
1085
|
+
transfer.doom()
|
1086
|
+
inflight = self.__inflight
|
1087
|
+
for transfer in inflight:
|
1088
|
+
try:
|
1089
|
+
transfer.cancel()
|
1090
|
+
except (self.__USBErrorNotFound, self.__USBErrorNoDevice):
|
1091
|
+
pass
|
1092
|
+
while inflight:
|
1093
|
+
try:
|
1094
|
+
self.__context.handleEvents()
|
1095
|
+
except self.__USBErrorInterrupted:
|
1096
|
+
pass
|
1097
|
+
for transfer in transfer_set:
|
1098
|
+
transfer.close()
|
1099
|
+
self.__libusb_close(handle)
|
1100
|
+
self.__handle = None
|
1101
|
+
|
1102
|
+
def getDevice(self):
|
1103
|
+
"""
|
1104
|
+
Get an USBDevice instance for the device accessed through this handle.
|
1105
|
+
Useful for example to query its configurations.
|
1106
|
+
"""
|
1107
|
+
return self.__device
|
1108
|
+
|
1109
|
+
def getConfiguration(self):
|
1110
|
+
"""
|
1111
|
+
Get the current configuration number for this device.
|
1112
|
+
"""
|
1113
|
+
configuration = c_int()
|
1114
|
+
result = libusb1.libusb_get_configuration(
|
1115
|
+
self.__handle, byref(configuration),
|
1116
|
+
)
|
1117
|
+
mayRaiseUSBError(result)
|
1118
|
+
return configuration.value
|
1119
|
+
|
1120
|
+
def setConfiguration(self, configuration):
|
1121
|
+
"""
|
1122
|
+
Set the configuration number for this device.
|
1123
|
+
"""
|
1124
|
+
result = libusb1.libusb_set_configuration(self.__handle, configuration)
|
1125
|
+
mayRaiseUSBError(result)
|
1126
|
+
|
1127
|
+
def claimInterface(self, interface):
|
1128
|
+
"""
|
1129
|
+
Claim (= get exclusive access to) given interface number. Required to
|
1130
|
+
receive/send data.
|
1131
|
+
"""
|
1132
|
+
result = libusb1.libusb_claim_interface(self.__handle, interface)
|
1133
|
+
mayRaiseUSBError(result)
|
1134
|
+
|
1135
|
+
def releaseInterface(self, interface):
|
1136
|
+
"""
|
1137
|
+
Release interface, allowing another process to use it.
|
1138
|
+
"""
|
1139
|
+
result = libusb1.libusb_release_interface(self.__handle, interface)
|
1140
|
+
mayRaiseUSBError(result)
|
1141
|
+
|
1142
|
+
def setInterfaceAltSetting(self, interface, alt_setting):
|
1143
|
+
"""
|
1144
|
+
Set interface's alternative setting (both parameters are integers).
|
1145
|
+
"""
|
1146
|
+
result = libusb1.libusb_set_interface_alt_setting(
|
1147
|
+
self.__handle, interface, alt_setting,
|
1148
|
+
)
|
1149
|
+
mayRaiseUSBError(result)
|
1150
|
+
|
1151
|
+
def clearHalt(self, endpoint):
|
1152
|
+
"""
|
1153
|
+
Clear a halt state on given endpoint number.
|
1154
|
+
"""
|
1155
|
+
result = libusb1.libusb_clear_halt(self.__handle, endpoint)
|
1156
|
+
mayRaiseUSBError(result)
|
1157
|
+
|
1158
|
+
def resetDevice(self):
|
1159
|
+
"""
|
1160
|
+
Reinitialise current device.
|
1161
|
+
Attempts to restore current configuration & alt settings.
|
1162
|
+
If this fails, will result in a device disconnect & reconnect, so you
|
1163
|
+
have to close current device and rediscover it (notified by a
|
1164
|
+
ERROR_NOT_FOUND error code).
|
1165
|
+
"""
|
1166
|
+
result = libusb1.libusb_reset_device(self.__handle)
|
1167
|
+
mayRaiseUSBError(result)
|
1168
|
+
|
1169
|
+
def kernelDriverActive(self, interface):
|
1170
|
+
"""
|
1171
|
+
Tell whether a kernel driver is active on given interface number.
|
1172
|
+
"""
|
1173
|
+
result = libusb1.libusb_kernel_driver_active(self.__handle, interface)
|
1174
|
+
if result == 0:
|
1175
|
+
return False
|
1176
|
+
elif result == 1:
|
1177
|
+
return True
|
1178
|
+
raiseUSBError(result)
|
1179
|
+
|
1180
|
+
def detachKernelDriver(self, interface):
|
1181
|
+
"""
|
1182
|
+
Ask kernel driver to detach from given interface number.
|
1183
|
+
"""
|
1184
|
+
result = libusb1.libusb_detach_kernel_driver(self.__handle, interface)
|
1185
|
+
mayRaiseUSBError(result)
|
1186
|
+
|
1187
|
+
def attachKernelDriver(self, interface):
|
1188
|
+
"""
|
1189
|
+
Ask kernel driver to re-attach to given interface number.
|
1190
|
+
"""
|
1191
|
+
result = libusb1.libusb_attach_kernel_driver(self.__handle, interface)
|
1192
|
+
mayRaiseUSBError(result)
|
1193
|
+
|
1194
|
+
def setAutoDetachKernelDriver(self, enable):
|
1195
|
+
"""
|
1196
|
+
Control automatic kernel driver detach.
|
1197
|
+
enable (bool)
|
1198
|
+
True to enable auto-detach, False to disable it.
|
1199
|
+
"""
|
1200
|
+
result = libusb1.libusb_set_auto_detach_kernel_driver(
|
1201
|
+
self.__handle, bool(enable),
|
1202
|
+
)
|
1203
|
+
mayRaiseUSBError(result)
|
1204
|
+
|
1205
|
+
def getSupportedLanguageList(self):
|
1206
|
+
"""
|
1207
|
+
Return a list of USB language identifiers (as integers) supported by
|
1208
|
+
current device for its string descriptors.
|
1209
|
+
|
1210
|
+
Note: language identifiers seem (I didn't check them all...) very
|
1211
|
+
similar to windows language identifiers, so you may want to use
|
1212
|
+
locales.windows_locale to get an rfc3066 representation. The 5 standard
|
1213
|
+
HID language codes are missing though.
|
1214
|
+
"""
|
1215
|
+
descriptor_string = create_binary_buffer(STRING_LENGTH)
|
1216
|
+
result = libusb1.libusb_get_string_descriptor(
|
1217
|
+
self.__handle, 0, 0, descriptor_string, sizeof(descriptor_string),
|
1218
|
+
)
|
1219
|
+
# pylint: disable=undefined-variable
|
1220
|
+
if result == ERROR_PIPE:
|
1221
|
+
# pylint: enable=undefined-variable
|
1222
|
+
# From libusb_control_transfer doc:
|
1223
|
+
# control request not supported by the device
|
1224
|
+
return []
|
1225
|
+
mayRaiseUSBError(result)
|
1226
|
+
length = cast(descriptor_string, POINTER(c_ubyte))[0]
|
1227
|
+
langid_list = cast(descriptor_string, POINTER(c_uint16))
|
1228
|
+
result = []
|
1229
|
+
append = result.append
|
1230
|
+
for offset in xrange(1, length / 2):
|
1231
|
+
append(libusb1.libusb_le16_to_cpu(langid_list[offset]))
|
1232
|
+
return result
|
1233
|
+
|
1234
|
+
def getStringDescriptor(self, descriptor, lang_id):
|
1235
|
+
"""
|
1236
|
+
Fetch description string for given descriptor and in given language.
|
1237
|
+
Use getSupportedLanguageList to know which languages are available.
|
1238
|
+
Return value is an unicode string.
|
1239
|
+
Return None if there is no such descriptor on device.
|
1240
|
+
"""
|
1241
|
+
descriptor_string = create_binary_buffer(STRING_LENGTH)
|
1242
|
+
result = libusb1.libusb_get_string_descriptor(
|
1243
|
+
self.__handle, descriptor, lang_id, descriptor_string,
|
1244
|
+
sizeof(descriptor_string),
|
1245
|
+
)
|
1246
|
+
# pylint: disable=undefined-variable
|
1247
|
+
if result == ERROR_NOT_FOUND:
|
1248
|
+
# pylint: enable=undefined-variable
|
1249
|
+
return None
|
1250
|
+
mayRaiseUSBError(result)
|
1251
|
+
return descriptor_string.value.decode('UTF-16-LE')
|
1252
|
+
|
1253
|
+
def getASCIIStringDescriptor(self, descriptor):
|
1254
|
+
"""
|
1255
|
+
Fetch description string for given descriptor in first available
|
1256
|
+
language.
|
1257
|
+
Return value is an ASCII string.
|
1258
|
+
Return None if there is no such descriptor on device.
|
1259
|
+
"""
|
1260
|
+
descriptor_string = create_binary_buffer(STRING_LENGTH)
|
1261
|
+
result = libusb1.libusb_get_string_descriptor_ascii(
|
1262
|
+
self.__handle, descriptor, descriptor_string,
|
1263
|
+
sizeof(descriptor_string),
|
1264
|
+
)
|
1265
|
+
# pylint: disable=undefined-variable
|
1266
|
+
if result == ERROR_NOT_FOUND:
|
1267
|
+
# pylint: enable=undefined-variable
|
1268
|
+
return None
|
1269
|
+
mayRaiseUSBError(result)
|
1270
|
+
return descriptor_string.value.decode('ASCII')
|
1271
|
+
|
1272
|
+
# Sync I/O
|
1273
|
+
|
1274
|
+
def _controlTransfer(
|
1275
|
+
self, request_type, request, value, index, data, length, timeout):
|
1276
|
+
result = libusb1.libusb_control_transfer(
|
1277
|
+
self.__handle, request_type, request, value, index, data, length,
|
1278
|
+
timeout,
|
1279
|
+
)
|
1280
|
+
mayRaiseUSBError(result)
|
1281
|
+
return result
|
1282
|
+
|
1283
|
+
def controlWrite(
|
1284
|
+
self, request_type, request, value, index, data, timeout=0):
|
1285
|
+
"""
|
1286
|
+
Synchronous control write.
|
1287
|
+
request_type: request type bitmask (bmRequestType), see
|
1288
|
+
constants TYPE_* and RECIPIENT_*.
|
1289
|
+
request: request id (some values are standard).
|
1290
|
+
value, index, data: meaning is request-dependent.
|
1291
|
+
timeout: in milliseconds, how long to wait for device acknowledgement.
|
1292
|
+
Set to 0 to disable.
|
1293
|
+
|
1294
|
+
Returns the number of bytes actually sent.
|
1295
|
+
"""
|
1296
|
+
# pylint: disable=undefined-variable
|
1297
|
+
request_type = (request_type & ~ENDPOINT_DIR_MASK) | ENDPOINT_OUT
|
1298
|
+
# pylint: enable=undefined-variable
|
1299
|
+
data = create_binary_buffer(data)
|
1300
|
+
return self._controlTransfer(request_type, request, value, index, data,
|
1301
|
+
sizeof(data), timeout)
|
1302
|
+
|
1303
|
+
def controlRead(
|
1304
|
+
self, request_type, request, value, index, length, timeout=0):
|
1305
|
+
"""
|
1306
|
+
Synchronous control read.
|
1307
|
+
timeout: in milliseconds, how long to wait for data. Set to 0 to
|
1308
|
+
disable.
|
1309
|
+
See controlWrite for other parameters description.
|
1310
|
+
|
1311
|
+
Returns received data.
|
1312
|
+
"""
|
1313
|
+
# pylint: disable=undefined-variable
|
1314
|
+
request_type = (request_type & ~ENDPOINT_DIR_MASK) | ENDPOINT_IN
|
1315
|
+
# pylint: enable=undefined-variable
|
1316
|
+
data = create_binary_buffer(length)
|
1317
|
+
transferred = self._controlTransfer(
|
1318
|
+
request_type, request, value, index, data, length, timeout,
|
1319
|
+
)
|
1320
|
+
return data.raw[:transferred]
|
1321
|
+
|
1322
|
+
def _bulkTransfer(self, endpoint, data, length, timeout):
|
1323
|
+
transferred = c_int()
|
1324
|
+
result = libusb1.libusb_bulk_transfer(
|
1325
|
+
self.__handle, endpoint, data, length, byref(transferred), timeout,
|
1326
|
+
)
|
1327
|
+
mayRaiseUSBError(result)
|
1328
|
+
return transferred.value
|
1329
|
+
|
1330
|
+
def bulkWrite(self, endpoint, data, timeout=0):
|
1331
|
+
"""
|
1332
|
+
Synchronous bulk write.
|
1333
|
+
endpoint: endpoint to send data to.
|
1334
|
+
data: data to send.
|
1335
|
+
timeout: in milliseconds, how long to wait for device acknowledgement.
|
1336
|
+
Set to 0 to disable.
|
1337
|
+
|
1338
|
+
Returns the number of bytes actually sent.
|
1339
|
+
"""
|
1340
|
+
# pylint: disable=undefined-variable
|
1341
|
+
endpoint = (endpoint & ~ENDPOINT_DIR_MASK) | ENDPOINT_OUT
|
1342
|
+
# pylint: enable=undefined-variable
|
1343
|
+
data = create_binary_buffer(data)
|
1344
|
+
return self._bulkTransfer(endpoint, data, sizeof(data), timeout)
|
1345
|
+
|
1346
|
+
def bulkRead(self, endpoint, length, timeout=0):
|
1347
|
+
"""
|
1348
|
+
Synchronous bulk read.
|
1349
|
+
timeout: in milliseconds, how long to wait for data. Set to 0 to
|
1350
|
+
disable.
|
1351
|
+
See bulkWrite for other parameters description.
|
1352
|
+
|
1353
|
+
Returns received data.
|
1354
|
+
"""
|
1355
|
+
# pylint: disable=undefined-variable
|
1356
|
+
endpoint = (endpoint & ~ENDPOINT_DIR_MASK) | ENDPOINT_IN
|
1357
|
+
# pylint: enable=undefined-variable
|
1358
|
+
data = create_binary_buffer(length)
|
1359
|
+
transferred = self._bulkTransfer(endpoint, data, length, timeout)
|
1360
|
+
# pylint: disable=invalid-slice-index
|
1361
|
+
return data.raw[:transferred]
|
1362
|
+
# pylint: enable=invalid-slice-index
|
1363
|
+
|
1364
|
+
def _interruptTransfer(self, endpoint, data, length, timeout):
|
1365
|
+
transferred = c_int()
|
1366
|
+
result = libusb1.libusb_interrupt_transfer(
|
1367
|
+
self.__handle, endpoint, data, length, byref(transferred), timeout,
|
1368
|
+
)
|
1369
|
+
mayRaiseUSBError(result)
|
1370
|
+
return transferred.value
|
1371
|
+
|
1372
|
+
def interruptWrite(self, endpoint, data, timeout=0):
|
1373
|
+
"""
|
1374
|
+
Synchronous interrupt write.
|
1375
|
+
endpoint: endpoint to send data to.
|
1376
|
+
data: data to send.
|
1377
|
+
timeout: in milliseconds, how long to wait for device acknowledgement.
|
1378
|
+
Set to 0 to disable.
|
1379
|
+
|
1380
|
+
Returns the number of bytes actually sent.
|
1381
|
+
"""
|
1382
|
+
# pylint: disable=undefined-variable
|
1383
|
+
endpoint = (endpoint & ~ENDPOINT_DIR_MASK) | ENDPOINT_OUT
|
1384
|
+
# pylint: enable=undefined-variable
|
1385
|
+
data = create_binary_buffer(data)
|
1386
|
+
return self._interruptTransfer(endpoint, data, sizeof(data), timeout)
|
1387
|
+
|
1388
|
+
def interruptRead(self, endpoint, length, timeout=0):
|
1389
|
+
"""
|
1390
|
+
Synchronous interrupt write.
|
1391
|
+
timeout: in milliseconds, how long to wait for data. Set to 0 to
|
1392
|
+
disable.
|
1393
|
+
See interruptRead for other parameters description.
|
1394
|
+
|
1395
|
+
Returns received data.
|
1396
|
+
"""
|
1397
|
+
# pylint: disable=undefined-variable
|
1398
|
+
endpoint = (endpoint & ~ENDPOINT_DIR_MASK) | ENDPOINT_IN
|
1399
|
+
# pylint: enable=undefined-variable
|
1400
|
+
data = create_binary_buffer(length)
|
1401
|
+
transferred = self._interruptTransfer(endpoint, data, length, timeout)
|
1402
|
+
# pylint: disable=invalid-slice-index
|
1403
|
+
return data.raw[:transferred]
|
1404
|
+
# pylint: enable=invalid-slice-index
|
1405
|
+
|
1406
|
+
def getTransfer(self, iso_packets=0):
|
1407
|
+
"""
|
1408
|
+
Get an USBTransfer instance for asynchronous use.
|
1409
|
+
iso_packets: the number of isochronous transfer descriptors to
|
1410
|
+
allocate.
|
1411
|
+
"""
|
1412
|
+
result = USBTransfer(
|
1413
|
+
self.__handle, iso_packets,
|
1414
|
+
self.__inflight_add, self.__inflight_remove,
|
1415
|
+
)
|
1416
|
+
self.__transfer_set.add(result)
|
1417
|
+
return result
|
1418
|
+
|
1419
|
+
class USBConfiguration(object):
|
1420
|
+
def __init__(self, context, config):
|
1421
|
+
"""
|
1422
|
+
You should not instanciate this class directly.
|
1423
|
+
Call USBDevice methods to get instances of this class.
|
1424
|
+
"""
|
1425
|
+
if not isinstance(config, libusb1.libusb_config_descriptor):
|
1426
|
+
raise TypeError('Unexpected descriptor type.')
|
1427
|
+
self.__config = config
|
1428
|
+
self.__context = context
|
1429
|
+
|
1430
|
+
def getNumInterfaces(self):
|
1431
|
+
return self.__config.bNumInterfaces
|
1432
|
+
|
1433
|
+
__len__ = getNumInterfaces
|
1434
|
+
|
1435
|
+
def getConfigurationValue(self):
|
1436
|
+
return self.__config.bConfigurationValue
|
1437
|
+
|
1438
|
+
def getDescriptor(self):
|
1439
|
+
return self.__config.iConfiguration
|
1440
|
+
|
1441
|
+
def getAttributes(self):
|
1442
|
+
return self.__config.bmAttributes
|
1443
|
+
|
1444
|
+
def getMaxPower(self):
|
1445
|
+
"""
|
1446
|
+
Returns device's power consumption in mW.
|
1447
|
+
Beware of unit: USB descriptor uses 2mW increments, this method
|
1448
|
+
converts it to mW units.
|
1449
|
+
"""
|
1450
|
+
return self.__config.MaxPower * 2
|
1451
|
+
|
1452
|
+
def getExtra(self):
|
1453
|
+
"""
|
1454
|
+
Returns a list of extra (non-basic) descriptors (DFU, HID, ...).
|
1455
|
+
"""
|
1456
|
+
return libusb1.get_extra(self.__config)
|
1457
|
+
|
1458
|
+
def __iter__(self):
|
1459
|
+
"""
|
1460
|
+
Iterates over interfaces available in this configuration, yielding
|
1461
|
+
USBInterface instances.
|
1462
|
+
"""
|
1463
|
+
context = self.__context
|
1464
|
+
interface_list = self.__config.interface
|
1465
|
+
for interface_num in xrange(self.getNumInterfaces()):
|
1466
|
+
yield USBInterface(context, interface_list[interface_num])
|
1467
|
+
|
1468
|
+
# BBB
|
1469
|
+
iterInterfaces = __iter__
|
1470
|
+
|
1471
|
+
def __getitem__(self, interface):
|
1472
|
+
"""
|
1473
|
+
Returns an USBInterface instance.
|
1474
|
+
"""
|
1475
|
+
if not isinstance(interface, int):
|
1476
|
+
raise TypeError('interface parameter must be an integer')
|
1477
|
+
if not 0 <= interface < self.getNumInterfaces():
|
1478
|
+
raise IndexError('No such interface: %r' % (interface, ))
|
1479
|
+
return USBInterface(self.__context, self.__config.interface[interface])
|
1480
|
+
|
1481
|
+
# pylint: disable=interface-not-implemented
|
1482
|
+
class USBInterface(object):
|
1483
|
+
def __init__(self, context, interface):
|
1484
|
+
"""
|
1485
|
+
You should not instanciate this class directly.
|
1486
|
+
Call USBConfiguration methods to get instances of this class.
|
1487
|
+
"""
|
1488
|
+
if not isinstance(interface, libusb1.libusb_interface):
|
1489
|
+
raise TypeError('Unexpected descriptor type.')
|
1490
|
+
self.__interface = interface
|
1491
|
+
self.__context = context
|
1492
|
+
|
1493
|
+
def getNumSettings(self):
|
1494
|
+
return self.__interface.num_altsetting
|
1495
|
+
|
1496
|
+
__len__ = getNumSettings
|
1497
|
+
|
1498
|
+
def __iter__(self):
|
1499
|
+
"""
|
1500
|
+
Iterates over settings in this insterface, yielding
|
1501
|
+
USBInterfaceSetting instances.
|
1502
|
+
"""
|
1503
|
+
context = self.__context
|
1504
|
+
alt_setting_list = self.__interface.altsetting
|
1505
|
+
for alt_setting_num in xrange(self.getNumSettings()):
|
1506
|
+
yield USBInterfaceSetting(
|
1507
|
+
context, alt_setting_list[alt_setting_num])
|
1508
|
+
|
1509
|
+
# BBB
|
1510
|
+
iterSettings = __iter__
|
1511
|
+
|
1512
|
+
def __getitem__(self, alt_setting):
|
1513
|
+
"""
|
1514
|
+
Returns an USBInterfaceSetting instance.
|
1515
|
+
"""
|
1516
|
+
if not isinstance(alt_setting, int):
|
1517
|
+
raise TypeError('alt_setting parameter must be an integer')
|
1518
|
+
if not 0 <= alt_setting < self.getNumSettings():
|
1519
|
+
raise IndexError('No such setting: %r' % (alt_setting, ))
|
1520
|
+
return USBInterfaceSetting(
|
1521
|
+
self.__context, self.__interface.altsetting[alt_setting])
|
1522
|
+
# pylint: enable=interface-not-implemented
|
1523
|
+
|
1524
|
+
class USBInterfaceSetting(object):
|
1525
|
+
def __init__(self, context, alt_setting):
|
1526
|
+
"""
|
1527
|
+
You should not instanciate this class directly.
|
1528
|
+
Call USBDevice or USBInterface methods to get instances of this class.
|
1529
|
+
"""
|
1530
|
+
if not isinstance(alt_setting, libusb1.libusb_interface_descriptor):
|
1531
|
+
raise TypeError('Unexpected descriptor type.')
|
1532
|
+
self.__alt_setting = alt_setting
|
1533
|
+
self.__context = context
|
1534
|
+
|
1535
|
+
def getNumber(self):
|
1536
|
+
return self.__alt_setting.bInterfaceNumber
|
1537
|
+
|
1538
|
+
def getAlternateSetting(self):
|
1539
|
+
return self.__alt_setting.bAlternateSetting
|
1540
|
+
|
1541
|
+
def getNumEndpoints(self):
|
1542
|
+
return self.__alt_setting.bNumEndpoints
|
1543
|
+
|
1544
|
+
__len__ = getNumEndpoints
|
1545
|
+
|
1546
|
+
def getClass(self):
|
1547
|
+
return self.__alt_setting.bInterfaceClass
|
1548
|
+
|
1549
|
+
def getSubClass(self):
|
1550
|
+
return self.__alt_setting.bInterfaceSubClass
|
1551
|
+
|
1552
|
+
def getClassTuple(self):
|
1553
|
+
"""
|
1554
|
+
For convenience: class and subclass are probably often matched
|
1555
|
+
simultaneously.
|
1556
|
+
"""
|
1557
|
+
alt_setting = self.__alt_setting
|
1558
|
+
return (alt_setting.bInterfaceClass, alt_setting.bInterfaceSubClass)
|
1559
|
+
|
1560
|
+
# BBB
|
1561
|
+
getClassTupple = getClassTuple
|
1562
|
+
|
1563
|
+
def getProtocol(self):
|
1564
|
+
return self.__alt_setting.bInterfaceProtocol
|
1565
|
+
|
1566
|
+
def getDescriptor(self):
|
1567
|
+
return self.__alt_setting.iInterface
|
1568
|
+
|
1569
|
+
def getExtra(self):
|
1570
|
+
return libusb1.get_extra(self.__alt_setting)
|
1571
|
+
|
1572
|
+
def __iter__(self):
|
1573
|
+
"""
|
1574
|
+
Iterates over endpoints in this interface setting , yielding
|
1575
|
+
USBEndpoint instances.
|
1576
|
+
"""
|
1577
|
+
context = self.__context
|
1578
|
+
endpoint_list = self.__alt_setting.endpoint
|
1579
|
+
for endpoint_num in xrange(self.getNumEndpoints()):
|
1580
|
+
yield USBEndpoint(context, endpoint_list[endpoint_num])
|
1581
|
+
|
1582
|
+
# BBB
|
1583
|
+
iterEndpoints = __iter__
|
1584
|
+
|
1585
|
+
def __getitem__(self, endpoint):
|
1586
|
+
"""
|
1587
|
+
Returns an USBEndpoint instance.
|
1588
|
+
"""
|
1589
|
+
if not isinstance(endpoint, int):
|
1590
|
+
raise TypeError('endpoint parameter must be an integer')
|
1591
|
+
if not 0 <= endpoint < self.getNumEndpoints():
|
1592
|
+
raise ValueError('No such endpoint: %r' % (endpoint, ))
|
1593
|
+
return USBEndpoint(
|
1594
|
+
self.__context, self.__alt_setting.endpoint[endpoint])
|
1595
|
+
|
1596
|
+
class USBEndpoint(object):
|
1597
|
+
def __init__(self, context, endpoint):
|
1598
|
+
if not isinstance(endpoint, libusb1.libusb_endpoint_descriptor):
|
1599
|
+
raise TypeError('Unexpected descriptor type.')
|
1600
|
+
self.__endpoint = endpoint
|
1601
|
+
self.__context = context
|
1602
|
+
|
1603
|
+
def getAddress(self):
|
1604
|
+
return self.__endpoint.bEndpointAddress
|
1605
|
+
|
1606
|
+
def getAttributes(self):
|
1607
|
+
return self.__endpoint.bmAttributes
|
1608
|
+
|
1609
|
+
def getMaxPacketSize(self):
|
1610
|
+
return self.__endpoint.wMaxPacketSize
|
1611
|
+
|
1612
|
+
def getInterval(self):
|
1613
|
+
return self.__endpoint.bInterval
|
1614
|
+
|
1615
|
+
def getRefresh(self):
|
1616
|
+
return self.__endpoint.bRefresh
|
1617
|
+
|
1618
|
+
def getSyncAddress(self):
|
1619
|
+
return self.__endpoint.bSynchAddress
|
1620
|
+
|
1621
|
+
def getExtra(self):
|
1622
|
+
return libusb1.get_extra(self.__endpoint)
|
1623
|
+
|
1624
|
+
class USBDevice(object):
|
1625
|
+
"""
|
1626
|
+
Represents a USB device.
|
1627
|
+
"""
|
1628
|
+
|
1629
|
+
__configuration_descriptor_list = ()
|
1630
|
+
__libusb_unref_device = libusb1.libusb_unref_device
|
1631
|
+
__libusb_free_config_descriptor = libusb1.libusb_free_config_descriptor
|
1632
|
+
__byref = byref
|
1633
|
+
|
1634
|
+
def __init__(self, context, device_p, can_load_configuration=True):
|
1635
|
+
"""
|
1636
|
+
You should not instanciate this class directly.
|
1637
|
+
Call USBContext methods to receive instances of this class.
|
1638
|
+
"""
|
1639
|
+
self.__context = context
|
1640
|
+
libusb1.libusb_ref_device(device_p)
|
1641
|
+
self.device_p = device_p
|
1642
|
+
# Fetch device descriptor
|
1643
|
+
device_descriptor = libusb1.libusb_device_descriptor()
|
1644
|
+
result = libusb1.libusb_get_device_descriptor(
|
1645
|
+
device_p, byref(device_descriptor))
|
1646
|
+
mayRaiseUSBError(result)
|
1647
|
+
self.device_descriptor = device_descriptor
|
1648
|
+
if can_load_configuration:
|
1649
|
+
self.__configuration_descriptor_list = descriptor_list = []
|
1650
|
+
append = descriptor_list.append
|
1651
|
+
device_p = self.device_p
|
1652
|
+
for configuration_id in xrange(
|
1653
|
+
self.device_descriptor.bNumConfigurations):
|
1654
|
+
config = libusb1.libusb_config_descriptor_p()
|
1655
|
+
result = libusb1.libusb_get_config_descriptor(
|
1656
|
+
device_p, configuration_id, byref(config))
|
1657
|
+
# pylint: disable=undefined-variable
|
1658
|
+
if result == ERROR_NOT_FOUND:
|
1659
|
+
# pylint: enable=undefined-variable
|
1660
|
+
# Some devices (ex windows' root hubs) tell they have
|
1661
|
+
# one configuration, but they have no configuration
|
1662
|
+
# descriptor.
|
1663
|
+
continue
|
1664
|
+
mayRaiseUSBError(result)
|
1665
|
+
append(config.contents)
|
1666
|
+
|
1667
|
+
def __del__(self):
|
1668
|
+
self.__libusb_unref_device(self.device_p)
|
1669
|
+
# pylint: disable=redefined-outer-name
|
1670
|
+
byref = self.__byref
|
1671
|
+
# pylint: enable=redefined-outer-name
|
1672
|
+
for config in self.__configuration_descriptor_list:
|
1673
|
+
self.__libusb_free_config_descriptor(byref(config))
|
1674
|
+
|
1675
|
+
def __str__(self):
|
1676
|
+
return 'Bus %03i Device %03i: ID %04x:%04x' % (
|
1677
|
+
self.getBusNumber(),
|
1678
|
+
self.getDeviceAddress(),
|
1679
|
+
self.getVendorID(),
|
1680
|
+
self.getProductID(),
|
1681
|
+
)
|
1682
|
+
|
1683
|
+
def __len__(self):
|
1684
|
+
return len(self.__configuration_descriptor_list)
|
1685
|
+
|
1686
|
+
def __getitem__(self, index):
|
1687
|
+
return USBConfiguration(
|
1688
|
+
self.__context, self.__configuration_descriptor_list[index])
|
1689
|
+
|
1690
|
+
def __key(self):
|
1691
|
+
return (
|
1692
|
+
id(self.__context), self.getBusNumber(),
|
1693
|
+
self.getDeviceAddress(), self.getVendorID(),
|
1694
|
+
self.getProductID(),
|
1695
|
+
)
|
1696
|
+
|
1697
|
+
def __hash__(self):
|
1698
|
+
return hash(self.__key())
|
1699
|
+
|
1700
|
+
def __eq__(self, other):
|
1701
|
+
return type(self) == type(other) and (
|
1702
|
+
self.device_p == other.device_p or
|
1703
|
+
# pylint: disable=protected-access
|
1704
|
+
self.__key() == other.__key()
|
1705
|
+
# pylint: enable=protected-access
|
1706
|
+
)
|
1707
|
+
|
1708
|
+
def iterConfigurations(self):
|
1709
|
+
context = self.__context
|
1710
|
+
for config in self.__configuration_descriptor_list:
|
1711
|
+
yield USBConfiguration(context, config)
|
1712
|
+
|
1713
|
+
# BBB
|
1714
|
+
iterConfiguations = iterConfigurations
|
1715
|
+
|
1716
|
+
def iterSettings(self):
|
1717
|
+
for config in self.iterConfigurations():
|
1718
|
+
for interface in config:
|
1719
|
+
for setting in interface:
|
1720
|
+
yield setting
|
1721
|
+
|
1722
|
+
def getBusNumber(self):
|
1723
|
+
"""
|
1724
|
+
Get device's bus number.
|
1725
|
+
"""
|
1726
|
+
return libusb1.libusb_get_bus_number(self.device_p)
|
1727
|
+
|
1728
|
+
def getPortNumber(self):
|
1729
|
+
"""
|
1730
|
+
Get device's port number.
|
1731
|
+
"""
|
1732
|
+
return libusb1.libusb_get_port_number(self.device_p)
|
1733
|
+
|
1734
|
+
def getPortNumberList(self):
|
1735
|
+
"""
|
1736
|
+
Get the port number of each hub toward device.
|
1737
|
+
"""
|
1738
|
+
port_list = (c_uint8 * PATH_MAX_DEPTH)()
|
1739
|
+
result = libusb1.libusb_get_port_numbers(
|
1740
|
+
self.device_p, port_list, len(port_list))
|
1741
|
+
mayRaiseUSBError(result)
|
1742
|
+
return list(port_list[:result])
|
1743
|
+
|
1744
|
+
# TODO: wrap libusb_get_parent when/if libusb removes the need to be inside
|
1745
|
+
# a libusb_(get|free)_device_list block.
|
1746
|
+
|
1747
|
+
def getDeviceAddress(self):
|
1748
|
+
"""
|
1749
|
+
Get device's address on its bus.
|
1750
|
+
"""
|
1751
|
+
return libusb1.libusb_get_device_address(self.device_p)
|
1752
|
+
|
1753
|
+
def getbcdUSB(self):
|
1754
|
+
"""
|
1755
|
+
Get the USB spec version device complies to, in BCD format.
|
1756
|
+
"""
|
1757
|
+
return self.device_descriptor.bcdUSB
|
1758
|
+
|
1759
|
+
def getDeviceClass(self):
|
1760
|
+
"""
|
1761
|
+
Get device's class id.
|
1762
|
+
"""
|
1763
|
+
return self.device_descriptor.bDeviceClass
|
1764
|
+
|
1765
|
+
def getDeviceSubClass(self):
|
1766
|
+
"""
|
1767
|
+
Get device's subclass id.
|
1768
|
+
"""
|
1769
|
+
return self.device_descriptor.bDeviceSubClass
|
1770
|
+
|
1771
|
+
def getDeviceProtocol(self):
|
1772
|
+
"""
|
1773
|
+
Get device's protocol id.
|
1774
|
+
"""
|
1775
|
+
return self.device_descriptor.bDeviceProtocol
|
1776
|
+
|
1777
|
+
def getMaxPacketSize0(self):
|
1778
|
+
"""
|
1779
|
+
Get device's max packet size for endpoint 0 (control).
|
1780
|
+
"""
|
1781
|
+
return self.device_descriptor.bMaxPacketSize0
|
1782
|
+
|
1783
|
+
def getMaxPacketSize(self, endpoint):
|
1784
|
+
"""
|
1785
|
+
Get device's max packet size for given endpoint.
|
1786
|
+
|
1787
|
+
Warning: this function will not always give you the expected result.
|
1788
|
+
See https://libusb.org/ticket/77 . You should instead consult the
|
1789
|
+
endpoint descriptor of current configuration and alternate setting.
|
1790
|
+
"""
|
1791
|
+
result = libusb1.libusb_get_max_packet_size(self.device_p, endpoint)
|
1792
|
+
mayRaiseUSBError(result)
|
1793
|
+
return result
|
1794
|
+
|
1795
|
+
def getMaxISOPacketSize(self, endpoint):
|
1796
|
+
"""
|
1797
|
+
Get the maximum size for a single isochronous packet for given
|
1798
|
+
endpoint.
|
1799
|
+
|
1800
|
+
Warning: this function will not always give you the expected result.
|
1801
|
+
See https://libusb.org/ticket/77 . You should instead consult the
|
1802
|
+
endpoint descriptor of current configuration and alternate setting.
|
1803
|
+
"""
|
1804
|
+
result = libusb1.libusb_get_max_iso_packet_size(self.device_p, endpoint)
|
1805
|
+
mayRaiseUSBError(result)
|
1806
|
+
return result
|
1807
|
+
|
1808
|
+
def getVendorID(self):
|
1809
|
+
"""
|
1810
|
+
Get device's vendor id.
|
1811
|
+
"""
|
1812
|
+
return self.device_descriptor.idVendor
|
1813
|
+
|
1814
|
+
def getProductID(self):
|
1815
|
+
"""
|
1816
|
+
Get device's product id.
|
1817
|
+
"""
|
1818
|
+
return self.device_descriptor.idProduct
|
1819
|
+
|
1820
|
+
def getbcdDevice(self):
|
1821
|
+
"""
|
1822
|
+
Get device's release number.
|
1823
|
+
"""
|
1824
|
+
return self.device_descriptor.bcdDevice
|
1825
|
+
|
1826
|
+
def getSupportedLanguageList(self):
|
1827
|
+
"""
|
1828
|
+
Get the list of language ids device has string descriptors for.
|
1829
|
+
"""
|
1830
|
+
temp_handle = self.open()
|
1831
|
+
return temp_handle.getSupportedLanguageList()
|
1832
|
+
|
1833
|
+
def _getStringDescriptor(self, descriptor, lang_id):
|
1834
|
+
if descriptor == 0:
|
1835
|
+
result = None
|
1836
|
+
else:
|
1837
|
+
temp_handle = self.open()
|
1838
|
+
result = temp_handle.getStringDescriptor(descriptor, lang_id)
|
1839
|
+
return result
|
1840
|
+
|
1841
|
+
def _getASCIIStringDescriptor(self, descriptor):
|
1842
|
+
if descriptor == 0:
|
1843
|
+
result = None
|
1844
|
+
else:
|
1845
|
+
temp_handle = self.open()
|
1846
|
+
result = temp_handle.getASCIIStringDescriptor(descriptor)
|
1847
|
+
return result
|
1848
|
+
|
1849
|
+
def getManufacturer(self):
|
1850
|
+
"""
|
1851
|
+
Get device's manufaturer name.
|
1852
|
+
Note: opens the device temporarily.
|
1853
|
+
"""
|
1854
|
+
return self._getASCIIStringDescriptor(
|
1855
|
+
self.device_descriptor.iManufacturer)
|
1856
|
+
|
1857
|
+
def getProduct(self):
|
1858
|
+
"""
|
1859
|
+
Get device's product name.
|
1860
|
+
Note: opens the device temporarily.
|
1861
|
+
"""
|
1862
|
+
return self._getASCIIStringDescriptor(self.device_descriptor.iProduct)
|
1863
|
+
|
1864
|
+
def getSerialNumber(self):
|
1865
|
+
"""
|
1866
|
+
Get device's serial number.
|
1867
|
+
Note: opens the device temporarily.
|
1868
|
+
"""
|
1869
|
+
return self._getASCIIStringDescriptor(
|
1870
|
+
self.device_descriptor.iSerialNumber)
|
1871
|
+
|
1872
|
+
def getNumConfigurations(self):
|
1873
|
+
"""
|
1874
|
+
Get device's number of possible configurations.
|
1875
|
+
"""
|
1876
|
+
return self.device_descriptor.bNumConfigurations
|
1877
|
+
|
1878
|
+
def getDeviceSpeed(self):
|
1879
|
+
"""
|
1880
|
+
Get device's speed.
|
1881
|
+
|
1882
|
+
Returns one of:
|
1883
|
+
SPEED_UNKNOWN
|
1884
|
+
SPEED_LOW
|
1885
|
+
SPEED_FULL
|
1886
|
+
SPEED_HIGH
|
1887
|
+
SPEED_SUPER
|
1888
|
+
"""
|
1889
|
+
return libusb1.libusb_get_device_speed(self.device_p)
|
1890
|
+
|
1891
|
+
def open(self):
|
1892
|
+
"""
|
1893
|
+
Open device.
|
1894
|
+
Returns an USBDeviceHandle instance.
|
1895
|
+
"""
|
1896
|
+
handle = libusb1.libusb_device_handle_p()
|
1897
|
+
result = libusb1.libusb_open(self.device_p, byref(handle))
|
1898
|
+
mayRaiseUSBError(result)
|
1899
|
+
return USBDeviceHandle(self.__context, handle, self)
|
1900
|
+
|
1901
|
+
_zero_tv = libusb1.timeval(0, 0)
|
1902
|
+
_zero_tv_p = byref(_zero_tv)
|
1903
|
+
|
1904
|
+
class USBContext(object):
|
1905
|
+
"""
|
1906
|
+
libusb1 USB context.
|
1907
|
+
|
1908
|
+
Provides methods to enumerate & look up USB devices.
|
1909
|
+
Also provides access to global (device-independent) libusb1 functions.
|
1910
|
+
"""
|
1911
|
+
__libusb_exit = libusb1.libusb_exit
|
1912
|
+
__context_p = None
|
1913
|
+
__added_cb = None
|
1914
|
+
__removed_cb = None
|
1915
|
+
__libusb_set_pollfd_notifiers = libusb1.libusb_set_pollfd_notifiers
|
1916
|
+
|
1917
|
+
#pylint: disable=no-self-argument
|
1918
|
+
def _validContext(func):
|
1919
|
+
# Defined inside USBContext so we can access "self.__*".
|
1920
|
+
@functools.wraps(func)
|
1921
|
+
def wrapper(self, *args, **kw):
|
1922
|
+
# pylint: disable=protected-access
|
1923
|
+
self.__context_cond.acquire()
|
1924
|
+
self.__context_refcount += 1
|
1925
|
+
self.__context_cond.release()
|
1926
|
+
try:
|
1927
|
+
if self.__context_p is not None:
|
1928
|
+
#pylint: disable=not-callable
|
1929
|
+
return func(self, *args, **kw)
|
1930
|
+
#pylint: enable=not-callable
|
1931
|
+
finally:
|
1932
|
+
self.__context_cond.acquire()
|
1933
|
+
self.__context_refcount -= 1
|
1934
|
+
if not self.__context_refcount:
|
1935
|
+
self.__context_cond.notifyAll()
|
1936
|
+
self.__context_cond.release()
|
1937
|
+
# pylint: enable=protected-access
|
1938
|
+
return wrapper
|
1939
|
+
#pylint: enable=no-self-argument
|
1940
|
+
|
1941
|
+
def __init__(self):
|
1942
|
+
"""
|
1943
|
+
Create a new USB context.
|
1944
|
+
"""
|
1945
|
+
# Used to prevent an exit to cause a segfault if a concurrent thread
|
1946
|
+
# is still in libusb.
|
1947
|
+
self.__context_refcount = 0
|
1948
|
+
self.__context_cond = threading.Condition()
|
1949
|
+
context_p = libusb1.libusb_context_p()
|
1950
|
+
result = libusb1.libusb_init(byref(context_p))
|
1951
|
+
mayRaiseUSBError(result)
|
1952
|
+
self.__context_p = context_p
|
1953
|
+
self.__hotplug_callback_dict = {}
|
1954
|
+
|
1955
|
+
def __del__(self):
|
1956
|
+
# Avoid locking.
|
1957
|
+
# XXX: Assumes __del__ should not normally be called while any
|
1958
|
+
# instance's method is being executed. It seems unlikely (they hold a
|
1959
|
+
# reference to their instance).
|
1960
|
+
self._exit()
|
1961
|
+
|
1962
|
+
def exit(self):
|
1963
|
+
"""
|
1964
|
+
Close (destroy) this USB context.
|
1965
|
+
|
1966
|
+
When this method has been called, methods on its instance will
|
1967
|
+
become mosty no-ops, returning None.
|
1968
|
+
"""
|
1969
|
+
self.__context_cond.acquire()
|
1970
|
+
try:
|
1971
|
+
while self.__context_refcount and self.__context_p is not None:
|
1972
|
+
self.__context_cond.wait()
|
1973
|
+
self._exit()
|
1974
|
+
finally:
|
1975
|
+
self.__context_cond.notifyAll()
|
1976
|
+
self.__context_cond.release()
|
1977
|
+
|
1978
|
+
def _exit(self):
|
1979
|
+
context_p = self.__context_p
|
1980
|
+
if context_p is not None:
|
1981
|
+
self.__libusb_exit(context_p)
|
1982
|
+
self.__context_p = None
|
1983
|
+
self.__added_cb = None
|
1984
|
+
self.__removed_cb = None
|
1985
|
+
|
1986
|
+
@_validContext
|
1987
|
+
def getDeviceList(self, skip_on_access_error=False, skip_on_error=False):
|
1988
|
+
"""
|
1989
|
+
Return a list of all USB devices currently plugged in, as USBDevice
|
1990
|
+
instances.
|
1991
|
+
|
1992
|
+
skip_on_error (bool)
|
1993
|
+
If True, ignore devices which raise USBError.
|
1994
|
+
|
1995
|
+
skip_on_access_error (bool)
|
1996
|
+
DEPRECATED. Alias for skip_on_error.
|
1997
|
+
"""
|
1998
|
+
skip_on_error = skip_on_error or skip_on_access_error
|
1999
|
+
device_p_p = libusb1.libusb_device_p_p()
|
2000
|
+
libusb_device_p = libusb1.libusb_device_p
|
2001
|
+
device_list_len = libusb1.libusb_get_device_list(self.__context_p,
|
2002
|
+
byref(device_p_p))
|
2003
|
+
mayRaiseUSBError(device_list_len)
|
2004
|
+
try:
|
2005
|
+
result = []
|
2006
|
+
append = result.append
|
2007
|
+
for device_p in device_p_p[:device_list_len]:
|
2008
|
+
try:
|
2009
|
+
# Instanciate our own libusb_device_p object so we can free
|
2010
|
+
# libusb-provided device list. Is this a bug in ctypes that
|
2011
|
+
# it doesn't copy pointer value (=pointed memory address) ?
|
2012
|
+
# At least, it's not so convenient and forces using such
|
2013
|
+
# weird code.
|
2014
|
+
device = USBDevice(self, libusb_device_p(device_p.contents))
|
2015
|
+
except USBError:
|
2016
|
+
if not skip_on_error:
|
2017
|
+
raise
|
2018
|
+
else:
|
2019
|
+
append(device)
|
2020
|
+
finally:
|
2021
|
+
libusb1.libusb_free_device_list(device_p_p, 1)
|
2022
|
+
return result
|
2023
|
+
|
2024
|
+
def getByVendorIDAndProductID(
|
2025
|
+
self, vendor_id, product_id,
|
2026
|
+
skip_on_access_error=False, skip_on_error=False):
|
2027
|
+
"""
|
2028
|
+
Get the first USB device matching given vendor and product ids.
|
2029
|
+
Returns an USBDevice instance, or None if no present device match.
|
2030
|
+
skip_on_error (bool)
|
2031
|
+
(see getDeviceList)
|
2032
|
+
skip_on_access_error (bool)
|
2033
|
+
(see getDeviceList)
|
2034
|
+
"""
|
2035
|
+
for device in self.getDeviceList(
|
2036
|
+
skip_on_access_error=skip_on_access_error,
|
2037
|
+
skip_on_error=skip_on_error):
|
2038
|
+
if device.getVendorID() == vendor_id and \
|
2039
|
+
device.getProductID() == product_id:
|
2040
|
+
return device
|
2041
|
+
|
2042
|
+
def openByVendorIDAndProductID(
|
2043
|
+
self, vendor_id, product_id,
|
2044
|
+
skip_on_access_error=False, skip_on_error=False):
|
2045
|
+
"""
|
2046
|
+
Get the first USB device matching given vendor and product ids.
|
2047
|
+
Returns an USBDeviceHandle instance, or None if no present device
|
2048
|
+
match.
|
2049
|
+
skip_on_error (bool)
|
2050
|
+
(see getDeviceList)
|
2051
|
+
skip_on_access_error (bool)
|
2052
|
+
(see getDeviceList)
|
2053
|
+
"""
|
2054
|
+
result = self.getByVendorIDAndProductID(
|
2055
|
+
vendor_id, product_id,
|
2056
|
+
skip_on_access_error=skip_on_access_error,
|
2057
|
+
skip_on_error=skip_on_error)
|
2058
|
+
if result is not None:
|
2059
|
+
return result.open()
|
2060
|
+
|
2061
|
+
@_validContext
|
2062
|
+
def getPollFDList(self):
|
2063
|
+
"""
|
2064
|
+
Return file descriptors to be used to poll USB events.
|
2065
|
+
You should not have to call this method, unless you are integrating
|
2066
|
+
this class with a polling mechanism.
|
2067
|
+
"""
|
2068
|
+
pollfd_p_p = libusb1.libusb_get_pollfds(self.__context_p)
|
2069
|
+
if not pollfd_p_p:
|
2070
|
+
errno = get_errno()
|
2071
|
+
if errno:
|
2072
|
+
raise OSError(errno)
|
2073
|
+
else:
|
2074
|
+
# Assume not implemented
|
2075
|
+
raise NotImplementedError(
|
2076
|
+
'Your libusb does not seem to implement pollable FDs')
|
2077
|
+
try:
|
2078
|
+
result = []
|
2079
|
+
append = result.append
|
2080
|
+
fd_index = 0
|
2081
|
+
while pollfd_p_p[fd_index]:
|
2082
|
+
append((
|
2083
|
+
pollfd_p_p[fd_index].contents.fd,
|
2084
|
+
pollfd_p_p[fd_index].contents.events,
|
2085
|
+
))
|
2086
|
+
fd_index += 1
|
2087
|
+
finally:
|
2088
|
+
_free(pollfd_p_p)
|
2089
|
+
return result
|
2090
|
+
|
2091
|
+
@_validContext
|
2092
|
+
def handleEvents(self):
|
2093
|
+
"""
|
2094
|
+
Handle any pending event (blocking).
|
2095
|
+
See libusb1 documentation for details (there is a timeout, so it's
|
2096
|
+
not "really" blocking).
|
2097
|
+
"""
|
2098
|
+
result = libusb1.libusb_handle_events(self.__context_p)
|
2099
|
+
mayRaiseUSBError(result)
|
2100
|
+
|
2101
|
+
# TODO: handleEventsCompleted
|
2102
|
+
|
2103
|
+
@_validContext
|
2104
|
+
def handleEventsTimeout(self, tv=0):
|
2105
|
+
"""
|
2106
|
+
Handle any pending event.
|
2107
|
+
If tv is 0, will return immediately after handling already-pending
|
2108
|
+
events.
|
2109
|
+
Otherwise, defines the maximum amount of time to wait for events, in
|
2110
|
+
seconds.
|
2111
|
+
"""
|
2112
|
+
if tv is None:
|
2113
|
+
tv = 0
|
2114
|
+
tv_s = int(tv)
|
2115
|
+
tv = libusb1.timeval(tv_s, int((tv - tv_s) * 1000000))
|
2116
|
+
result = libusb1.libusb_handle_events_timeout(
|
2117
|
+
self.__context_p, byref(tv))
|
2118
|
+
mayRaiseUSBError(result)
|
2119
|
+
|
2120
|
+
# TODO: handleEventsTimeoutCompleted
|
2121
|
+
|
2122
|
+
@_validContext
|
2123
|
+
def setPollFDNotifiers(
|
2124
|
+
self, added_cb=None, removed_cb=None, user_data=None):
|
2125
|
+
"""
|
2126
|
+
Give libusb1 methods to call when it should add/remove file descriptor
|
2127
|
+
for polling.
|
2128
|
+
You should not have to call this method, unless you are integrating
|
2129
|
+
this class with a polling mechanism.
|
2130
|
+
"""
|
2131
|
+
if added_cb is None:
|
2132
|
+
added_cb = POINTER(None)
|
2133
|
+
else:
|
2134
|
+
added_cb = libusb1.libusb_pollfd_added_cb_p(added_cb)
|
2135
|
+
if removed_cb is None:
|
2136
|
+
removed_cb = POINTER(None)
|
2137
|
+
else:
|
2138
|
+
removed_cb = libusb1.libusb_pollfd_removed_cb_p(removed_cb)
|
2139
|
+
self.__added_cb = added_cb
|
2140
|
+
self.__removed_cb = removed_cb
|
2141
|
+
self.__libusb_set_pollfd_notifiers(
|
2142
|
+
self.__context_p, added_cb, removed_cb, user_data)
|
2143
|
+
|
2144
|
+
@_validContext
|
2145
|
+
def getNextTimeout(self):
|
2146
|
+
"""
|
2147
|
+
Returns the next internal timeout that libusb needs to handle, in
|
2148
|
+
seconds, or None if no timeout is needed.
|
2149
|
+
You should not have to call this method, unless you are integrating
|
2150
|
+
this class with a polling mechanism.
|
2151
|
+
"""
|
2152
|
+
timeval = libusb1.timeval()
|
2153
|
+
result = libusb1.libusb_get_next_timeout(
|
2154
|
+
self.__context_p, byref(timeval))
|
2155
|
+
if result == 0:
|
2156
|
+
return None
|
2157
|
+
elif result == 1:
|
2158
|
+
return timeval.tv_sec + (timeval.tv_usec * 0.000001)
|
2159
|
+
raiseUSBError(result)
|
2160
|
+
|
2161
|
+
@_validContext
|
2162
|
+
def setDebug(self, level):
|
2163
|
+
"""
|
2164
|
+
Set debugging level.
|
2165
|
+
Note: depending on libusb compilation settings, this might have no
|
2166
|
+
effect.
|
2167
|
+
"""
|
2168
|
+
libusb1.libusb_set_debug(self.__context_p, level)
|
2169
|
+
|
2170
|
+
@_validContext
|
2171
|
+
def tryLockEvents(self):
|
2172
|
+
"""
|
2173
|
+
See libusb_try_lock_events doc.
|
2174
|
+
"""
|
2175
|
+
return libusb1.libusb_try_lock_events(self.__context_p)
|
2176
|
+
|
2177
|
+
@_validContext
|
2178
|
+
def lockEvents(self):
|
2179
|
+
"""
|
2180
|
+
See libusb_lock_events doc.
|
2181
|
+
"""
|
2182
|
+
libusb1.libusb_lock_events(self.__context_p)
|
2183
|
+
|
2184
|
+
@_validContext
|
2185
|
+
def lockEventWaiters(self):
|
2186
|
+
"""
|
2187
|
+
See libusb_lock_event_waiters doc.
|
2188
|
+
"""
|
2189
|
+
libusb1.libusb_lock_event_waiters(self.__context_p)
|
2190
|
+
|
2191
|
+
@_validContext
|
2192
|
+
def waitForEvent(self, tv=0):
|
2193
|
+
"""
|
2194
|
+
See libusb_wait_for_event doc.
|
2195
|
+
"""
|
2196
|
+
if tv is None:
|
2197
|
+
tv = 0
|
2198
|
+
tv_s = int(tv)
|
2199
|
+
tv = libusb1.timeval(tv_s, int((tv - tv_s) * 1000000))
|
2200
|
+
libusb1.libusb_wait_for_event(self.__context_p, byref(tv))
|
2201
|
+
|
2202
|
+
@_validContext
|
2203
|
+
def unlockEventWaiters(self):
|
2204
|
+
"""
|
2205
|
+
See libusb_unlock_event_waiters doc.
|
2206
|
+
"""
|
2207
|
+
libusb1.libusb_unlock_event_waiters(self.__context_p)
|
2208
|
+
|
2209
|
+
@_validContext
|
2210
|
+
def eventHandlingOK(self):
|
2211
|
+
"""
|
2212
|
+
See libusb_event_handling_ok doc.
|
2213
|
+
"""
|
2214
|
+
return libusb1.libusb_event_handling_ok(self.__context_p)
|
2215
|
+
|
2216
|
+
@_validContext
|
2217
|
+
def unlockEvents(self):
|
2218
|
+
"""
|
2219
|
+
See libusb_unlock_events doc.
|
2220
|
+
"""
|
2221
|
+
libusb1.libusb_unlock_events(self.__context_p)
|
2222
|
+
|
2223
|
+
@_validContext
|
2224
|
+
def handleEventsLocked(self):
|
2225
|
+
"""
|
2226
|
+
See libusb_handle_events_locked doc.
|
2227
|
+
"""
|
2228
|
+
# XXX: does tv parameter need to be exposed ?
|
2229
|
+
result = libusb1.libusb_handle_events_locked(
|
2230
|
+
self.__context_p, _zero_tv_p)
|
2231
|
+
mayRaiseUSBError(result)
|
2232
|
+
|
2233
|
+
@_validContext
|
2234
|
+
def eventHandlerActive(self):
|
2235
|
+
"""
|
2236
|
+
See libusb_event_handler_active doc.
|
2237
|
+
"""
|
2238
|
+
return libusb1.libusb_event_handler_active(self.__context_p)
|
2239
|
+
|
2240
|
+
@staticmethod
|
2241
|
+
def hasCapability(capability):
|
2242
|
+
"""
|
2243
|
+
Backward-compatibility alias for module-level hasCapability.
|
2244
|
+
"""
|
2245
|
+
return hasCapability(capability)
|
2246
|
+
|
2247
|
+
@_validContext
|
2248
|
+
def hotplugRegisterCallback(
|
2249
|
+
self, callback,
|
2250
|
+
# pylint: disable=undefined-variable
|
2251
|
+
events=HOTPLUG_EVENT_DEVICE_ARRIVED | HOTPLUG_EVENT_DEVICE_LEFT,
|
2252
|
+
flags=HOTPLUG_ENUMERATE,
|
2253
|
+
vendor_id=HOTPLUG_MATCH_ANY,
|
2254
|
+
product_id=HOTPLUG_MATCH_ANY,
|
2255
|
+
dev_class=HOTPLUG_MATCH_ANY,
|
2256
|
+
# pylint: enable=undefined-variable
|
2257
|
+
):
|
2258
|
+
"""
|
2259
|
+
Registers an hotplug callback.
|
2260
|
+
On success, returns an opaque value which can be passed to
|
2261
|
+
hotplugDeregisterCallback.
|
2262
|
+
Callback must accept the following positional arguments:
|
2263
|
+
- this USBContext instance
|
2264
|
+
- an USBDevice instance
|
2265
|
+
If device has left, configuration descriptors may not be
|
2266
|
+
available. Its device descriptor will be available.
|
2267
|
+
- event type, one of:
|
2268
|
+
HOTPLUG_EVENT_DEVICE_ARRIVED
|
2269
|
+
HOTPLUG_EVENT_DEVICE_LEFT
|
2270
|
+
Callback must return whether it must be unregistered (any true value
|
2271
|
+
to be unregistered, any false value to be kept registered).
|
2272
|
+
"""
|
2273
|
+
def wrapped_callback(context_p, device_p, event, _):
|
2274
|
+
assert addressof(context_p.contents) == addressof(
|
2275
|
+
self.__context_p.contents), (context_p, self.__context_p)
|
2276
|
+
unregister = bool(callback(
|
2277
|
+
self,
|
2278
|
+
USBDevice(
|
2279
|
+
self,
|
2280
|
+
device_p,
|
2281
|
+
# pylint: disable=undefined-variable
|
2282
|
+
event != HOTPLUG_EVENT_DEVICE_LEFT,
|
2283
|
+
# pylint: enable=undefined-variable
|
2284
|
+
),
|
2285
|
+
event,
|
2286
|
+
))
|
2287
|
+
if unregister:
|
2288
|
+
del self.__hotplug_callback_dict[handle]
|
2289
|
+
return unregister
|
2290
|
+
handle = c_int()
|
2291
|
+
callback_p = libusb1.libusb_hotplug_callback_fn_p(wrapped_callback)
|
2292
|
+
result = libusb1.libusb_hotplug_register_callback(
|
2293
|
+
self.__context_p, events, flags, vendor_id, product_id, dev_class,
|
2294
|
+
callback_p, None, byref(handle))
|
2295
|
+
mayRaiseUSBError(result)
|
2296
|
+
handle = handle.value
|
2297
|
+
# Keep strong references
|
2298
|
+
assert handle not in self.__hotplug_callback_dict, (
|
2299
|
+
handle,
|
2300
|
+
self.__hotplug_callback_dict,
|
2301
|
+
)
|
2302
|
+
self.__hotplug_callback_dict[handle] = (callback_p, wrapped_callback)
|
2303
|
+
return handle
|
2304
|
+
|
2305
|
+
@_validContext
|
2306
|
+
def hotplugDeregisterCallback(self, handle):
|
2307
|
+
"""
|
2308
|
+
Deregisters an hotplug callback.
|
2309
|
+
handle (opaque)
|
2310
|
+
Return value of a former hotplugRegisterCallback call.
|
2311
|
+
"""
|
2312
|
+
del self.__hotplug_callback_dict[handle]
|
2313
|
+
libusb1.libusb_hotplug_deregister_callback(self.__context_p, handle)
|
2314
|
+
|
2315
|
+
del USBContext._validContext
|
2316
|
+
|
2317
|
+
def getVersion():
|
2318
|
+
"""
|
2319
|
+
Returns underlying libusb's version information as a 6-namedtuple (or
|
2320
|
+
6-tuple if namedtuples are not avaiable):
|
2321
|
+
- major
|
2322
|
+
- minor
|
2323
|
+
- micro
|
2324
|
+
- nano
|
2325
|
+
- rc
|
2326
|
+
- describe
|
2327
|
+
Returns (0, 0, 0, 0, '', '') if libusb doesn't have required entry point.
|
2328
|
+
"""
|
2329
|
+
version = libusb1.libusb_get_version().contents
|
2330
|
+
return Version(
|
2331
|
+
version.major,
|
2332
|
+
version.minor,
|
2333
|
+
version.micro,
|
2334
|
+
version.nano,
|
2335
|
+
version.rc,
|
2336
|
+
version.describe,
|
2337
|
+
)
|
2338
|
+
|
2339
|
+
def hasCapability(capability):
|
2340
|
+
"""
|
2341
|
+
Tests feature presence.
|
2342
|
+
|
2343
|
+
capability should be one of:
|
2344
|
+
CAP_HAS_CAPABILITY
|
2345
|
+
CAP_HAS_HOTPLUG
|
2346
|
+
CAP_HAS_HID_ACCESS
|
2347
|
+
CAP_SUPPORTS_DETACH_KERNEL_DRIVER
|
2348
|
+
"""
|
2349
|
+
return libusb1.libusb_has_capability(capability)
|
2350
|
+
|
2351
|
+
class LibUSBContext(USBContext):
|
2352
|
+
"""
|
2353
|
+
Backward-compatibility alias for USBContext.
|
2354
|
+
"""
|
2355
|
+
def __init__(self):
|
2356
|
+
warnings.warn(
|
2357
|
+
'LibUSBContext is being renamed to USBContext',
|
2358
|
+
DeprecationWarning,
|
2359
|
+
)
|
2360
|
+
super(LibUSBContext, self).__init__()
|