Pugs continued, “To answer your question about how a driver selectively registers or skips a particular interface of a USB device, you need to understand the significance of the return value of the
probe() callback.” Note that the USB core would invoke probe for all the interfaces of a detected device, except the ones which are already registered — thus, while doing it for the first time, it will probe for all interfaces. Now, if the probe returns 0, it means the driver has registered for that interface. Returning an error code indicates not registering for it. That’s all. “That was simple,” commented Shweta.
“Now, let’s talk about the ultimate — data transfers to and from a USB device,” continued Pugs.
“But before that, tell me, what is this MODULE_DEVICE_TABLE? This has been bothering me since you explained the USB device ID table macros,” asked Shweta, urging Pugs to slow down.
“That’s trivial stuff. It is mainly for the user-space
depmod,” he said. ‘Module’ is another term for a driver, which can be dynamically loaded/unloaded. The macro
MODULE_DEVICE_TABLEgenerates two variables in a module’s read-only section, which is extracted by
depmod and stored in global map files under
/lib/modules/<kernel_version>. Two such files are
modules.pcimap, for USB and PCI device drivers, respectively. This enables auto-loading of these drivers, as we saw the usb-storage driver getting auto-loaded.
USB data transfer
“Time for USB data transfers. Let’s build upon the USB device driver coded in our previous sessions, using the same handy JetFlash pen drive from Transcend, with vendor ID
0x058f and product ID
0x6387,” said Pugs, enthusiastically.
USB, being a hardware protocol, forms the usual horizontal layer in the kernel space. And hence, for it to provide an interface to user-space, it has to connect through one of the vertical layers. As the character (driver) vertical has already been discussed, it is the current preferred choice for the connection with the USB horizontal, in order to understand the complete data transfer flow.
Also, we do not need to get a free unreserved character major number, but can use the character major number 180, reserved for USB-based character device files. Moreover, to achieve this complete character driver logic with the USB horizontal in one go, the following are the APIs declared in
Usually, we would expect these functions to be invoked in the constructor and the destructor of a module, respectively. However, to achieve the hot-plug-n-play behaviour for the (character) device files corresponding to USB devices, these are instead invoked in the probe and disconnect callbacks, respectively.
The first parameter in the above functions is the interface pointer received as the first parameter in both probe and disconnect. The second parameter,
struct usb_class_driver, needs to be populated with the suggested device file name and the set of device file operations, before invoking
usb_register_dev. For the actual usage, refer to the functions
pen_disconnect in the code listing of
Moreover, as the file operations (write, read, etc.,) are now provided, that is exactly where we need to do the data transfers to and from the USB device. So,
pen_ readbelow show the possible calls to
usb_bulk_msg() (prototyped in
<linux/usb.h>) to do the transfers over the pen drive’s bulk end-points 0×01 and 0×82, respectively. Refer to the ‘E’ lines of the middle section in Figure 1 for the endpoint number listings of our pen drive.
Figure 1: USB specifications for the pen drive
Refer to the header file
<linux/usb.h> under kernel sources, for the complete list of USB core API prototypes for other endpoint-specific data transfer functions like
usb_sndbulkpipe(), and many such other macros, also defined in
<linux/usb.h>, compute the actual endpoint bit-mask to be passed to the various USB core APIs.
Note that a pen drive belongs to a USB mass storage class, which expects a set of SCSI-like commands to be transacted over the bulk endpoints. So, a raw read/write as shown in the code listing below may not really do a data transfer as expected, unless the data is appropriately formatted. But still, this summarises the overall code flow of a USB driver. To get a feel of a real working USB data transfer in a simple and elegant way, one would need some kind of custom USB device, something like the one available here.
As a reminder, the usual steps for any Linux device driver may be repeated with the above code, along with the following steps for the pen drive:
- Build the driver (
pen_driver.ko) by running
- Load the driver using
- Plug in the pen drive (after making sure that the
usb-storagedriver is not already loaded).
- Check for the dynamic creation of
/dev/pen0(0 being the minor number obtained — check
dmesglogs for the value on your system).
- Possibly try some write/read on
/dev/pen0(you most likely will get a connection timeout and/or broken pipe errors, because of non-conforming SCSI commands).
- Unplug the pen drive and look for
/dev/pen0to be gone.
- Unload the driver using
Meanwhile, Pugs hooked up his first-of-its-kind creation — the Linux device driver kit (LDDK) — into his system for a live demonstration of the USB data transfers.
“Aha! Finally a cool complete working USB driver,” quipped Shweta, excited. “Want to have more fun? We could do a block driver over it…,” added Pugs. “Oh! Really?” asked Shweta, with glee. “Yes. But before that, we need to understand the partitioning mechanisms,” commented Pugs.