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

124
33775
USB device drivers

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 0x01 and 0x82, 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.

124 COMMENTS

      • There is an error in source! You should not allocate buffer globally. Microchip FSUSB demo board did not work by me. I allocated buffer in write_msg function. Then, it worked.

        • Can you explain as what do you mean by “not working” with global buffer? I assume that when you allocated the buffer in write_msg, you freed it also, there itself.

          • One more thing: this was kernel 3.5.0.

            What happened was this: static buffer contained correct data from userspace. Device however received corrupted data. When I moved this buffer variable into red/write rutines, it fixed the problem.

            It was just a char array.

            One more question: how does kernel distinguish between identical devices (literally cloned; same deviceID, same vendor ID, same serial, BCD code, etc.)? Kernel assigns different minor numbers. But I have a problem “how to send data to correct device (or read from it)” when device file read/write in userspace happens.

          • Possibly a race condition of simultaneous access of the buffer.

            Your probe will get called multiple times for each interface of the multiple devices, and accordingly distinct minors should be allotted to each. Also, in each probe the corresponding device pointer should be preserved into the private data of the corresponding interface, and then possibly the struct file, in case of system calls. With that the system calls would access the correct device, from their struct file’s private data.

          • There was one single access to driver at the time (as far as I am aware of), and this was my test program.

            So I guess I’ll have to implement this multiple devices handling mechanysm inside of the driver by myself.

          • Agreed that you had only single access from the user space – but there could be other accesses from within the kernel, itself.

            And yes, support for handling multiple devices has to be provided from within the driver only. Once the support is there, then it is left to the applications, what do they do with that.

          • Hi sir, I tried with program i was sucessful with but i faced problems implementing use program.(using read and right calls). with this can i transfer a entire file and read file
            how is the criteria for efficient read and write to drive harry_mysooru@yahoo.com

          • This doesn’t implement the complete usb storage protocol – so you may not be able to do an actual read/write.

          • Thanks for u reply Sir i want about high mem,and low mem.
            kmalloc allocates memory in low mem i.e after linux image sits?
            what is high mem, entire 896> to 4GB is high mem

            how different is this memory from 4 GB process address space

            How exactly CFS works

          • high mem – 896> to 4GB is DOS days concept. The high mem in Linux kernel context is a very different thing. In kernel, low mem & high mem is just the differentiation whether the memory is directly mapped & accessed or indirectly mapped through a window. 4GB process address space could map to any one or both of them, but typically done with low mem.

            For working of CFS, check out the CFS code.

          • What is difference between fast irq and irq.
            With respect to x86 there is INTR line which interfaced to 8059 PIC based on flag given in handler what actions are take with respect to INTR and 8059 controller

            As an interrupt occurs, handler is called and all interrupts are disabled then on exit of handler interrupts are enabled
            then what are nested interrupts are they software interrupts occuring inside handler as interrupts are disabled in the handler

          • Thank you soo much for valuable time and sharing such good links Hope you do not mind if i come up with more questions in near future

          • Hi Sir,

            when exactly to go for semaphore and when for mutex.
            binary sema is itself mutex??
            when we use counting?
            semaphore any process can release it .mutex parent itself as to relase it

          • It seems to be typical but they are not clear can you please explain me what exactly you have understood from it.
            It is clear with mutext,But semaphore not getting properly.

          • In Linux kernel, we only need mutex, for synchronization i.e. concurrent access protection – typically no use case for semaphore.

          • Hello sir, How can we create file inside a device file .
            I mean when i open device file inside pen drive if i am doing copy of a file the file as to be created how it is done.
            What is driver level create call??

          • Device files are just place holders – they are not devices and hence we do not create file inside a device file but onto the device only – device file just represents it, to connect/mount the device to a particular folder under /

          • i want to implement copy into my pendrive meaning i have file1 which has to be copied to pendrive what exactly will happen with driver level calls .
            How exactly file1 on disk map to pendrive location.
            i am clear about read and write

          • This is done by the file system driver and system calls provided by it – the driver for the file system which is created/formatted on the pen drive.

          • Sir please clearly explain me when binary semaphore is used and when counting semaphore is used with respect to OS

          • Please explain more clearly on this line

            4GB process address space could map to any one or both of them, but typically done with low mem.

  1. 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.

  2. 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 ?

      • 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.

  3. 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 ?

    • 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.

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

  5. 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)

  6. 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 !!!

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

  8. 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);

  9. 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

  10. 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#

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

  11. 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)

  12. 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

  13. Hi Anil,thanks for this session of knowledge transfer.
    I have one query about how the file system inside usb is mounting to linux existing filesystem.Could u help us with that also?

    • For that you may have to read my article #18 to #24 on file system modules. As of this comment, most of these 7 articles are available only in LFY hard copy.

  14. Sir as i’v seen in my usb.c(old- usb-storage.c) usb is registered to VFS by block driver api’s , so can you tell me in which USB devices char API’s are used as in our article code

  15. Sir after inserting the module i am not able to see any file in /dev/ i’v unloaded
    the usb-storage.ko as well , pls suggest something

  16. i am writing a device driver for CDC-EEM class device. it has two interfaces one for control and other for bulk transfers. the device probe is called for two interfaces. how to deal with two probes. if i register only one interface , is there any way that i can use other interface in read/write functions of registered interface??

  17. after inserting the module (after unloading the usb-storage.ko) from the kernel another driver (pen_driver ) picks up the pen drive instead of my driver , suggest something … pls

      • Sir, how can we implement the basic steps of reading and writing of data or any string with the help of our code. Please do let me know.

        • As far as I understand, the implementation is shown in the above article. Or, by reading / writing do you mean something like what a user application does using scanf / printf. If yes, then drivers are not meant for that.

  18. Hi Anil,
    Thank you for the this great work, really good articles to understand the basics of Linux Device drivers. Can you please give us readers an idea on media drivers like JPEG.

  19. I have followed all the steps, the device is getting registered as you can see in the attached image but i am unable to read or write data from/to usb because the device is not mounting. Plz help…

    • The above driver is not supposed to expose the device as a block device, and hence not expected to support mounting. It is just to demonstrate the basic workings of the USB protocol, not even data read/write as per the usb storage protocol.

    • You may simply use cat and echo with /dev/pen0 to respectively call the read and write methods. Or, if you want, you may write a simple C program (app) to do the same.

  20. Hello Anil, nice article. . .
    I have tried this and i am able to see /dev/pen0. Now If i try to read then it gives connection time out error, as you have mentioned.
    I want to read first 512 bytes of raw data of storage device. Is it possible?

    • Thanks for your appreciation. For data transaction for the storage device, you may have read up the usb storage protocol and write commands as per that into the corresponding out endpoint.

  21. I wrote this code for an unknown device and I have beagle usb analyzer tool. Now with this code I can see data from out endpoint. Is it normal ? I don’t know that I must write driver for this device. Can I see in data with application ?

    • Yes, it is normal to see data from the out endpoint. And you would also be
      able to get data in from the in endpoint, if the device sends –
      otherwise you may not be able to see, neither in the analyzer, nor with
      the application.

  22. Yes, it is normal to see data from out endpoint. And you would also be able to get data in from the in endpoint, if the device sends – otherwise you may not be able to see, neither in the analyzer, nor with the application.

  23. Hey sorry, I said that I see data from out endpoint with usb analyzer tool. But there is a big mistake. With this module we cannot see datas . I saw datas because of using linux in virtual machine. And virtual machine get this datas through driver that is available in windows. If you look at removable devices option in virtual machine you can see that this device is connectable from windows and if you choose disconnect option in there, you cannot see data though you build this module and see that this devices file in /dev folder.

    So sorry about my big mistake. Thanks

  24. Hello anil_pugalia

    Can I send data to unknown device that hasn’t got driver through default endpoint with java or c++ application ?

  25. Hi,
    Thank you for this tutorial. It helped me a lot.

    My next goal is to send SCSI commands to my pendrive. I thought I would be nice to encapsulate a CDB (Command Descriptor Block) (with a SCSI command in it) in the buffer.

    Will it work to send a CDB (with SCSI command) as buffer to the Input-Endpoint via usb_bulk_mg() or do i have to create a URB and use usb_submit_urb() ?

    I really don’t know where i have to put my scsi command and/or how to fill the buffer with a scsi command.

    Can you help me?

    • First, you have to send it to Out Endpoint, not In Endpoint. And, you should be able to use usb_bulk_msg(), if doing from a system call. Regarding, how to put the SCSI command &/or fill the buffer, you may have to refer the usb-storage driver under drivers/usb in the kernel source.

  26. Hello Anil Sir,
    Could you please explain usb/pen%d part in the code.
    My probe function is not getting called. I read that, for the probe to get called automatically, device name should match with device driver name. If “pen_driver” is the device driver name, then where is the device name set in this code?

    • Where did you read that device name should match the device driver name? Instead make the vendor id, product id in the pen_table above, same as that of your pen drive, and make sure to remove the usb-storage driver – and the probe should get called

  27. Hello Sir,
    Could you please tell me what is the need of running this module on custom USB Device, would it not work on normal pen drive?

    • Notice that for the normal pen drive, you’d have to code the logic of usb storage protocol in the read & write calls, which is non-trivial. However, if you have a custom device like the LDDK, the vendor-specific protocol followed there is simple read & write into interrupt endpoints, along with some basic settings into the control endpoint 0.

      • Thank you sir for your reply.
        Sir , would this code work fine on LDDK, showing real USB Data Transfer or it needs some changes as you mention in the above comment and what about connection Timeout Error and non-confirming SCSI Commands?

          • Sir, it shows connection timeout error when i try to read data from USB Device i.e pen drive, i want to know would i get the same type of error when i run this on LDDK? i want to confirm this before ordering this LDDK, actually this is my sem project and i want to show smooth read and write of data would it be possible with LDDK??

          • Yes. It would work perfectly fine, but not with the article code – instead with code available at the link I mentioned above.

  28. Hello sir,
    I wrote my code with URB (usb request block). i just want to know whether is it important to detect pen drive in disk or mount when i insert pen drive?
    my probe function is working but pen drive is not detecting.
    Need help sir.

  29. Hi Sir,
    Please find the bellow steps as per requirement in my application code.
    1.My application (running on an embedded ARM cortex based platform, running linux 3.14 and has 2 USB ports) needs to detect USB devices such as Mass Storage devices that has been inserted/removed.
    2.The application need to know that a usb device has been inserted/removed along with the device node (like /dev/sda1, /dev/sdb1 etc). Then I need to mount this device node to a known location in the application.
    3.Application want know the usb status(if its idle or busy with transferring files) while transferring some files from pen drive to EMMC memory.

    Is there a way to get these 3 things, from my application, which is in C language.

    1) usb device inserted/removed (Is there a way like an interrupt or polling).
    2) device node
    3) copying files
    Could you please help on this
    Thanks in advance
    N Shankar

  30. Hi Sir,

    I was not able to understand what exactly a USB device driver has to convert from struct usb_interface into struct usb_device using “interface_to_usbdev”.Can you please give me an example of one or two parameters which this function will convert?

  31. Hello Sir,

    while operating the custom USB device (one avalaible at eSrijan) i am getting an issue.
    I downloaded your Workshop templates from your sysplay website after that while loading the ddk_io.ko or ddk_led.ko any driver using “sudo insmod ddk_io.ko” it shows
    “DDK USB i/f 0 now probed: (16C0:05DC)
    [ 2390.371379] ddk_io: probe of 1-2:1.0 failed with error -22
    [ 2390.371383] DDK USB i/f 1 now probed: (16C0:05DC)
    [ 2390.371385] ddk_io: probe of 1-2:1.1 failed with error -22
    [ 2390.371388] DDK USB i/f 2 now probed: (16C0:05DC)
    [ 2390.373799] Minor obtained: 0
    [ 2390.374258] usbcore: registered new interface driver ddk_io
    [ 2390.374260] DDK usb_registered” in “dmesg”
    and after that while executing any of the app line usb_ops
    it shows “usb_ops ioctl: Inappropriate ioctl for device”

    HELP ME !!!

LEAVE A REPLY

Please enter your comment!
Please enter your name here