Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to read/write to an USB storage device with a linux driver?

During the attempt to write my own simple usb driver for an usb-flash-drive, I got stuck reading the data that I wrote to the device. So, my first question is:

How is the transfer and the storage on a device going on? (in detail)

I know I have to perform the following steps:

  • Create an urb (USB request block)
  • Allocate a DMA buffer
  • Transfer the data from the user-space into the DMA buffer
  • Send the data through a pipe to the device

I couldn't find any documentation on how a device handles this data.

Is this even possible to write such a driver, or would it be necessary to disassemble the usb device, to send special commands?

The code I have written looks something like the following and is from the ldd3 and "http://lxr.free-electrons.com/source/drivers/usb/usb-skeleton.c". It only shows a shortened version of the important functions.

After loading the driver into the kernel, I can write to the device without any error, but if I read, an EPIPE error occurs. Ldd3 mentions that the usb_clear_halt() could solve this problem, but it doesn't.

// This function is called when the device is plugged in
static int my_driver_probe(struct usb_interface* interface, const struct usb_device_id* id)
{
    struct usb_skel* dev = NULL;
    struct usb_device* udev = interface_to_usbdev(interface);
    struct usb_host_interface* iface_desc;
    struct usb_endpoint_descriptor* endpoint;
    int retval = -ENODEV;
    int i = 0;
    size_t buffer_size;

    dev = kzalloc(sizeof(struct usb_skel), GFP_KERNEL);

    // Check vendor and product id
    // … 

    dev->udev = udev;
    dev->interface = interface;

    // Set up the endpoint information
    iface_desc = interface->cur_altsetting;
    for(i=0; i < iface_desc->desc.bNumEndpoints; ++i) {
        endpoint = &iface_desc->endpoint[i].desc;

        if(!dev->bulk_in_endpointAddr && usb_endpoint_is_bulk_in(endpoint)) {
            buffer_size = endpoint->wMaxPacketSize;
            dev->bulk_in_size = buffer_size;
            dev->bulk_in_endpointAddr = endpoint->bEndpointAddress;
            dev->bulk_in_buffer = kmalloc(buffer_size, GFP_KERNEL);
            if(!dev->bulk_in_buffer) {
                printk("Could not allocate bulk_in_buffer\n");
                goto error;
            }
            dev->bulk_in_urb = usb_alloc_urb(0, GFP_KERNEL);
        }
        if(!dev->bulk_out_endpointAddr && usb_endpoint_is_bulk_out(endpoint))
            dev->bulk_out_endpointAddr = endpoint->bEndpointAddress;
    }
    // Check that the endpoints are set
    // … 

    // Save our data pointer in this interface device
    usb_set_intfdata(interface, dev);

    // Register the device
    retval = usb_register_dev(interface, &class_descr);
    return retval;
}

// Is called when another program writes into /dev/my_usb_driver
static ssize_t my_driver_write( struct file* file, const char __user* user_buffer, size_t count, loff_t* offs)
{
    struct usb_skel* dev = file->private_data;
    struct urb* urb = NULL;
    char* buf = NULL;
    int retval = 0;
    size_t writesize = min(count, (size_t)MAX_TRANSFER);

    // Create a urb, and a buffer for it, and copy the data to the urb
    urb = usb_alloc_urb(0, GFP_KERNEL);

    // Creates a DMA buffer
    buf = usb_alloc_coherent(dev->udev, writesize, GFP_KERNEL, &urb->transfer_dma);

    // The data that is passed to the driver should be copied into the DMA buffer
    copy_from_user(buf, user_buffer, writesize;

    // Initialize the urb proberly
    usb_fill_bulk_urb(urb, dev->udev, 
                  usb_sndbulkpipe(dev->udev, dev->bulk_out_endpointAddr),
                  buf, writesize, (void*)my_write_bulk_callback, dev);

    // Send the data out the bulk port
    urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;

    usb_submit_urb(urb, GFP_KERNEL);

    return writesize; 
}

// Is called when another program reads from /dev/my_usb_driver
static ssize_t my_driver_read( struct file *file, char* buffer, size_t count, loff_t* offs) 
{
    struct usb_skel* dev = file->private_data;
    int retval = 0;

    // Check that we have data to read
    // … 

    usb_fill_bulk_urb(dev->bulk_in_urb, dev->udev, 
                  usb_rcvbulkpipe(dev->udev, dev->bulk_in_endpointAddr),
                  dev->bulk_in_buffer, 
                  min(dev->bulk_in_size, count), read_bulk_callback, dev);  

    retval = usb_submit_urb(dev->bulk_in_urb, GFP_KERNEL);

    // If the read was succesful, copy the data to user space
    copy_to_user(buffer, dev->bulk_in_buffer, count);

    return retval;
}
like image 590
S1J0 Avatar asked Sep 03 '25 02:09

S1J0


1 Answers

USB is just a transport layer. Storage devices generally implement SCSI protocol. Create a SCSI command for reading or writing from the data that user space has sent. Then create URB for the SCSI command and send it to the USB device.

SCSI is a huge protocol, for learning USB device driver development it is better to start with simple devices like USB to serial devices.

like image 148
prasannatsm Avatar answered Sep 04 '25 18:09

prasannatsm