Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

* Minor docs and messages improvements.

 * Tutorial update.


git-svn-id: https://pyusb.svn.sourceforge.net/svnroot/pyusb/trunk@33 3043e840-054b-0410-bc1a-858b58f158f7
  • Loading branch information...
commit 97c767c25ff1554da0b6557cc923ab6abc98fe42 1 parent d320f10
@walac authored
Showing with 115 additions and 40 deletions.
  1. +115 −40 docs/{tutorial → }/tutorial.rst
View
155 docs/tutorial/tutorial.rst → docs/tutorial.rst
@@ -11,17 +11,17 @@ PyUSB 1.0 is a Python_ library allowing easy USB_ access. It has the following f
Unlike the previous 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.
-Backend neutrality:
+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 bultins backends for libusb 0.1, libusb 1.0 and OpenUSB.
+ interface. PyUSB comes with bultin 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.3, ctypes_ and at least one of the
- suppoted USB libraries.
+ suppoted USB bultin 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 devices.
+ but PyUSB has good defaults for most common configurations.
Support for isochronous transfers:
PyUSB supports isochronous transfer if the underline backend supports it.
@@ -35,8 +35,8 @@ Enough talk, let's code!
Who's who
---------
-First of all, let's do an overview on the PyUSB modules. PyUSB modules are under
-the usb package. This package has the following contents:
+First of all, let's give an overview on the PyUSB modules. PyUSB modules are under
+the ``usb`` package. This package has the following contents:
======= ===========
Content Description
@@ -49,14 +49,21 @@ 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()
+ >>> import usb.core
+ >>> dev = usb.core.find()
+
+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.
Where are you?
--------------
-The ``find()`` function present in the ``core`` module is used to
-find and enumerate devices present in the system. For example, let's
+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::
@@ -66,17 +73,17 @@ equals to 0x0001. If we would like to find it, we would do so::
if dev is None:
raise ValueError('Our device is not connected')
-Just it, the function will return a ``usb.core.Device`` object representing
+Just it, the function will return an ``usb.core.Device`` object representing
our device. It 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 we find an USB printer connected? This
-is far easy::
+if we would like to discover if the 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')
-This 7 is the code for the printer class according to the USB specification.
+This 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...
@@ -90,19 +97,19 @@ What happend? 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 the a true value,
-``find`` instead will return a list with all device matching the criteria.
-That's it! Simple.
+``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 its 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. "I got tired reading this, image implementing it?!?!?!
+field equals to 7. "I got tired reading this, imagine implementing it?!?!?!
I am a `programmer <http://en.wikipedia.org/wiki/Laziness>`_", you say.
Yes, I am one two, that's because I have implemented some
-stuff to make our lives easier. First, let's give a look on the final
-code to find all printers connected::
+stuff to make our lives a bit easier. First, let's give a look on the
+final code to find all printers connected::
import usb.core
import usb.util
@@ -128,13 +135,15 @@ code to find all printers connected::
printers = usb.core.find(find_all=1, custom_match=find_all(7))
-The ``custom_match`` accepts any callable object that receives the device
+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-match
-device. You can also combine ``custom_match`` with device fields::
+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_all(7), idVendor=0xfffe)
+Here we are only interested in the printers of the 0xfffe vendor.
+
Describe yourself
-----------------
@@ -157,7 +166,7 @@ device::
sys.stdout.write(str(cfg.bConfigurationValue) + '\n')
In the same way, you can iterate over a configuration to access the interfaces,
-and iterator over the interface to access its endpoints. Each kind of object has
+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:
@@ -182,13 +191,13 @@ You can also use the subscript operator to access the descriptors randomly, like
>>> # third endpoint
>>> ep = intf[2]
-As you can see, the index is zero based. But wait! There is something weird in way
+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 an tuple of two item, with the first one being the index of the Interface and
-the second one, the alternate setting. So, access the first interface, but its second
+accepts an tuple 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 descriptor, the ``find_descriptor``
+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:
@@ -209,7 +218,7 @@ How am I supposed to work?
--------------------------
USB devices after connected must be configured through a few requests. When
-I first started to study USB_ spec, I found myself confused with descriptors,
+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
@@ -218,11 +227,11 @@ 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).
+value to use is annoying (although most code I have seem simply hardcodes it).
Therefore, in PyUSB, you can just issue a ``set_configuration`` call with no
-parameters. In this case, I will set the first configuration found (if your
+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 you have a device with one configuration descriptor
+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::
@@ -235,8 +244,8 @@ work equally::
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 configuration you may have or may not have to do the interface alternate
-setting. Ok, a crash course on USB: each device can have only one activated configuration
+The other configuration you may or may not have to set is the interface alternate
+setting. Ok, a crash course: 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
@@ -248,13 +257,13 @@ 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 have one or
+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 each interface. For example, USB_ spec says that a device cannot
+setting active. For example, USB spec says that a device cannot
have a isochronous endpoint in its primary alternate setting [#]_, so a streaming device
-has to have at least two alternate settting, with the second one having the isochronous
+has to 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::
@@ -265,8 +274,9 @@ through the ``set_interface_altsetting`` function::
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 not, the safesty way is wrap the
- ``set_interface_altsetting`` call with a try-except block, like so::
+ 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(...)
@@ -274,7 +284,7 @@ through the ``set_interface_altsetting`` function::
pass
You can also use an ``Interface`` object as parameter to the function, the
-``interface`` and ``alternate_setting`` parameters are automatically infered
+``interface`` and ``alternate_setting`` parameters are automatically inferred
from ``bInterfaceNumber`` and ``bAlternateSetting`` fields. Example::
>>> intf = find_descriptor(...)
@@ -284,6 +294,68 @@ from ``bInterfaceNumber`` and ``bAlternateSetting`` fields. Example::
.. warning::
The ``Interface`` object must belong to the active configuration descriptor.
+Talk to me, honey
+-----------------
+
+Now it's time for we learn how to communicate with USB devices. USB spec has four
+kinds of transfers: bulk, interrupt, isochronous and control. I do not intend
+to explain the purpose of each transfer and the differences among them. Therefore,
+I assume you know at least the basics of USB transfers.
+
+Control transfer is the unique transfer that has structured data specified in the
+spec. Because of it, you have a different function to deal with control transfers,
+the other transfers are managed by the same functions.
+
+You do a control transfer through the ``ctrl_transfer`` method. It is used both for
+OUT and IN transfers. The transfer direction is inferred through 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 = ''.join([chr(x) for x in dev.ctrl_transfer(0x40, CTRL_LOOPBACK_READ, 0, 0, len(msg))])
+ >>> assert ret == msg
+
+The beggining 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 in 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).
+
+It is assumed that we have created a device specific pair of control requests that implement
+a loopback pipe. What you write with the ``CTRL_LOOPBACK_WRITE`` message, you can read with the
+``CTRL_LOOPBACK_READ`` message. In an OUT transfer, the return value is the number of bytes
+really written in the payload. In an IN transfer, the return value is an ``array.array`` object
+with the data read.
+
+For the other transfers, you use the method ``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)) == msg
+ >>> ret = ''join([chr(x) for x in dev.read(0x81, len(msg), 0, 100)])
+ >>> assert ret == msg
+
+The first, third and fourth parameters are equal for both methods, they are the endpoint
+address, interface number and timeout, respectivelly. The third parameter is the data
+payload (write) and the number of bytes to read (read). The return of the ``read``
+function is an instance of the ``array.array`` object and 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.
+
+Additional Topics
+=================
+
+TODO.
+
.. [#] 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 and false.
@@ -296,14 +368,17 @@ from ``bInterfaceNumber`` and ``bAlternateSetting`` fields. Example::
.. [#] Actually things are a little more complex, but this simple explanation is enough
for us.
-.. [#] I know it is weird.
+.. [#] 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 a
+.. [#] 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).
+
.. _libusb: http://www.libusb.org
.. _OpenUSB: http://openusb.wiki.sourceforge.net
.. _USB: http://www.usb.org
Please sign in to comment.
Something went wrong with that request. Please try again.