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

Reply via email to