123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533 |
- ==========================
- Programming with PyUSB 1.0
- ==========================
- Let me introduce myself
- =======================
- PyUSB 1.0 is a Python_ library allowing easy USB_ access. It has the following features:
- 100% written in Python:
- Unlike the 0.x version, which is written in C, 1.0 version is written in Python.
- This allows Python programmers with no background in C to understand better how PyUSB
- works.
- Platform neutrality:
- 1.0 version implements a frontend-backend scheme. This isolates the API from system
- specific implementation details. The glue between the two layers is the ``IBackend``
- interface. PyUSB comes with builtin backends for libusb 0.1, libusb 1.0 and OpenUSB.
- You can write your own backend if you desire to.
- Portability:
- PyUSB should run on any platform with Python >= 2.4, ctypes_ and at least one of the
- supported builtin backends.
- Easiness:
- Communicating with an USB_ device has never been so easy! USB is a complex protocol,
- but PyUSB has good defaults for most common configurations.
- Support for isochronous transfers:
- PyUSB supports isochronous transfers if the underline backend supports it.
- Although PyUSB makes USB programming less painful, it is assumed in this tutorial that
- you have a minimal USB protocol background. If you don't know anything about USB, I
- recommend you the excellent Jan Axelson's book **USB Complete**.
- Enough talk, let's code!
- ========================
- Who's who
- ---------
- First of all, let's give an overview on the PyUSB modules. PyUSB modules are under
- the ``usb`` package. This package has the following modules:
- ======= ===========
- Content Description
- ------- -----------
- core The main USB module.
- util Utility functions.
- control Standard control requests.
- legacy The 0.x compatibility layer.
- backend A subpackage containing the builtin backends.
- ======= ===========
- For example, to import the ``core`` module, you do as so::
- >>> import usb.core
- >>> dev = usb.core.find()
- Let's get it started
- --------------------
- Following is a simplistic program that sends the 'test' string to the first OUT endpoint
- found::
- import usb.core
- import usb.util
- # find our device
- dev = usb.core.find(idVendor=0xfffe, idProduct=0x0001)
- # was it found?
- if dev is None:
- raise ValueError('Device not found')
- # set the active configuration. With no arguments, the first
- # configuration will be the active one
- dev.set_configuration()
- # get an endpoint instance
- ep = usb.util.find_descriptor(
- dev.get_interface_altsetting(), # first interface
- # match the first OUT endpoint
- custom_match = \
- lambda e: \
- usb.util.endpoint_direction(e.bEndpointAddress) == \
- usb.util.ENDPOINT_OUT
- )
- assert ep is not None
- # write the data
- ep.write('test')
- The first two lines import PyUSB package modules. ``usb.core`` is the main module, and
- ``usb.util`` contains utility functions. The next command searches our device
- and returns an instance object if it is found. If not, ``None`` is returned.
- After, we set the configuration to use. Note that no argument indicating what
- configuration we want was supplied. As you will see, many PyUSB functions
- have defaults for most common devices. In this case, the configuration set is
- the first one found.
- Then, we look for the endpoint we are interested. We search it inside the first
- interface we have. After finding the endpoint, we send data to it.
- If we know the endpoint address in advance, we could just call the ``write`` function
- from the device object::
- dev.write(1, 'test', 0)
- Here we write the string 'test' at endpoint address *1* of the interface number *0*.
- All these functions will be detailed in the next sections.
- What's wrong?
- -------------
- Every function in PyUSB raises an exception in case of an error. Besides the `Python
- standard exceptions <http://docs.python.org/library/exceptions.html>`_, PyUSB defines
- the ``usb.core.USBError`` for USB related errors.
- You can also use the PyUSB log funcionality. It uses the `logging
- <http://docs.python.org/library/logging.html>`_ module. To enable it, define
- the environment variable ``PYUSB_DEBUG_LEVEL`` with one of the following level
- names: ``critical``, ``error``, ``warning``, ``info`` or ``debug``.
- By default the messages are sent to `sys.stderr <http://docs.python.org/library/sys.html>`_.
- If you want to, you can redirect log messages to a file by defining the ``PYUSB_LOG_FILENAME``
- environment variable. If its value is a valid file path, messages will be written to it,
- otherwise it will be sent to ``sys.stderr``.
- Where are you?
- --------------
- The ``find()`` function in the ``core`` module is used to
- find and enumerate devices connected to the system. For example, let's
- say that our device has a vendor id equals to 0xfffe and product id
- equals to 0x0001. If we would like to find it, we would do so::
- import usb.core
- dev = usb.core.find(idVendor=0xfffe, idProduct=0x0001)
- if dev is None:
- raise ValueError('Our device is not connected')
- Just it, the function will return an ``usb.core.Device`` object representing
- our device. If the device is not found, it returns ``None``. Actually, you
- can use any field of the Device Descriptor_ you desire. For example, what
- if we would like to discover if there is an USB printer connected to the system?
- This is far easy::
- # actually this is not the whole history, keep reading
- if usb.core.find(bDeviceClass=7) is None:
- raise ValueError('No printer found')
- The 7 is the code for the printer class according to the USB standard.
- Hey, wait, what if I want to enumerate all printers present? No problem::
- # this is not the whole history yet...
- printers = usb.core.find(find_all=True, bDeviceClass=7)
- # Python 2, Python 3, to be or not to be
- import sys
- sys.stdout.write('There are ' + len(printers) + ' in the system\n.')
- What happened? Well, it is time for a little explanation... ``find``
- has a parameter called ``find_all`` that defaults to False. When it is
- false [#]_, ``find`` will return the first device found that matches the
- specified criteria (more on it soon). If you give it a true value,
- ``find`` instead will return a list with all devices matching the criteria.
- That's it! Simple, doesn't it?
- Finished? No! I have not told you the whole history: many devices actually
- put their class information in the Interface Descriptor_ instead of the
- Device Descriptor_. So, to really find all printers connected to the
- system, we would need to transverse all configurations, and then
- all interfaces and check if one of the interfaces has its bInterfaceClass
- field equals to 7. If you are a
- `programmer <http://en.wikipedia.org/wiki/Laziness>`_" like me, you might be wondering
- if there is an easier way to do that. The answer is yes, it does. Firstly, let's
- give a look on the final code to find all printers connected::
- import usb.core
- import usb.util
- import sys
- class find_class(object):
- def __init__(self, class_):
- self._class = class_
- def __call__(self, device):
- # first, let's check the device
- if device.bDeviceClass == self._class:
- return True
- # ok, transverse all devices to find an
- # interface that matches our class
- for cfg in device:
- # find_descriptor: what's it?
- intf = usb.util.find_descriptor(
- cfg,
- bInterfaceClass=self._class
- )
- if intf is not None:
- return True
- return False
- printers = usb.core.find(find_all=1, custom_match=find_all(7))
- The ``custom_match`` parameter accepts any callable object that receives the device
- object. It must return true for a matching device, and false for a non-matching
- device. You can also combine ``custom_match`` with device fields if you want::
- # find all printers that belongs to our vendor:
- printers = usb.core.find(find_all=1, custom_match=find_class(7), idVendor=0xfffe)
- Here we are only interested in the printers of the 0xfffe vendor.
- Describe yourself
- -----------------
- Ok, we've found our device, but before talking to it, we would like
- to know more about it, you know, configurations, interfaces, endpoints,
- transfer types...
- If you have a device, you can access any device descriptor fields as object
- properties::
- >>> dev.bLength
- >>> dev.bNumConfigurations
- >>> dev.bDeviceClass
- >>> # ...
- To access the configurations available in the device, you can iterate over the
- device::
- for cfg in dev:
- sys.stdout.write(str(cfg.bConfigurationValue) + '\n')
- In the same way, you can iterate over a configuration to access the interfaces,
- and iterate over the interfaces to access their endpoints. Each kind of object has
- as attributes the fields of the respective descriptor. Let's see an example::
- for cfg in dev:
- sys.stdout.write(str(cfg.bConfigurationValue) + '\n')
- for intf in cfg:
- sys.stdout.write('\t' + \
- str(intf.bInterfaceNumber) + \
- ',' + \
- str(intf.bAlternateSetting) + \
- '\n')
- for ep in intf:
- sys.stdout.write('\t\t' + \
- str(ep.bEndpointAddress) + \
- '\n')
- You can also use the subscript operator to access the descriptors randomly, like that::
- >>> # access the second configuration
- >>> cfg = dev[1]
- >>> # access the first interface
- >>> intf = cfg[(0,0)]
- >>> # third endpoint
- >>> ep = intf[2]
- As you can see, the index is zero based. But wait! There is something weird in the way
- I access an interface... Yes, you are right, the subscript operator in the Configuration
- accepts a sequence of two items, with the first one being the index of the Interface and
- the second one, the alternate setting. So, to access the first interface, but its second
- alternate setting, we write ``cfg[(0,1)]``.
- Now it's time to we learn a powerfull way to find descriptors, the ``find_descriptor``
- utility function. We have already seem it in the printer finding example.
- ``find_descriptor`` works in almost the same way as ``find``, with two exceptions:
- * ``find_descriptor`` receives as its first parameter the parent descriptor that you
- will search on.
- * There is no ``backend`` [#]_ parameter.
- For example, if we have a configuration descriptor ``cfg`` and want to find all
- alternate setttings of the interface 1, we do so::
- import usb.util
- alt = usb.util.find_descriptor(cfg, find_all=True, bInterfaceNumber=1)
- Note that ``find_descriptor`` is in the ``usb.util`` module. It also
- accepts the early described ``custom_match`` parameter.
- How am I supposed to work?
- --------------------------
- USB devices after connected must be configured through a few standard requests.
- When I got started to study USB_ spec, I found myself confused with descriptors,
- configurations, interfaces, alternate settings, transfer types and all this
- stuff... And worst, you cannot simply ignore them, a device does not work
- without setting a configuration, even if it has just one! PyUSB tries to
- make your life as easy as possible. For example, after getting your device
- object, one of the first things you need to do before communicating with it
- is issueing a ``set_configuration`` request. The parameter for this request
- is the ``bConfigurationValue`` of the configuration you are interested in.
- Most devices has no more than one configuration, and tracking the configuration
- value to use is annoying (although most code I have seem simply hardcode it).
- Therefore, in PyUSB, you can just issue a ``set_configuration`` call with no
- parameters. In this case, it will set the first configuration found (if your
- device has just one, you don't need to worry about the configuration value
- at all). For example, let's imagine you have a device with one configuration descriptor
- with its bConfigurationValue field equals to 5 [#]_, the following ways bellow will
- work equally::
- >>> dev.set_configuration(5)
- >>> dev.set_configuration() # we assume the configuration 5 is the first one
- >>> cfg = util.find_descriptor(dev, bConfiguration=5)
- >>> cfg.set()
- >>> dev.set_configuration(cfg)
- Wow! You can use a ``Configuration`` object as a parameter to ``set_configuration``!
- Yes, and also it has a ``set`` method to configure itself as the current configuration.
- The other setting you might or might not have to configure is the interface alternate
- setting. Each device can have only one activated configuration at a time, and each
- configuration may have more than one interface, and you can use all interfaces at the
- same time. You better understand this concept if you think of an interface as a logical
- device. For example, let's imagine a multifunction printer, which is at the same time a
- printer and a scanner. To keep things simple (or at least as simple as we can), let's
- consider it has just one configuration. As we have a printer and a scanner, the configuration
- has two interfaces, one for the printer and one for the scanner. A device with more than
- one interface is called a composite device. When you connect your multifunction printer
- to your computer, the Operating System would load two different drivers: one for each
- "logical" peripheral you have [#]_.
- And about the alternate setting? Good you have asked. An interface has one or
- more alternate settings. An interface with just one alternate setting is considered
- to not having an alternate settting [#]_. Alternate settings are for interfaces which
- configurations are for devices, i.e, for each interface, you can have only one alternate
- setting active. For example, USB spec says that a device cannot
- have a isochronous endpoint in its primary alternate setting [#]_, so a streaming device
- must have at least two alternate setttings, with the second one having the isochronous
- endpoint(s). But as opposed to configurations, interfaces with just one alternate
- setting don't need to be set [#]_. You select an interface alternate setting
- through the ``set_interface_altsetting`` function::
- >>> dev.set_interface_altsetting(interface = 0, alternate_setting = 0)
- .. warning::
- The USB spec says that a device is allowed to return an error in case it
- receives a SET_INTERFACE request for an interface that has no additional
- alternate settings. So, if you are not sure if the interface has more
- than one alternate setting or it accepts a SET_INTERFACE request,
- the safesty way is to call ``set_interface_altsetting`` inside an
- try-except block, like so::
- try:
- dev.set_interface_altsetting(...)
- except USBError:
- pass
- You can also use an ``Interface`` object as parameter to the function, the
- ``interface`` and ``alternate_setting`` parameters are automatically inferred
- from ``bInterfaceNumber`` and ``bAlternateSetting`` fields. Example::
- >>> intf = find_descriptor(...)
- >>> dev.set_interface_altsetting(intf)
- >>> intf.set_altsetting() # wow! Interface also has a method for it
- .. warning::
- The ``Interface`` object must belong to the active configuration descriptor.
- Talk to me, honey
- -----------------
- Now it's time to we learn how to communicate with USB devices. USB has four
- flavors of transfers: bulk, interrupt, isochronous and control. I don't intend
- to explain the purpose of each transfer and the differences among them. Therefore,
- I assume you know at least the basics of the USB transfers.
- Control transfer is the unique transfer that has structured data described in the
- spec, the others just send and receive raw data from USB point of view. Because of it,
- you have a different function to deal with control transfers,
- all the other transfers are managed by the same functions.
- You issue a control transfer through the ``ctrl_transfer`` method. It is used both for
- OUT and IN transfers. The transfer direction is determined from the ``bmRequestType``
- parameter.
- The ``ctrl_transfer`` parameters are almost equal to the control request
- structure. Following is a example of how to do a control transfer [#]_::
- >>> msg = 'test'
- >>> assert dev.ctrl_transfer(0x40, CTRL_LOOPBACK_WRITE, 0, 0, msg) == len(msg)
- >>> ret = dev.ctrl_transfer(0x40, CTRL_LOOPBACK_READ, 0, 0, len(msg))
- >>> sret = ''.join([chr(x) for x in ret])
- >>> assert sret == msg
- In this example, it is assumed that our device implements two custom control requests that act
- as a loopback pipe. What you write with the ``CTRL_LOOPBACK_WRITE`` message, you can read with the
- ``CTRL_LOOPBACK_READ`` message.
- The first four parameters are the ``bmRequestType``, ``bmRequest``, ``wValue`` and
- ``wIndex`` fields of the standard control transfer structure. The fifth parameter is either
- the data payload for an OUT transfer or the number of bytes to read in an IN transfer.
- The data payload can be any sequence type that can be used as a parameter for the array_
- ``__init__`` method. If there is no data payload, the parameter should be ``None`` (or 0 in case
- of an IN transfer). There is one last optional parameter specifying the timeout of the operation.
- If you don't supply it, a default timeout will be used (more on that later). In an OUT transfer,
- the return value is the number of bytes really sent to the device. In an IN transfer, the return
- value is an array_ object with the data read.
- For the other transfers, you use the methods ``write`` and ``read``, respectivelly, to
- write and read data. You don't need to worry about the transfer type, it is automatically
- determined from the endpoint address. Here is our loopback example assuming the we have
- a loopback pipe in the endpoint 1::
- >>> msg = 'test'
- >>> assert len(dev.write(1, msg, 0, 100)) == len(msg)
- >>> ret = dev.read(0x81, len(msg), 0, 100)
- >>> sret = ''.join([chr(x) for x in ret])
- >>> assert sret == msg
- The first, third and fourth parameters are equal for both methods, they are the endpoint
- address, interface number and timeout, respectivelly. The second parameter is the data
- payload (write) or the number of bytes to read (read). The return of the ``read``
- function is an instance of the array_ object or the number of bytes written
- for the ``write`` method.
- As in ``ctrl_transfer``, the ``timeout`` parameter is optional. When the ``timeout``
- is omitted, it is used the ``Device.default_timeout`` property as the operation timeout.
- Control yourself
- ----------------
- Besides the transfers functions, the module ``usb.control`` offers functions which
- implement the standard USB control requests and the ``usb.util`` module has the
- convenience function ``get_string`` specifically to return string descriptors.
- Additional Topics
- =================
- Behind every great abstraction, there's a great implementation
- --------------------------------------------------------------
- On early days, there was only libusb_. Then came libusb 1.0, and now we had libusb 0.1 and 1.0.
- After, they created OpenUSB_, and now we live at the
- `Tower of Babel <http://en.wikipedia.org/wiki/Tower_of_Babel>`_ of the USB libraries [#]_.
- How does PyUSB deal with it? Well, PyUSB is a democratic library, you may choose whatever
- library you want. Actually, you can write your own USB library from scratch and tell
- PyUSB to use it.
- The ``find`` function has one more parameter that I haven't told you. It is the ``backend``
- parameter. If you don't supply it, it will be used one of the builtin backends. A backend
- is a object derived from ``usb.backend.IBackend``, responsible to implement the operating
- system specific USB stuff. As you might guess, the builtins are libusb 0.1, libusb 1.0 and
- OpenUSB backends.
- You can create you own backend and use it. Just inherit from ``IBackend`` and implement
- the methods necessary. You might want to give a look at ``backend`` package documentation
- to learn how to do that.
- Don't be selfish
- ----------------
- Python has what we say *automatic memory management*. This means that the virtual machine
- will take care about when to release objects from the memory. Under the hoods, PyUSB manages
- all low level resource management it needs to work (interface claiming, device handles, etc.)
- internally and most of users don't need to worry about that. But, because of the nonderterminisc
- nature of automatic object destruction of Python, users cannot predict when the resources
- allocated will be released. Some applications need to allocate and free the resources deterministically.
- For these kind of applications, the ``usb.util`` module has a set of functions to deal with resource
- management.
- If you want to claim and release interfaces manually, you may use the ``claim_interface``
- and ``release_interface`` functions. ``claim_interface`` will claim the specified interface
- if the device has not done it yet. If the device already claimed the interface, it does nothing.
- In a similar way, ``release_interface`` will release the specified interface if it is claimed.
- If the interface is not claimed, it does nothing. You can use manual interface claim to solve
- the `configuration selection problem <http://libusb.sourceforge.net/api-1.0/caveats.html>`_
- described in the libusb_ documentation.
- If you want to free all resources allocated by the device object (including interfaces claimed),
- you can use the ``dispose_resources`` function. It releases all resources allocated and put the
- device object (but not the device hardware itself) in the state it was at the time when the ``find``
- function returned.
- Oldschool rules
- ---------------
- If you wrote an application using the old PyUSB API (0.whatever), you may be asking yourself
- if you need to update your code to use the new API. Well, you should, but you don't need to. PyUSB
- 1.0 comes with the ``usb.legacy`` compatibility module. It implements the older API above the
- new API. "So, do I have just to replace my ``import usb`` statement with ``import usb.legacy as
- usb`` to get my application working?", you ask. The answer is yes, it will, but you don't have
- to. If you run your application untouched it will just work, because the ``import usb`` statement
- will import all public symbols from ``usb.legacy``. If you face a problem, probably you found a bug.
- Help me, please
- ---------------
- If you need help, **do not email me**, the mailing list is there for this. Subscribe instructions
- can be found at the PyUSB_ website.
- What do you think about it?
- ---------------------------
- At alpha stage, users of PyUSB are invited to give their opinion about the PyUSB API.
- If you think a feature is hard to use and you have a better idea, open a new thread
- in the mailing list so we can discuss about that.
- .. [#] When I say True or False (capitalized), I mean the respectivelly values of the
- Python language. And when I say true and false, I mean any expression in Python
- which evals to true or false.
- .. [#] See backend specific documentation.
- .. [#] USB spec does not impose any sequential value to the configuration value. The same
- is true for interface and alternate setting numbers.
- .. [#] Actually things are a little more complex, but this simple explanation is enough
- for us.
- .. [#] I know it sounds weird.
- .. [#] This is because if there is no bandwidth for isochronous transfer at the device
- configuration time, the device can be successfully enumerated.
- .. [#] This does not happen for configurations because a device is allowed to be in an
- unconfigured state.
- .. [#] In PyUSB, control transfers are only issued in the endpoint 0. It's very very very
- rare a device having an alternate control endpoint (I've never seem such device).
- .. [#] It's just a joke, don't take it serious. Many choices is better than no choice.
- .. _libusb: http://www.libusb.org
- .. _OpenUSB: http://openusb.wiki.sourceforge.net
- .. _USB: http://www.usb.org
- .. _PyUSB: http://pyusb.wiki.sourceforge.net
- .. _Python: http://www.python.org
- .. _ctypes: http://docs.python.org/library/ctypes.html
- .. _Descriptor: http://www.beyondlogic.org/usbnutshell/usb5.htm
- .. _array: http://docs.python.org/library/array.html
|