Web   ·   Wiki   ·   Activities   ·   Blog   ·   Lists   ·   Chat   ·   Meeting   ·   Bugs   ·   Git   ·   Translate   ·   Archive   ·   People   ·   Donate
summaryrefslogtreecommitdiffstats
path: root/plugins/wedo_plugin/usb/core.py
diff options
context:
space:
mode:
Diffstat (limited to 'plugins/wedo_plugin/usb/core.py')
-rw-r--r--plugins/wedo_plugin/usb/core.py856
1 files changed, 856 insertions, 0 deletions
diff --git a/plugins/wedo_plugin/usb/core.py b/plugins/wedo_plugin/usb/core.py
new file mode 100644
index 0000000..c90d011
--- /dev/null
+++ b/plugins/wedo_plugin/usb/core.py
@@ -0,0 +1,856 @@
+# Copyright (C) 2009-2011 Wander Lairson Costa
+#
+# The following terms apply to all files associated
+# with the software unless explicitly disclaimed in individual files.
+#
+# The authors hereby grant permission to use, copy, modify, distribute,
+# and license this software and its documentation for any purpose, provided
+# that existing copyright notices are retained in all copies and that this
+# notice is included verbatim in any distributions. No written agreement,
+# license, or royalty fee is required for any of the authorized uses.
+# Modifications to this software may be copyrighted by their authors
+# and need not follow the licensing terms described here, provided that
+# the new terms are clearly indicated on the first page of each file where
+# they apply.
+#
+# IN NO EVENT SHALL THE AUTHORS OR DISTRIBUTORS BE LIABLE TO ANY PARTY
+# FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
+# ARISING OUT OF THE USE OF THIS SOFTWARE, ITS DOCUMENTATION, OR ANY
+# DERIVATIVES THEREOF, EVEN IF THE AUTHORS HAVE BEEN ADVISED OF THE
+# POSSIBILITY OF SUCH DAMAGE.
+#
+# THE AUTHORS AND DISTRIBUTORS SPECIFICALLY DISCLAIM ANY WARRANTIES,
+# INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE, AND NON-INFRINGEMENT. THIS SOFTWARE
+# IS PROVIDED ON AN "AS IS" BASIS, AND THE AUTHORS AND DISTRIBUTORS HAVE
+# NO OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR
+# MODIFICATIONS.
+
+r"""usb.core - Core USB features.
+
+This module exports:
+
+Device - a class representing a USB device.
+Configuration - a class representing a configuration descriptor.
+Interface - a class representing an interface descriptor.
+Endpoint - a class representing an endpoint descriptor.
+find() - a function to find USB devices.
+"""
+
+__author__ = 'Wander Lairson Costa'
+
+__all__ = ['Device', 'Configuration', 'Interface', 'Endpoint', 'find']
+
+import usb.util as util
+import copy
+import operator
+import usb._interop as _interop
+import logging
+
+_logger = logging.getLogger('usb.core')
+
+_DEFAULT_TIMEOUT = 1000
+
+def _set_attr(input, output, fields):
+ for f in fields:
+ setattr(output, f, int(getattr(input, f)))
+
+class _ResourceManager(object):
+ def __init__(self, dev, backend):
+ self.backend = backend
+ self._active_cfg_index = None
+ self.dev = dev
+ self.handle = None
+ self._claimed_intf = _interop._set()
+ self._alt_set = {}
+ self._ep_type_map = {}
+
+ def managed_open(self):
+ if self.handle is None:
+ self.handle = self.backend.open_device(self.dev)
+ return self.handle
+
+ def managed_close(self):
+ if self.handle is not None:
+ self.backend.close_device(self.handle)
+ self.handle = None
+
+ def managed_set_configuration(self, device, config):
+ if config is None:
+ cfg = device[0]
+ elif isinstance(config, Configuration):
+ cfg = config
+ elif config == 0: # unconfigured state
+ class FakeConfiguration(object):
+ def __init__(self):
+ self.index = None
+ self.bConfigurationValue = 0
+ cfg = FakeConfiguration()
+ else:
+ cfg = util.find_descriptor(device, bConfigurationValue=config)
+ self.managed_open()
+ self.backend.set_configuration(self.handle, cfg.bConfigurationValue)
+ # cache the index instead of the object to avoid cyclic references
+ # of the device and Configuration (Device tracks the _ResourceManager,
+ # which tracks the Configuration, which tracks the Device)
+ self._active_cfg_index = cfg.index
+ # after changing configuration, our alternate setting and endpoint type caches
+ # are not valid anymore
+ self._ep_type_map.clear()
+ self._alt_set.clear()
+
+ def managed_claim_interface(self, device, intf):
+ self.managed_open()
+ if intf is None:
+ cfg = self.get_active_configuration(device)
+ i = cfg[(0,0)].bInterfaceNumber
+ elif isinstance(intf, Interface):
+ i = intf.bInterfaceNumber
+ else:
+ i = intf
+ if i not in self._claimed_intf:
+ self.backend.claim_interface(self.handle, i)
+ self._claimed_intf.add(i)
+
+ def managed_release_interface(self, device, intf):
+ if intf is None:
+ cfg = self.get_active_configuration(device)
+ i = cfg[(0,0)].bInterfaceNumber
+ elif isinstance(intf, Interface):
+ i = intf.bInterfaceNumber
+ else:
+ i = intf
+ if i in self._claimed_intf:
+ self.backend.release_interface(self.handle, i)
+ self._claimed_intf.remove(i)
+
+ def managed_set_interface(self, device, intf, alt):
+ if intf is None:
+ i = self.get_interface(device, intf)
+ elif isinstance(intf, Interface):
+ i = intf
+ else:
+ cfg = self.get_active_configuration(device)
+ if alt is not None:
+ i = util.find_descriptor(cfg, bInterfaceNumber=intf, bAlternateSetting=alt)
+ else:
+ i = util.find_descriptor(cfg, bInterfaceNumber=intf)
+ self.managed_claim_interface(device, i)
+ if alt is None:
+ alt = i.bAlternateSetting
+ self.backend.set_interface_altsetting(self.handle, i.bInterfaceNumber, alt)
+ self._alt_set[i.bInterfaceNumber] = alt
+
+ def get_interface(self, device, intf):
+ # TODO: check the viability of issuing a GET_INTERFACE
+ # request when we don't have a alternate setting cached
+ if intf is None:
+ cfg = self.get_active_configuration(device)
+ return cfg[(0,0)]
+ elif isinstance(intf, Interface):
+ return intf
+ else:
+ cfg = self.get_active_configuration(device)
+ if intf in self._alt_set:
+ return util.find_descriptor(cfg,
+ bInterfaceNumber=intf,
+ bAlternateSetting=self._alt_set[intf])
+ else:
+ return util.find_descriptor(cfg, bInterfaceNumber=intf)
+
+ def get_active_configuration(self, device):
+ if self._active_cfg_index is None:
+ self.managed_open()
+ cfg = util.find_descriptor(
+ device,
+ bConfigurationValue=self.backend.get_configuration(self.handle)
+ )
+ if cfg is None:
+ raise USBError('Configuration not set')
+ self._active_cfg_index = cfg.index
+ return cfg
+ return device[self._active_cfg_index]
+
+ def get_endpoint_type(self, device, address, intf):
+ intf = self.get_interface(device, intf)
+ key = (address, intf.bInterfaceNumber, intf.bAlternateSetting)
+ try:
+ return self._ep_type_map[key]
+ except KeyError:
+ e = util.find_descriptor(intf, bEndpointAddress=address)
+ etype = util.endpoint_type(e.bmAttributes)
+ self._ep_type_map[key] = etype
+ return etype
+
+ def release_all_interfaces(self, device):
+ claimed = copy.copy(self._claimed_intf)
+ for i in claimed:
+ self.managed_release_interface(device, i)
+
+ def dispose(self, device, close_handle = True):
+ self.release_all_interfaces(device)
+ if close_handle:
+ self.managed_close()
+ self._ep_type_map.clear()
+ self._alt_set.clear()
+ self._active_cfg_index = None
+
+class USBError(IOError):
+ r"""Exception class for USB errors.
+
+ Backends must raise this exception when USB related errors occur.
+ The backend specific error code is available through the
+ 'backend_error_code' member variable.
+ """
+
+ def __init__(self, strerror, error_code = None, errno = None):
+ r"""Initialize the object.
+
+ This initializes the USBError object. The strerror and errno are passed
+ to the parent object. The error_code parameter is attributed to the
+ backend_error_code member variable.
+ """
+ IOError.__init__(self, errno, strerror)
+ self.backend_error_code = error_code
+
+class Endpoint(object):
+ r"""Represent an endpoint object.
+
+ This class contains all fields of the Endpoint Descriptor
+ according to the USB Specification. You may access them as class
+ properties. For example, to access the field bEndpointAddress
+ of the endpoint descriptor:
+
+ >>> import usb.core
+ >>> dev = usb.core.find()
+ >>> for cfg in dev:
+ >>> for i in cfg:
+ >>> for e in i:
+ >>> print e.bEndpointAddress
+ """
+
+ def __init__(self, device, endpoint, interface = 0,
+ alternate_setting = 0, configuration = 0):
+ r"""Initialize the Endpoint object.
+
+ The device parameter is the device object returned by the find()
+ function. endpoint is the endpoint logical index (not the endpoint address).
+ The configuration parameter is the logical index of the
+ configuration (not the bConfigurationValue field). The interface
+ parameter is the interface logical index (not the bInterfaceNumber field)
+ and alternate_setting is the alternate setting logical index (not the
+ bAlternateSetting value). Not every interface has more than one alternate
+ setting. In this case, the alternate_setting parameter should be zero.
+ By "logical index" we mean the relative order of the configurations returned by the
+ peripheral as a result of GET_DESCRIPTOR request.
+ """
+ self.device = device
+ intf = Interface(device, interface, alternate_setting, configuration)
+ self.interface = intf.bInterfaceNumber
+ self.index = endpoint
+
+ backend = device._ctx.backend
+
+ desc = backend.get_endpoint_descriptor(
+ device._ctx.dev,
+ endpoint,
+ interface,
+ alternate_setting,
+ configuration
+ )
+
+ _set_attr(
+ desc,
+ self,
+ (
+ 'bLength',
+ 'bDescriptorType',
+ 'bEndpointAddress',
+ 'bmAttributes',
+ 'wMaxPacketSize',
+ 'bInterval',
+ 'bRefresh',
+ 'bSynchAddress'
+ )
+ )
+
+ def write(self, data, timeout = None):
+ r"""Write data to the endpoint.
+
+ The parameter data contains the data to be sent to the endpoint and
+ timeout is the time limit of the operation. The transfer type and
+ endpoint address are automatically inferred.
+
+ The method returns the number of bytes written.
+
+ For details, see the Device.write() method.
+ """
+ return self.device.write(self.bEndpointAddress, data, self.interface, timeout)
+
+ def read(self, size, timeout = None):
+ r"""Read data from the endpoint.
+
+ The parameter size is the number of bytes to read and timeout is the
+ time limit of the operation.The transfer type and endpoint address
+ are automatically inferred.
+
+ The method returns an array.array object with the data read.
+
+ For details, see the Device.read() method.
+ """
+ return self.device.read(self.bEndpointAddress, size, self.interface, timeout)
+
+class Interface(object):
+ r"""Represent an interface object.
+
+ This class contains all fields of the Interface Descriptor
+ according to the USB Specification. You may access them as class
+ properties. For example, to access the field bInterfaceNumber
+ of the interface descriptor:
+
+ >>> import usb.core
+ >>> dev = usb.core.find()
+ >>> for cfg in dev:
+ >>> for i in cfg:
+ >>> print i.bInterfaceNumber
+ """
+
+ def __init__(self, device, interface = 0,
+ alternate_setting = 0, configuration = 0):
+ r"""Initialize the interface object.
+
+ The device parameter is the device object returned by the find()
+ function. The configuration parameter is the logical index of the
+ configuration (not the bConfigurationValue field). The interface
+ parameter is the interface logical index (not the bInterfaceNumber field)
+ and alternate_setting is the alternate setting logical index (not the
+ bAlternateSetting value). Not every interface has more than one alternate
+ setting. In this case, the alternate_setting parameter should be zero.
+ By "logical index" we mean the relative order of the configurations returned by the
+ peripheral as a result of GET_DESCRIPTOR request.
+ """
+ self.device = device
+ self.alternate_index = alternate_setting
+ self.index = interface
+ self.configuration = configuration
+
+ backend = device._ctx.backend
+
+ desc = backend.get_interface_descriptor(
+ self.device._ctx.dev,
+ interface,
+ alternate_setting,
+ configuration
+ )
+
+ _set_attr(
+ desc,
+ self,
+ (
+ 'bLength',
+ 'bDescriptorType',
+ 'bInterfaceNumber',
+ 'bAlternateSetting',
+ 'bNumEndpoints',
+ 'bInterfaceClass',
+ 'bInterfaceSubClass',
+ 'bInterfaceProtocol',
+ 'iInterface',
+ )
+ )
+
+ def set_altsetting(self):
+ r"""Set the interface alternate setting."""
+ self.device.set_interface_altsetting(
+ self.bInterfaceNumber,
+ self.bAlternateSetting
+ )
+
+ def __iter__(self):
+ r"""Iterate over all endpoints of the interface."""
+ for i in range(self.bNumEndpoints):
+ yield Endpoint(
+ self.device,
+ i,
+ self.index,
+ self.alternate_index,
+ self.configuration
+ )
+ def __getitem__(self, index):
+ r"""Return the Endpoint object in the given position."""
+ return Endpoint(
+ self.device,
+ index,
+ self.index,
+ self.alternate_index,
+ self.configuration
+ )
+
+class Configuration(object):
+ r"""Represent a configuration object.
+
+ This class contains all fields of the Configuration Descriptor
+ according to the USB Specification. You may access them as class
+ properties. For example, to access the field bConfigurationValue
+ of the configuration descriptor:
+
+ >>> import usb.core
+ >>> dev = usb.core.find()
+ >>> for cfg in dev:
+ >>> print cfg.bConfigurationValue
+ """
+
+ def __init__(self, device, configuration = 0):
+ r"""Initialize the configuration object.
+
+ The device parameter is the device object returned by the find()
+ function. The configuration parameter is the logical index of the
+ configuration (not the bConfigurationValue field). By "logical index"
+ we mean the relative order of the configurations returned by the
+ peripheral as a result of GET_DESCRIPTOR request.
+ """
+ self.device = device
+ self.index = configuration
+
+ backend = device._ctx.backend
+
+ desc = backend.get_configuration_descriptor(
+ self.device._ctx.dev,
+ configuration
+ )
+
+ _set_attr(
+ desc,
+ self,
+ (
+ 'bLength',
+ 'bDescriptorType',
+ 'wTotalLength',
+ 'bNumInterfaces',
+ 'bConfigurationValue',
+ 'iConfiguration',
+ 'bmAttributes',
+ 'bMaxPower'
+ )
+ )
+
+ def set(self):
+ r"""Set this configuration as the active one."""
+ self.device.set_configuration(self.bConfigurationValue)
+
+ def __iter__(self):
+ r"""Iterate over all interfaces of the configuration."""
+ for i in range(self.bNumInterfaces):
+ alt = 0
+ try:
+ while True:
+ yield Interface(self.device, i, alt, self.index)
+ alt += 1
+ except (USBError, IndexError):
+ pass
+ def __getitem__(self, index):
+ r"""Return the Interface object in the given position.
+
+ index is a tuple of two values with interface index and
+ alternate setting index, respectivally. Example:
+
+ >>> interface = config[(0, 0)]
+ """
+ return Interface(self.device, index[0], index[1], self.index)
+
+
+class Device(object):
+ r"""Device object.
+
+ This class contains all fields of the Device Descriptor according
+ to the USB Specification. You may access them as class properties.
+ For example, to access the field bDescriptorType of the device
+ descriptor:
+
+ >>> import usb.core
+ >>> dev = usb.core.find()
+ >>> dev.bDescriptorType
+
+ Additionally, the class provides methods to communicate with
+ the hardware. Typically, an application will first call the
+ set_configuration() method to put the device in a known configured
+ state, optionally call the set_interface_altsetting() to select the
+ alternate setting (if there is more than one) of the interface used,
+ and call the write() and read() method to send and receive data.
+
+ When working in a new hardware, one first try would be like this:
+
+ >>> import usb.core
+ >>> dev = usb.core.find(idVendor=myVendorId, idProduct=myProductId)
+ >>> dev.set_configuration()
+ >>> dev.write(1, 'test')
+
+ This sample finds the device of interest (myVendorId and myProductId should be
+ replaced by the corresponding values of your device), then configures the device
+ (by default, the configuration value is 1, which is a typical value for most
+ devices) and then writes some data to the endpoint 0x01.
+
+ Timeout values for the write, read and ctrl_transfer methods are specified in
+ miliseconds. If the parameter is omitted, Device.default_timeout value will
+ be used instead. This property can be set by the user at anytime.
+ """
+
+ def __init__(self, dev, backend):
+ r"""Initialize the Device object.
+
+ Library users should normally get a Device instance through
+ the find function. The dev parameter is the identification
+ of a device to the backend and its meaning is opaque outside
+ of it. The backend parameter is a instance of a backend
+ object.
+ """
+ self._ctx = _ResourceManager(dev, backend)
+ self.__default_timeout = _DEFAULT_TIMEOUT
+
+ desc = backend.get_device_descriptor(dev)
+
+ _set_attr(
+ desc,
+ self,
+ (
+ 'bLength',
+ 'bDescriptorType',
+ 'bcdUSB',
+ 'bDeviceClass',
+ 'bDeviceSubClass',
+ 'bDeviceProtocol',
+ 'bMaxPacketSize0',
+ 'idVendor',
+ 'idProduct',
+ 'bcdDevice',
+ 'iManufacturer',
+ 'iProduct',
+ 'iSerialNumber',
+ 'bNumConfigurations',
+ 'address',
+ 'bus'
+ )
+ )
+
+ self.bus = int(desc.bus) if desc.bus is not None else None
+ self.address = int(desc.address) if desc.address is not None else None
+
+ def set_configuration(self, configuration = None):
+ r"""Set the active configuration.
+
+ The configuration parameter is the bConfigurationValue field of the
+ configuration you want to set as active. If you call this method
+ without parameter, it will use the first configuration found.
+ As a device hardly ever has more than one configuration, calling
+ the method without parameter is enough to get the device ready.
+ """
+ self._ctx.managed_set_configuration(self, configuration)
+
+ def get_active_configuration(self):
+ r"""Return a Configuration object representing the current configuration set."""
+ return self._ctx.get_active_configuration(self)
+
+ def set_interface_altsetting(self, interface = None, alternate_setting = None):
+ r"""Set the alternate setting for an interface.
+
+ When you want to use an interface and it has more than one alternate setting,
+ you should call this method to select the alternate setting you would like
+ to use. If you call the method without one or the two parameters, it will
+ be selected the first one found in the Device in the same way of set_configuration
+ method.
+
+ Commonly, an interface has only one alternate setting and this call is
+ not necessary. For most of the devices, either it has more than one alternate
+ setting or not, it is not harmful to make a call to this method with no arguments,
+ as devices will silently ignore the request when there is only one alternate
+ setting, though the USB Spec allows devices with no additional alternate setting
+ return an error to the Host in response to a SET_INTERFACE request.
+
+ If you are in doubt, you may want to call it with no arguments wrapped by
+ a try/except clause:
+
+ >>> try:
+ >>> dev.set_interface_altsetting()
+ >>> except usb.core.USBError:
+ >>> pass
+ """
+ self._ctx.managed_set_interface(self, interface, alternate_setting)
+
+ def reset(self):
+ r"""Reset the device."""
+ self._ctx.dispose(self, False)
+ self._ctx.backend.reset_device(self._ctx.handle)
+ self._ctx.dispose(self, True)
+
+ def write(self, endpoint, data, interface = None, timeout = None):
+ r"""Write data to the endpoint.
+
+ This method is used to send data to the device. The endpoint parameter
+ corresponds to the bEndpointAddress member whose endpoint you want to
+ communicate with. The interface parameter is the bInterfaceNumber field
+ of the interface descriptor which contains the endpoint. If you do not
+ provide one, the first one found will be used, as explained in the
+ set_interface_altsetting() method.
+
+ The data parameter should be a sequence like type convertible to
+ array type (see array module).
+
+ The timeout is specified in miliseconds.
+
+ The method returns the number of bytes written.
+ """
+ backend = self._ctx.backend
+
+ fn_map = {
+ util.ENDPOINT_TYPE_BULK:backend.bulk_write,
+ util.ENDPOINT_TYPE_INTR:backend.intr_write,
+ util.ENDPOINT_TYPE_ISO:backend.iso_write
+ }
+
+ intf = self._ctx.get_interface(self, interface)
+ fn = fn_map[self._ctx.get_endpoint_type(self, endpoint, intf)]
+ self._ctx.managed_claim_interface(self, intf)
+
+ return fn(
+ self._ctx.handle,
+ endpoint,
+ intf.bInterfaceNumber,
+ _interop.as_array(data),
+ self.__get_timeout(timeout)
+ )
+
+ def read(self, endpoint, size, interface = None, timeout = None):
+ r"""Read data from the endpoint.
+
+ This method is used to receive data from the device. The endpoint parameter
+ corresponds to the bEndpointAddress member whose endpoint you want to
+ communicate with. The interface parameter is the bInterfaceNumber field
+ of the interface descriptor which contains the endpoint. If you do not
+ provide one, the first one found will be used, as explained in the
+ set_interface_altsetting() method. The size parameters tells how many
+ bytes you want to read.
+
+ The timeout is specified in miliseconds.
+
+ The method returns an array object with the data read.
+ """
+ backend = self._ctx.backend
+
+ fn_map = {
+ util.ENDPOINT_TYPE_BULK:backend.bulk_read,
+ util.ENDPOINT_TYPE_INTR:backend.intr_read,
+ util.ENDPOINT_TYPE_ISO:backend.iso_read
+ }
+
+ intf = self._ctx.get_interface(self, interface)
+ fn = fn_map[self._ctx.get_endpoint_type(self, endpoint, intf)]
+ self._ctx.managed_claim_interface(self, intf)
+
+ return fn(
+ self._ctx.handle,
+ endpoint,
+ intf.bInterfaceNumber,
+ size,
+ self.__get_timeout(timeout)
+ )
+
+
+ def ctrl_transfer(self, bmRequestType, bRequest, wValue=0, wIndex=0,
+ data_or_wLength = None, timeout = None):
+ r"""Do a control transfer on the endpoint 0.
+
+ This method is used to issue a control transfer over the
+ endpoint 0(endpoint 0 is required to always be a control endpoint).
+
+ The parameters bmRequestType, bRequest, wValue and wIndex are the
+ same of the USB Standard Control Request format.
+
+ Control requests may or may not have a data payload to write/read.
+ In cases which it has, the direction bit of the bmRequestType
+ field is used to infere the desired request direction. For
+ host to device requests (OUT), data_or_wLength parameter is
+ the data payload to send, and it must be a sequence type convertible
+ to an array object. In this case, the return value is the number of data
+ payload written. For device to host requests (IN), data_or_wLength
+ is the wLength parameter of the control request specifying the
+ number of bytes to read in data payload. In this case, the return
+ value is the data payload read, as an array object.
+ """
+ if util.ctrl_direction(bmRequestType) == util.CTRL_OUT:
+ a = _interop.as_array(data_or_wLength)
+ elif data_or_wLength is None:
+ a = 0
+ else:
+ a = data_or_wLength
+
+ self._ctx.managed_open()
+
+ return self._ctx.backend.ctrl_transfer(
+ self._ctx.handle,
+ bmRequestType,
+ bRequest,
+ wValue,
+ wIndex,
+ a,
+ self.__get_timeout(timeout)
+ )
+
+ def is_kernel_driver_active(self, interface):
+ r"""Determine if there is kernel driver associated with the interface.
+
+ If a kernel driver is active, and the object will be unable to perform I/O.
+ """
+ self._ctx.managed_open()
+ return self._ctx.backend.is_kernel_driver_active(self._ctx.handle, interface)
+
+ def detach_kernel_driver(self, interface):
+ r"""Detach a kernel driver.
+
+ If successful, you will then be able to perform I/O.
+ """
+ self._ctx.managed_open()
+ self._ctx.backend.detach_kernel_driver(self._ctx.handle, interface)
+
+ def attach_kernel_driver(self, interface):
+ r"""Re-attach an interface's kernel driver, which was previously
+ detached using detach_kernel_driver()."""
+ self._ctx.managed_open()
+ self._ctx.backend.attach_kernel_driver(self._ctx.handle, interface)
+
+ def __iter__(self):
+ r"""Iterate over all configurations of the device."""
+ for i in range(self.bNumConfigurations):
+ yield Configuration(self, i)
+
+ def __getitem__(self, index):
+ r"""Return the Configuration object in the given position."""
+ return Configuration(self, index)
+
+ def __del__(self):
+ self._ctx.dispose(self)
+
+ def __get_timeout(self, timeout):
+ if timeout is not None:
+ return timeout
+ return self.__default_timeout
+
+ def __set_def_tmo(self, tmo):
+ if tmo < 0:
+ raise ValueError('Timeout cannot be a negative value')
+ self.__default_timeout = tmo
+
+ def __get_def_tmo(self):
+ return self.__default_timeout
+
+ default_timeout = property(
+ __get_def_tmo,
+ __set_def_tmo,
+ doc = 'Default timeout for transfer I/O functions'
+ )
+
+def find(find_all=False, backend = None, custom_match = None, **args):
+ r"""Find an USB device and return it.
+
+ find() is the function used to discover USB devices.
+ You can pass as arguments any combination of the
+ USB Device Descriptor fields to match a device. For example:
+
+ find(idVendor=0x3f4, idProduct=0x2009)
+
+ will return the Device object for the device with
+ idVendor Device descriptor field equals to 0x3f4 and
+ idProduct equals to 0x2009.
+
+ If there is more than one device which matchs the criteria,
+ the first one found will be returned. If a matching device cannot
+ be found the function returns None. If you want to get all
+ devices, you can set the parameter find_all to True, then find
+ will return an list with all matched devices. If no matching device
+ is found, it will return an empty list. Example:
+
+ printers = find(find_all=True, bDeviceClass=7)
+
+ This call will get all the USB printers connected to the system.
+ (actually may be not, because some devices put their class
+ information in the Interface Descriptor).
+
+ You can also use a customized match criteria:
+
+ dev = find(custom_match = lambda d: d.idProduct=0x3f4 and d.idvendor=0x2009)
+
+ A more accurate printer finder using a customized match would be like
+ so:
+
+ def is_printer(dev):
+ import usb.util
+ if dev.bDeviceClass == 7:
+ return True
+ for cfg in dev:
+ if usb.util.find_descriptor(cfg, bInterfaceClass=7) is not None:
+ return True
+
+ printers = find(find_all=True, custom_match = is_printer)
+
+ Now even if the device class code is in the interface descriptor the
+ printer will be found.
+
+ You can combine a customized match with device descriptor fields. In this
+ case, the fields must match and the custom_match must return True. In the our
+ previous example, if we would like to get all printers belonging to the
+ manufacturer 0x3f4, the code would be like so:
+
+ printers = find(find_all=True, idVendor=0x3f4, custom_match=is_printer)
+
+ If you want to use find as a 'list all devices' function, just call
+ it with find_all = True:
+
+ devices = find(find_all=True)
+
+ Finally, you may pass a custom backend to the find function:
+
+ find(backend = MyBackend())
+
+ PyUSB has builtin backends for libusb 0.1, libusb 1.0 and OpenUSB.
+ If you do not supply a backend explicitly, find() function will select
+ one of the predefineds backends according to system availability.
+
+ Backends are explained in the usb.backend module.
+ """
+
+ def device_iter(k, v):
+ for dev in backend.enumerate_devices():
+ d = Device(dev, backend)
+ if (custom_match is None or custom_match(d)) and \
+ _interop._reduce(
+ lambda a, b: a and b,
+ map(
+ operator.eq,
+ v,
+ map(lambda i: getattr(d, i), k)
+ ),
+ True
+ ):
+ yield d
+
+ if backend is None:
+ import usb.backend.libusb10 as libusb10
+ import usb.backend.libusb01 as libusb01
+ import usb.backend.openusb as openusb
+
+ for m in (libusb10, openusb, libusb01):
+ backend = m.get_backend()
+ if backend is not None:
+ _logger.info('find(): using backend "%s"', m.__name__)
+ break
+ else:
+ raise ValueError('No backend available')
+
+ k, v = args.keys(), args.values()
+
+ if find_all:
+ return [d for d in device_iter(k, v)]
+ else:
+ try:
+ return _interop._next(device_iter(k, v))
+ except StopIteration:
+ return None