Hi guys, I'm currently making my first steps in USB driver development as I am developping a linux driver for an optical fiber to USB convertor. I managed to successfully write a driver for the 2.6.9 kernel, but when I upgraded to 2.6.10 or 2.6.11 things suddenly didn't work anymore. As I am not that quite proficient with USB driver development, I guess I must have been lucky things worked out in the first place.
The convertor uses bulk in/out endpoints for control commands and a isochronous in endpoint for streaming data. I want to buffer both the bulk in and iso in data into one buffer. The convertor can only operate on an USB2 interface, so I check for that in the probe function. The problem is that loading the driver into the kernel goes well. Sometimes a bulk transfer comes through. Iso transfers sometimes get through but always generate -71 (EPROTO) errors. According to Mr. Fliegl's programming guide it means bitstuff or unknown error: doesn't get me further. When I disable the iso URB submitting, the bulk works well for about 2 minutes. Unloading the driver results most of the time in kernel panics, but only when I actively used the driver in a open/close session. I took the usb-skeleton driver as a starting point and looked at different drivers in the media section on how to cope with iso transfers. In the open function bulk and iso receive URB's are created and submitted. In both callback functions I resubmit the URB's to create a constant "stream". I searched the mailinglists / Googled for similair problems, but couldn't find any. I hope you can help me out with this driver, or giving me hints / further reading or so as it is generating headaches for a couple of days now :S kind regards and thanks in advance, Paul Koster /* * fusbi.c: USB driver for the TMS International Fiber to USB convertor * used in the Refa8 series of products. * * Copyright (C) 2004-2005 Clinical Science Systems ([EMAIL PROTECTED]) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation, version 2. */ /* Kernel headers */ #include <linux/config.h> #include <linux/kernel.h> #include <linux/errno.h> #include <linux/init.h> #include <linux/slab.h> #include <linux/module.h> #include <linux/kref.h> #include <asm/uaccess.h> #include <linux/usb.h> /* Driver information */ #define DRIVER_VERSION "v1.0" #define DRIVER_AUTHOR "Paul Koster (Clinical Science Systems), [EMAIL PROTECTED]" #define DRIVER_DESC "TMS International USB <-> Fiber Interface Driver for Linux (c) 2004-2005" /* Define these values to match your devices */ #define USB_FUSBI_VENDOR_ID 0x0C7C #define USB_FUSBI_PRODUCT_ID 0x0004 /* IOCTL commands */ #define IOCTL_FUSBI_BUFFERSIZE 0x40044601 #define IOCTL_FUSBI_DATABUFFER 0x40044602 /* Buffer structure */ #define CONTROL_BUFFER_SIZE 131072 #define DATA_BUFFER_SIZE 131072 #define ISOC_RECV_URBS 3 /* Get a minor range for your devices from the usb maintainer */ #define USB_FUSBI_MINOR_BASE 192 /* Structure to hold all of our device specific stuff */ struct fusbi_control_buffer { spinlock_t lock; unsigned char* data; size_t read_ptr; size_t write_ptr; char overflow; }; struct fusbi_data_buffer { spinlock_t lock; int* data; unsigned long channels; size_t write_ptr; char overflow; }; struct fusbi_data { struct usb_device* udev; struct usb_interface* interface; struct kref kref; // Receive buffers unsigned char* bulk_recv_buffer; unsigned char** isoc_recv_buffer; // Receive URB's struct urb* bulk_recv_urb; struct urb** isoc_recv_urb; // Endpoints struct usb_endpoint_descriptor* bulk_recv_endpoint; struct usb_endpoint_descriptor* bulk_send_endpoint; struct usb_endpoint_descriptor* isoc_recv_endpoint; // Buffer pointers struct fusbi_control_buffer* control_buffer; struct fusbi_data_buffer* data_buffer; }; static struct usb_driver fusbi_driver; /******************************************************************* Function prototypes *******************************************************************/ // File operations static int fusbi_open(struct inode *inode, struct file *file); static int fusbi_release(struct inode *inode, struct file *file); static ssize_t fusbi_read(struct file *file, char *buffer, size_t count, loff_t *ppos); static ssize_t fusbi_write(struct file *file, const char *user_buffer, size_t count, loff_t *ppos); static int fusbi_ioctl(struct inode* inode, struct file* file, unsigned int command, unsigned long argument); // R/W callback functions static void fusbi_write_bulk_callback(struct urb *urb, struct pt_regs *regs); static void fusbi_read_bulk_callback(struct urb *urb, struct pt_regs *regs); static void fusbi_read_isoc_callback(struct urb *urb, struct pt_regs *regs); // User buffer functions int fusbi_enqueue_buffer(struct fusbi_data* dev, void* buffer, int count); int fusbi_dequeue_buffer(struct fusbi_data* dev, void* buffer, int count); size_t fusbi_buffer_size(struct fusbi_data* dev); // Deletion function static void fusbi_delete(struct kref *kref); /******************************************************************* File operations function implementations *******************************************************************/ static int fusbi_open(struct inode *inode, struct file *file) { struct fusbi_data* dev; struct usb_interface* interface; int i, subminor; int retval = 0; subminor = iminor(inode); interface = usb_find_interface(&fusbi_driver, subminor); if (!interface) { err ("%s - error, can't find device for minor %d", __FUNCTION__, subminor); retval = -ENODEV; goto exit; } dev = usb_get_intfdata(interface); if (!dev) { retval = -ENODEV; goto exit; } /* increment our usage count for the device */ kref_get(&dev->kref); /* save our object in the file's private structure */ file->private_data = dev; /* Setup initial bulk receive URB and submit */ usb_fill_bulk_urb(dev->bulk_recv_urb, dev->udev, usb_rcvbulkpipe(dev->udev, dev->bulk_recv_endpoint->bEndpointAddress), dev->bulk_recv_buffer, 3, fusbi_read_bulk_callback, dev); retval = usb_submit_urb(dev->bulk_recv_urb, GFP_KERNEL); if(retval) err("%s - failed submitting bulk read urb, error %d", __FUNCTION__, retval); // Setup initial isochronous receive URB's and submit for(i = 0; i < ISOC_RECV_URBS; ++i) { spin_lock_init(&dev->isoc_recv_urb[i]->lock); dev->isoc_recv_urb[i]->dev = dev->udev; dev->isoc_recv_urb[i]->pipe = usb_rcvisocpipe(dev->udev, dev->isoc_recv_endpoint->bEndpointAddress); dev->isoc_recv_urb[i]->transfer_buffer = dev->isoc_recv_buffer[i]; dev->isoc_recv_urb[i]->transfer_buffer_length = dev->isoc_recv_endpoint->wMaxPacketSize; dev->isoc_recv_urb[i]->complete = fusbi_read_isoc_callback; dev->isoc_recv_urb[i]->context = dev; dev->isoc_recv_urb[i]->interval = dev->isoc_recv_endpoint->bInterval; dev->isoc_recv_urb[i]->number_of_packets = 1; dev->isoc_recv_urb[i]->start_frame = -1; dev->isoc_recv_urb[i]->iso_frame_desc[0].offset = 0; dev->isoc_recv_urb[i]->iso_frame_desc[0].length = dev->isoc_recv_endpoint->wMaxPacketSize; dev->isoc_recv_urb[i]->transfer_flags = URB_ISO_ASAP; //retval = usb_submit_urb(dev->isoc_recv_urb[i], GFP_KERNEL); //if(retval) // err("%s - failed submitting isochronous read urb(%d), error %d", __FUNCTION__, i, retval); } exit: return retval; } static int fusbi_release(struct inode *inode, struct file *file) { struct fusbi_data* dev; int i; dev = (struct fusbi_data*)file->private_data; if (dev == NULL) return -ENODEV; // Unlink current receiving bulk URB usb_kill_urb(dev->bulk_recv_urb); // Unlink current receiving isochronous URB's //for(i = 0; i < ISOC_RECV_URBS; ++i) // usb_kill_urb(dev->isoc_recv_urb[i]); /* decrement the count on our device */ kref_put(&dev->kref, fusbi_delete); return 0; } static ssize_t fusbi_read(struct file *file, char *buffer, size_t count, loff_t *ppos) { struct fusbi_data* dev; char* temp_buffer = NULL; int retval = 0; size_t true_count; // Get device context from private_data file* member. dev = (struct fusbi_data*)file->private_data; // Allocate temporary buffer temp_buffer = kmalloc(count, GFP_KERNEL); // Read from user buffer true_count = fusbi_dequeue_buffer(dev, temp_buffer, count); /* if the read was successful, copy the data to userspace */ if(copy_to_user(buffer, temp_buffer, true_count)) retval = -EFAULT; else retval = true_count; kfree(temp_buffer); return retval; } static ssize_t fusbi_write(struct file *file, const char *user_buffer, size_t count, loff_t *ppos) { struct fusbi_data* dev; int retval = 0; struct urb *urb = NULL; char *buf = NULL; dev = (struct fusbi_data*)file->private_data; /* Verify that we actually have some data to write */ if (count == 0) goto exit; /* Create an URB */ urb = usb_alloc_urb(0, GFP_KERNEL); if (!urb) { retval = -ENOMEM; goto error; } /* Create a send buffer */ buf = kmalloc(count, GFP_KERNEL); if (!buf) { retval = -ENOMEM; goto error; } /* Copy the data to the urb */ if (copy_from_user(buf, user_buffer, count)) { retval = -EFAULT; goto error; } /* Initialize the urb properly */ usb_fill_bulk_urb(urb, dev->udev, usb_sndbulkpipe(dev->udev, dev->bulk_send_endpoint->bEndpointAddress), buf, count, fusbi_write_bulk_callback, dev); /* Send the data out the bulk port */ retval = usb_submit_urb(urb, GFP_KERNEL); if (retval) { err("%s - failed submitting write urb, error %d", __FUNCTION__, retval); goto error; } /* release our reference to this urb, the USB core will eventually free it entirely */ usb_free_urb(urb); exit: return count; error: kfree(buf); usb_free_urb(urb); return retval; } static int fusbi_ioctl(struct inode* inode, struct file* file, unsigned int command, unsigned long argument) { struct fusbi_data* dev; unsigned long* arg_address = (unsigned long*)argument; dev = (struct fusbi_data*)file->private_data; switch(command) { case IOCTL_FUSBI_BUFFERSIZE: { put_user(fusbi_buffer_size(dev), arg_address); return 0; break; } default: info("%s: IOCTL command 0x%X not implemented!", __FUNCTION__, command); break; } return -1; } /******************************************************************* R/W callback functions *******************************************************************/ static void fusbi_write_bulk_callback(struct urb *urb, struct pt_regs *regs) { struct fusbi_data* dev; dev = (struct fusbi_data*)urb->context; /* sync/async unlink faults aren't errors */ if (urb->status && !(urb->status == -ENOENT || urb->status == -ECONNRESET || urb->status == -ESHUTDOWN)) err("%s - nonzero write bulk status received: %d", __FUNCTION__, urb->status); /* free up our allocated buffer */ kfree(urb->transfer_buffer); } static void fusbi_read_bulk_callback(struct urb *urb, struct pt_regs *regs) { int retval = 0; int read_size = 0; struct fusbi_data* dev; if(!(urb->status == -ENOENT || urb->status == -ECONNRESET || urb->status == -ESHUTDOWN)) { // Retrieve private data from URB's context dev = (struct fusbi_data*)urb->context; switch(urb->status) { case 0: { // Packet received is OK and cleared the buffer completely // Enqueue packet in user buffer fusbi_enqueue_buffer(dev, dev->bulk_recv_buffer, urb->actual_length); read_size = 3; break; } case -EOVERFLOW: { // This error occurs when a read request reads less bytes than the buffer contains. // We use this option to determine the protocol packetlength (byte 3) and read the whole packet. if(dev->bulk_recv_buffer[0] == 0xAA && dev->bulk_recv_buffer[1] == 0xAA) read_size = dev->bulk_recv_buffer[2] * 2 + 6; else read_size = 3; break; } case -EPIPE: { // This error occurs when an endpoint has stalled. info("%s: The bulk endpoint has stalled. (size = %d)", __FUNCTION__, urb->actual_length); read_size = 3; break; } default: { // Unknown error. Log to syslog info("%s: Unknown bulk error occurred (status %d)", __FUNCTION__, urb->status); read_size = 3; break; } } if(read_size > 0) { urb->dev = dev->udev; urb->status = 0; urb->transfer_buffer_length = read_size; // Submit the URB retval = usb_submit_urb(urb, GFP_ATOMIC); if (retval) err("%s - failed submitting bulk_recv_urb, error %d", __FUNCTION__, retval); } } } static void fusbi_read_isoc_callback(struct urb *urb, struct pt_regs *regs) { int retval = 0; struct fusbi_data* dev; if(!(urb->status == -ENOENT || urb->status == -ECONNRESET || urb->status == -ESHUTDOWN)) { // Retrieve private data from URB's context dev = (struct fusbi_data*)urb->context; switch(urb->iso_frame_desc[0].status) { case 0: { // Packet received is OK and cleared the buffer completely // Enqueue packet in user buffer fusbi_enqueue_buffer(dev, urb->transfer_buffer, urb->iso_frame_desc[0].actual_length); break; } default: { // Unknown error. Log to syslog info("%s: Unknown USB error occurred (isostatus %d)", __FUNCTION__, urb->iso_frame_desc[0].status); info("%s: Unknown USB error occurred (status %d)", __FUNCTION__, urb->status); break; } } urb->dev = dev->udev; urb->status = 0; urb->iso_frame_desc[0].status = 0; urb->iso_frame_desc[0].actual_length = 0; // Submit the URB retval = usb_submit_urb(urb, GFP_ATOMIC); if (retval) err("%s - failed submitting isoc_recv_urb, error %d", __FUNCTION__, retval); } } /******************************************************************* User buffer functions *******************************************************************/ int fusbi_enqueue_buffer(struct fusbi_data* dev, void* buffer, int count) { int i; unsigned char* real_buffer = buffer; for(i = 0; i < count && !dev->control_buffer->overflow; ++i) { // Calculate new offset dev->control_buffer->write_ptr = (dev->control_buffer->write_ptr + 1) % CONTROL_BUFFER_SIZE; // Check for overflow dev->control_buffer->overflow = dev->control_buffer->write_ptr == dev->control_buffer->read_ptr; // Fill databuffer if(!dev->control_buffer->overflow) dev->control_buffer->data[dev->control_buffer->write_ptr] = real_buffer[i]; else info("%s: Cannot enqueue any packets: buffer full", __FUNCTION__); } return i; } int fusbi_dequeue_buffer(struct fusbi_data* dev, void* buffer, int count) { int i; unsigned char* real_buffer = buffer; for(i = 0; i < count && fusbi_buffer_size(dev); ++i) { // Calculate new offset dev->control_buffer->read_ptr = (dev->control_buffer->read_ptr + 1) % CONTROL_BUFFER_SIZE; // Read databuffer real_buffer[i] = dev->control_buffer->data[dev->control_buffer->read_ptr]; } // Clear overflow dev->control_buffer->overflow = dev->control_buffer->overflow && (count == 0); return i; } size_t fusbi_buffer_size(struct fusbi_data* dev) { return dev->control_buffer->overflow ? CONTROL_BUFFER_SIZE : (dev->control_buffer->write_ptr < dev->control_buffer->read_ptr ? dev->control_buffer->write_ptr + CONTROL_BUFFER_SIZE - dev->control_buffer->read_ptr : dev->control_buffer->write_ptr - dev->control_buffer->read_ptr); } /******************************************************************* Delete function for freeing our dev structure *******************************************************************/ static void fusbi_delete(struct kref *kref) { struct fusbi_data* dev = container_of(kref, struct fusbi_data, kref); int i; usb_put_dev(dev->udev); // Free device instance kfree(dev->control_buffer->data); kfree(dev->control_buffer); for(i = 0; i < ISOC_RECV_URBS; ++i) kfree(dev->isoc_recv_buffer[i]); kfree(dev->isoc_recv_buffer); kfree(dev->isoc_recv_urb); kfree(dev->data_buffer->data); kfree(dev->data_buffer); kfree(dev); } /******************************************************************* file_operations struct *******************************************************************/ static struct file_operations fusbi_fops = { .owner = THIS_MODULE, .open = fusbi_open, .release = fusbi_release, .read = fusbi_read, .write = fusbi_write, .ioctl = fusbi_ioctl, }; /******************************************************************* USB class driver info in order to get a minor number from the usb core and to have the device registered with the driver core *******************************************************************/ static struct usb_class_driver fusbi_class = { .name = "usb/fusbi%d", .fops = &fusbi_fops, .mode = S_IFCHR | S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH, .minor_base = USB_FUSBI_MINOR_BASE, }; static int fusbi_probe(struct usb_interface *interface, const struct usb_device_id *id) { struct fusbi_data* dev = NULL; struct usb_host_interface* iface_desc; struct usb_endpoint_descriptor* endpoint; int i; int retval = -ENOMEM; // Allocate memory for our device state and initialize it dev = kmalloc(sizeof(*dev), GFP_KERNEL); if (dev == NULL) { err("Out of memory"); goto error; } memset(dev, 0x00, sizeof(*dev)); dev->isoc_recv_buffer = kmalloc(sizeof(unsigned char*), GFP_KERNEL); dev->isoc_recv_urb = kmalloc(sizeof(struct urb*), GFP_KERNEL); // Initialize buffer structure. dev->control_buffer = kmalloc(sizeof(struct fusbi_control_buffer), GFP_KERNEL); memset(dev->control_buffer, 0x00, sizeof(struct fusbi_control_buffer)); dev->data_buffer = kmalloc(sizeof(struct fusbi_data_buffer), GFP_KERNEL); memset(dev->data_buffer, 0x00, sizeof(struct fusbi_data_buffer)); dev->control_buffer->lock = SPIN_LOCK_UNLOCKED; dev->data_buffer->lock = SPIN_LOCK_UNLOCKED; // Initialize buffer data pointers dev->control_buffer->data = kmalloc(CONTROL_BUFFER_SIZE, GFP_KERNEL); memset(dev->control_buffer->data, 0x00, CONTROL_BUFFER_SIZE); dev->data_buffer->data = kmalloc(DATA_BUFFER_SIZE, GFP_KERNEL); memset(dev->data_buffer->data, 0x00, DATA_BUFFER_SIZE); // Initialize reference counter kref_init(&dev->kref); dev->udev = usb_get_dev(interface_to_usbdev(interface)); dev->interface = interface; /* Use the alternate interface specified in the fusbi device */ usb_set_interface(dev->udev, 0, 1); /* 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; /* we found a Bulk IN endpoint */ if (!dev->bulk_recv_endpoint && (endpoint->bEndpointAddress & USB_DIR_IN) && ((endpoint->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) == USB_ENDPOINT_XFER_BULK)) { dev->bulk_recv_endpoint = endpoint; dev->bulk_recv_urb = usb_alloc_urb(0, GFP_KERNEL); dev->bulk_recv_buffer = kmalloc(endpoint->wMaxPacketSize, GFP_KERNEL); } /* we found a Bulk OUT endpoint */ if (!dev->bulk_send_endpoint && !(endpoint->bEndpointAddress & USB_DIR_IN) && ((endpoint->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) == USB_ENDPOINT_XFER_BULK)) { dev->bulk_send_endpoint = endpoint; } /* We found an Isochronous IN endpoint */ if (!dev->isoc_recv_endpoint && (endpoint->bEndpointAddress & USB_DIR_IN) && ((endpoint->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) == USB_ENDPOINT_XFER_ISOC)) { dev->isoc_recv_endpoint = endpoint; for(i = 0; i < ISOC_RECV_URBS; ++i) { dev->isoc_recv_urb[i] = usb_alloc_urb(1, GFP_KERNEL); dev->isoc_recv_buffer[i] = kmalloc(endpoint->wMaxPacketSize, GFP_KERNEL); } } } /* Check if all required endpoints are present */ if (!(dev->bulk_recv_endpoint->bEndpointAddress && dev->bulk_send_endpoint->bEndpointAddress && dev->isoc_recv_endpoint->bEndpointAddress)) { err("Could not find the required USB endpoints (bulk in/out, isochronous in)"); goto error; } /* Check if device is attached to an USB2 interface */ if(dev->udev->speed != USB_SPEED_HIGH) { err("Device is not attached to an USB2 bus"); goto error; } /* save our data pointer in this interface device */ usb_set_intfdata(interface, dev); /* we can register the device now, as it is ready */ retval = usb_register_dev(interface, &fusbi_class); if (retval) { /* something prevented us from registering this driver */ err("Not able to get a minor for this device."); usb_set_intfdata(interface, NULL); goto error; } /* let the user know what node this device is now attached to */ info("USB fusbi control device now attached (minor %d)", interface->minor); return 0; error: if (dev) kref_put(&dev->kref, fusbi_delete); return retval; } static void fusbi_disconnect(struct usb_interface *interface) { struct fusbi_data *dev; int i, minor = interface->minor; /* prevent fusbi_open() from racing fusbi_disconnect() */ lock_kernel(); // Deallocate URB's dev = usb_get_intfdata(interface); kfree(dev->bulk_recv_urb->transfer_buffer); for(i = 0; i < ISOC_RECV_URBS; ++i) kfree(dev->isoc_recv_buffer[i]); usb_set_intfdata(interface, NULL); usb_deregister_dev(interface, &fusbi_class); unlock_kernel(); /* decrement our usage count */ kref_put(&dev->kref, fusbi_delete); info("USB fusbi device now disconnected (minor %d)", minor); } static struct usb_device_id fusbi_idtable [] = { { USB_DEVICE(USB_FUSBI_VENDOR_ID, USB_FUSBI_PRODUCT_ID) }, { } /* Terminating entry */ }; MODULE_DEVICE_TABLE (usb, fusbi_idtable); static struct usb_driver fusbi_driver = { .owner = THIS_MODULE, .name = "fusbi driver", .probe = fusbi_probe, .disconnect = fusbi_disconnect, .id_table = fusbi_idtable, }; /******************************************************************* Module entry points *******************************************************************/ static int __init usb_fusbi_init(void) { int result; /* register this driver with the USB subsystem */ result = usb_register(&fusbi_driver); if (result) err("%s: Registration of fusbi driver failed. (error %d)", __FUNCTION__, result); return result; } static void __exit usb_fusbi_exit(void) { /* deregister this driver with the USB subsystem */ usb_deregister(&fusbi_driver); } module_init (usb_fusbi_init); module_exit (usb_fusbi_exit); MODULE_LICENSE("GPL"); ------------------------------------------------------- This SF.net email is sponsored by Microsoft Mobile & Embedded DevCon 2005 Attend MEDC 2005 May 9-12 in Vegas. Learn more about the latest Windows Embedded(r) & Windows Mobile(tm) platforms, applications & content. Register by 3/29 & save $300 http://ads.osdn.com/?ad_id=6883&alloc_id=15149&op=click _______________________________________________ linux-usb-devel@lists.sourceforge.net To unsubscribe, use the last form field at: https://lists.sourceforge.net/lists/listinfo/linux-usb-devel