3
votes

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;
}
1

1 Answers

1
votes

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.