Device Drivers, Part 13: Data Transfer to and from USB Devices

USB device drivers

This article, which is part of the series on Linux device drivers, continues from the previous two articles. It details the ultimate step of data transfer to and from a USB device, using your first USB driver in Linux.

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_TABLE generates 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.usbmap and 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 <linux/usb.h>:

int usb_register_dev(struct usb_interface *intf, struct usb_class_driver *class_driver);
void usb_deregister_dev(struct usb_interface *intf, struct usb_class_driver *class_driver);

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_probe and pen_disconnect in the code listing of pen_driver.c below.

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_write and pen_ read below 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.

USB specifications for the 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_control_msg(), usb_interrupt_msg(), etc. usb_rcvbulkpipe(), 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.

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/usb.h>

#define MIN(a,b) (((a) <= (b)) ? (a) : (b))
#define BULK_EP_OUT 0x01
#define BULK_EP_IN 0x82
#define MAX_PKT_SIZE 512

static struct usb_device *device;
static struct usb_class_driver class;
static unsigned char bulk_buf[MAX_PKT_SIZE];

static int pen_open(struct inode *i, struct file *f)
{
    return 0;
}
static int pen_close(struct inode *i, struct file *f)
{
    return 0;
}
static ssize_t pen_read(struct file *f, char __user *buf, size_t cnt, loff_t *off)
{
    int retval;
    int read_cnt;

    /* Read the data from the bulk endpoint */
    retval = usb_bulk_msg(device, usb_rcvbulkpipe(device, BULK_EP_IN),
            bulk_buf, MAX_PKT_SIZE, &read_cnt, 5000);
    if (retval)
    {
        printk(KERN_ERR "Bulk message returned %d\n", retval);
        return retval;
    }
    if (copy_to_user(buf, bulk_buf, MIN(cnt, read_cnt)))
    {
        return -EFAULT;
    }

    return MIN(cnt, read_cnt);
}
static ssize_t pen_write(struct file *f, const char __user *buf, size_t cnt, loff_t *off)
{
    int retval;
    int wrote_cnt = MIN(cnt, MAX_PKT_SIZE);

    if (copy_from_user(bulk_buf, buf, MIN(cnt, MAX_PKT_SIZE)))
    {
        return -EFAULT;
    }

    /* Write the data into the bulk endpoint */
    retval = usb_bulk_msg(device, usb_sndbulkpipe(device, BULK_EP_OUT),
            bulk_buf, MIN(cnt, MAX_PKT_SIZE), &wrote_cnt, 5000);
    if (retval)
    {
        printk(KERN_ERR "Bulk message returned %d\n", retval);
        return retval;
    }

    return wrote_cnt;
}

static struct file_operations fops =
{
    .open = pen_open,
    .release = pen_close,
    .read = pen_read,
    .write = pen_write,
};

static int pen_probe(struct usb_interface *interface, const struct usb_device_id *id)
{
    int retval;

    device = interface_to_usbdev(interface);

    class.name = "usb/pen%d";
    class.fops = &fops;
    if ((retval = usb_register_dev(interface, &class)) < 0)
    {
        /* Something prevented us from registering this driver */
        err("Not able to get a minor for this device.");
    }
    else
    {
        printk(KERN_INFO "Minor obtained: %d\n", interface->minor);
    }

    return retval;
}

static void pen_disconnect(struct usb_interface *interface)
{
    usb_deregister_dev(interface, &class);
}

/* Table of devices that work with this driver */
static struct usb_device_id pen_table[] =
{
    { USB_DEVICE(0x058F, 0x6387) },
    {} /* Terminating entry */
};
MODULE_DEVICE_TABLE (usb, pen_table);

static struct usb_driver pen_driver =
{
    .name = "pen_driver",
    .probe = pen_probe,
    .disconnect = pen_disconnect,
    .id_table = pen_table,
};

static int __init pen_init(void)
{
    int result;

    /* Register this driver with the USB subsystem */
    if ((result = usb_register(&pen_driver)))
    {
        err("usb_register failed. Error number %d", result);
    }
    return result;
}

static void __exit pen_exit(void)
{
    /* Deregister this driver with the USB subsystem */
    usb_deregister(&pen_driver);
}

module_init(pen_init);
module_exit(pen_exit);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("Anil Kumar Pugalia <email_at_sarika-pugs_dot_com>");
MODULE_DESCRIPTION("USB Pen Device Driver");

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 make.
  • Load the driver using insmod pen_driver.ko.
  • Plug in the pen drive (after making sure that the usb-storage driver is not already loaded).
  • Check for the dynamic creation of /dev/pen0 (0 being the minor number obtained — check dmesg logs 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/pen0 to be gone.
  • Unload the driver using rmmod pen_driver.

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.

  • Braian Agustin Lopez

    FUA ES RE CROTO LINUX PERO NO ENTRAN VIRUS ESO ES LO BUENO

  • Braian Agustin Lopez

    TIENEN QUE HACER QUE TODOS LOS PROGRAMAS QUE VIENEN PARA WINDOWS TAMBIEN SEAN PARA LINUX

  • rkarthikreddy

    where can i get linux DDK  ? i found a link to a web site for downloading DDK but the server is down!

  • shiavng

    very nice explanation and it gives motivation to those who are learning device driver…
    very admiring work….keep world more technical through sharing such information and innovation on drivers.

    • anil_pugalia

      Thanks for your admiration.

  • plr

    hi sir,
    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.

    I dint understand this correctly.

    What I have understood is , an usb device can be multifunctional and for each functionality there will be a corresponding interface . So a driver is written for an interface rather than the device itself . Now when the device is plugged in , there would be multiple drivers written for multiple interfaces . So the probe function of all the interfaces is called ?

    This is ok . But there can be different vertical drivers assciated with different interfaces and these verticals can have thier own operations defined (fops in case of charcter vertical) . So when a open is called , how will the kernel know ,open is associated with which particular interface ?

    • http://www.facebook.com/anil.pugalia Anil Pugalia

      Kernel would know about the open, as per the registration done by the vertical with the VFS

      • anil_pugalia

        And in the driver, we would register the corresponding minor, as to which interface we are going to handle (as per the USB device data sheet). So, there would always be a unique mapping.

  • Avijit Sur

    Very nicely explained!!! Could you please start another chapter on “partitioning mechanisms” ?

  • manas jyoti sarmah

    Sir,

    Please explain the read() and write() methods.I am unable to implement them.

    • anil_pugalia

      Please share your problem in implementation, and we’ll try to solve that.

  • aaa

    Sir,

    probe is called per interface and it gets called by the usb-core . But In a multifunctional device , how to distinguish between different interfaces becoz what I observed is , we are just mentioning the vendor id and device id in the usb driver structure , which will be same for all the interfaces becoz the device is same . My question is how will the kernel know , which interface has to be called in a multifunctional device ? how does the kernel distinguish between different interfaces of the same device ?

    • anil_pugalia

      Note that the probe function has the first parameter as the pointer to the interface structure, which contains the interface number. So, on probe invocation, we know as for which interface has this probe been called.

  • MenDuong

    Dear, Why I insmod pen_driver.ko appear error message “insmod: error inserting ‘pen_driver.ko’: -1 Device or resource busy”. Thanks

    • http://twitter.com/anil_pugalia Anil Pugalia

      Please paste the output of “dmesg | tail -20″, to be able to decode that error.

      • MenDuong

        Okays. I resolved that error. Cause I haven’t removed pen_info module previous. Thanks!

  • http://twitter.com/jayasantosh1 jayasantosh

    not required to recompile the kernel as anil describe . just compile your module in the name of usb-storage and replace the .ko file in the folder /lib/modules/{your kernel version}/kernel/drivers/usb/storage/usb-storage.ko with your usb-storage.ko file.(note pls take backup of kernel usb-storage.ko)

  • MELWIN

    In the pen_read function the usb_bulk_msg returns an ERROR NO of -32, which is the PIPE BROKEN errno. I ran the same code here. Help !!!

  • resque

    While reading from pen device cat /dev/pen0 bulk message returned error 22 – Invalid argument

    • http://twitter.com/anil_pugalia Anil Pugalia

      Possibly the endpoint number on your pen drive is different. Check it out & correct it accordingly.

  • anon

    I stuck with the problem – while reading, usb_bulk_msg returns error 22 – Invalid argument. Write operation succeeds.

    • anon

      sorry for spamming, i was confused how to read newest posts.

  • anon

    stuck with the problem – while reading, usb_bulk_msg returns error 22 – Invalid argument. Write operation succeeds.

    • http://twitter.com/anil_pugalia Anil Pugalia

      As mentioned below, check out the endpoint number on your pen drive. If it is different correct it accordingly.

  • ayush

    can any one pls tell me what are the parameter passed in this
    usb_bulk_msg function ..

    retval = usb_bulk_msg(device, usb_rcvbulkpipe(device, BULK_EP_IN),
    bulk_buf, MAX_PKT_SIZE, &read_cnt, 5000);

    • http://twitter.com/anil_pugalia Anil Pugalia

      Check out its prototype in the header file <linux/usb.h>

  • jaya

    pls tell me differrence btw

    usb_register_dev(interface, &class)) && usb_register(&pen_driver)) function call i think both are for registering usb with usb subsystem

    • http://twitter.com/anil_pugalia Anil Pugalia

      usb_register registers with the horizontal usb core. usb_register_dev registers the vertical with VFS.

  • jaya

    getting error of permisions inspite of doing chmod command …..pls reply

    root@pcch-ee206270:/home/pcch-ee206270/Desktop/bb/usb_read_write# chmod 777 /dev/pen0
    root@pcch-ee206270:/home/pcch-ee206270/Desktop/bb/usb_read_write# echo “hi” | /dev/pen0
    bash: /dev/pen0: Permission denied
    root@pcch-ee206270:/home/pcch-ee206270/Desktop/bb/usb_read_write#

    • http://twitter.com/anil_pugalia Anil Pugalia

      Please replace | by >, i.e. echo “hi” > /dev/pen0
      Moreover, you do not need chmod 777, just 666 should be good enough.

  • jaya

    PLS someone tell me abt the funda of this usb_class_drive .. its been used many times in above code .. like below .. what this struct actually defines ..??

    static struct usb_class_driver class;
    class.name = “usb/pen%d”;

    class.fops = &fops;
    if ((retval = usb_register_dev(interface, &class)) < 0)

    • http://twitter.com/anil_pugalia Anil Pugalia

      This is more like giving the suggested device file name & the file operations for the vertical layer.

  • jaya

    i have successfully completed all process /dev/pen0 is also there in /dev … now what is next … what should i do next to see read and write working ….

    i tried this .. but error is coming ..

    root@pcch-ee206270:/home/pcch-ee206270/Desktop/bb/usb_read_write# echo “hi” > /dev/pen0
    bash: echo: write error: Invalid argument

    • http://twitter.com/anil_pugalia Anil Pugalia

      These various errors are expected and as mentioned in the last para of the article.

All published articles are released under Creative Commons Attribution-NonCommercial 3.0 Unported License, unless otherwise noted.
LINUX For You is powered by WordPress, which gladly sits on top of a CentOS-based LEMP stack.

Creative Commons License.