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:
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;
}
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With