123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839 |
- # 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:
- 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.
- """
- pass
- 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'
- )
- )
- 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
|