// // USB Voip-Blaster driver - v0.00 // // Written by David Stanhope // TODO: // Add ioctl to read usb descriptors // // Based on: USB Skeleton driver - 0.6 // // Copyright (c) 2001 Greg Kroah-Hartman (greg@kroah.com) // // 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; either version 2 of // the License, or (at your option) any later version. // // This driver is to be used as a skeleton driver to be able to create a // USB driver quickly. The design of it is based on the usb-serial and // dc2xx drivers. // // Thanks to Oliver Neukum and David Brownell for their help in debugging // this driver. // // TODO: // - fix urb->status race condition in write sequence // - move minor_table to a dynamic list. // // History: // // 2001_11_05 - 0.6 - fix minor locking problem in skel_disconnect. // Thanks to Pete Zaitcev for the fix. // 2001_09_04 - 0.5 - fix devfs bug in skel_disconnect. Thanks to wim delvaux // 2001_08_21 - 0.4 - more small bug fixes. // 2001_05_29 - 0.3 - more bug fixes based on review from linux-usb-devel // 2001_05_24 - 0.2 - bug fixes based on review from linux-usb-devel people // 2001_05_01 - 0.1 - first version // // (Written with Tabs=4) // #define BG_READ 1 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef CONFIG_USB_DEBUG static int debug = 1; #else static int debug; #endif // Use our own dbg() macro #undef dbg #define dbg(format, arg...) if (debug) \ printk(KERN_DEBUG __FILE__ "," __FUNCTION__ ": " format "\n" , ## arg); \ // Use our own err() macro #undef err #define err(format, arg...) \ printk(KERN_ERR __FILE__ "," __FUNCTION__ ": " format "\n" , ## arg); \ // Version Information #define DRIVER_VERSION "v0.20" #define DRIVER_AUTHOR "David Stanhope (voip@fobbit.com)" #define DRIVER_DESC "USB Voip-Blaster Device Driver" // Module paramaters MODULE_PARM(debug, "i"); MODULE_PARM_DESC(debug, "Debug enabled or not"); // Define these values to match your device #define USB_VB_VENDOR_ID 0x1292 #define USB_VB_PRODUCT_ID 0x0258 #ifdef MODULE_DEVICE_TABLE // table of devices that work with this driver static struct usb_device_id vb_table [] = { { USB_DEVICE(USB_VB_VENDOR_ID, USB_VB_PRODUCT_ID) }, { } // Terminating entry }; MODULE_DEVICE_TABLE (usb, vb_table); #endif // Get a minor range for your devices from the usb maintainer #define USB_VB_MINOR_BASE 200 // we can have up to this number of device plugged in at once // (really only half since each device takes two device nodes #define MAX_DEVICES 8 // various pipe sizes #define VOUT_SIZE 64 #define VINP_SIZE 20 #define CMD_SIZE 1 #define STAT_SIZE 1 #define RDR_TIMEOUT (10*HZ) #define RDR_BUF_CNT 64 // each buffer: 1st byte is bytes-in-buffer, then actual buffer // Structure to hold all of our device specific stuff struct usb_vb { struct usb_device *udev; // save off the usb device pointer int minor; // starting minor number for this device u_char out_buffer[VOUT_SIZE]; // voice/command output buffer for urb u_char inp_buffer[VINP_SIZE]; // voice/status input buffer for urb #ifdef BG_READ u_char *rdr_buffer; // read system call buffer int rdr_head ; // interface to read system call int rdr_tail ; // interface to read system call int rdr_status; // interface to read system call wait_queue_head_t rdr_wqh ; // Processes waiting wait_queue_head_t rdr_pwait ; // Processes waiting urb_t *have_urb ; #endif int out_size; // max size for output pipe int inp_size; // max size for input pipe int out_endpoint; // output endpoint address int inp_endpoint; // input endpoint address int open_count; // no. of times this port has been opened struct semaphore out_sem; // locks this structure struct semaphore inp_sem; // locks this structure }; // array of pointers to our devices that are currently connected static struct usb_vb *minor_table[MAX_DEVICES]; // lock to protect the minor_table structure static DECLARE_MUTEX (minor_table_mutex); // ------------------------------------------------------------------------ // START code largely ripped from 'drivers/usb/usb.c' // // PROBLEM HERE, THE LINUX_VERSION_CODE BIT HERE DOESN'T SEEM TO TO ALWAYS // WORK, FOR ONE PERSON NEEDS TO BE KERNEL_VERSION(2, 4, 9) AND OTHER // NEEDS KERNEL_VERSION(2, 4, 11)????????? // // ------------------------------------------------------------------------ #if LINUX_VERSION_CODE < KERNEL_VERSION(2, 4, 9) #define VB_WRITE_TIMEOUT (0) static void vb_blocking_completion(urb_t *urb) { api_wrapper_data *awd = (api_wrapper_data *)urb->context; if (waitqueue_active(awd->wakeup)) { wake_up(awd->wakeup); } #if 0 else { dbg("(blocking_completion): waitqueue empty!"); // even occurs if urb was unlinked by timeout... } #endif } static int vb_bulk_msg(struct usb_device *usb_dev, unsigned int pipe, void *data, int len, int *actual_length, int timeout) { DECLARE_WAITQUEUE(wait, current); DECLARE_WAIT_QUEUE_HEAD(wqh); api_wrapper_data awd; int status, was_sig; urb_t *urb; if (len < 0) { return -EINVAL; } urb = usb_alloc_urb(0); if (!urb) { return -ENOMEM; } // build urb FILL_BULK_URB(urb, usb_dev, pipe, (unsigned char*) data, len, (usb_complete_t) vb_blocking_completion, 0); // start urb and wait for completion or timeout awd.wakeup = &wqh; init_waitqueue_head(&wqh); current->state = TASK_INTERRUPTIBLE; add_wait_queue(&wqh, &wait); urb->context = &awd; status = usb_submit_urb(urb); if (status) { // something went wrong usb_free_urb(urb); current->state = TASK_RUNNING; remove_wait_queue(&wqh, &wait); return status; } was_sig = 0; // assume not a signal status = 1; // assume will complete ok while (urb->status == -EINPROGRESS) { schedule_timeout(1); if(signal_pending(current)) { was_sig = 1; status = 0; // say failed break ; } if ((timeout) && (--timeout == 0)) { status = 0; // say failed break ; // timeout occured } } current->state = TASK_RUNNING; remove_wait_queue(&wqh, &wait); if (!status) // timeout { usb_unlink_urb(urb); // remove urb safely if (was_sig) { status = -EINTR; } else { err("usb_control/bulk_msg timeout"); status = -ETIMEDOUT; } } else { status = urb->status; } if (actual_length) { *actual_length = urb->actual_length; } usb_free_urb(urb); return status; } // ------------------------------------------------------------------------ #else // LINUX_VERSION_CODE >= KERNEL_VERSION(2, 4, 9) // ------------------------------------------------------------------------ #define VB_WRITE_TIMEOUT (HZ*10) static void vb_blocking_completion(urb_t *urb) { struct usb_api_data *awd = (struct usb_api_data *)urb->context; awd->done = 1; wmb(); wake_up(&awd->wqh); } static int vb_bulk_msg(struct usb_device *usb_dev, unsigned int pipe, void *data, int len, int *actual_length, int timeout) { DECLARE_WAITQUEUE(wait, current); struct usb_api_data awd; int status, was_sig; urb_t *urb; if (len < 0) { return -EINVAL; } urb=usb_alloc_urb(0); if (!urb) { return -ENOMEM; } FILL_BULK_URB(urb, usb_dev, pipe, data, len, vb_blocking_completion, 0); // start urb and wait for completion or timeout init_waitqueue_head(&awd.wqh); awd.done = 0; set_current_state(TASK_UNINTERRUPTIBLE); add_wait_queue(&awd.wqh, &wait); urb->context = &awd; status = usb_submit_urb(urb); if (status) { // something went wrong usb_free_urb(urb); set_current_state(TASK_RUNNING); remove_wait_queue(&awd.wqh, &wait); return status; } was_sig = 0; // assume not a signal while (!awd.done) { schedule_timeout(1); set_current_state(TASK_UNINTERRUPTIBLE); rmb(); if(signal_pending(current)) { was_sig = 1; timeout = 0; // so goes thru timeout code break ; } if ((timeout) && (--timeout == 0)) { break ; // timeout occured } } set_current_state(TASK_RUNNING); remove_wait_queue(&awd.wqh, &wait); if (!timeout && !awd.done) { if (urb->status != -EINPROGRESS) // No callback?!! { err("usb: raced timeout, pipe 0x%x status %d time left %d", urb->pipe, urb->status, timeout); status = urb->status; } else { usb_unlink_urb(urb); // remove urb safely if (was_sig) { status = -EINTR; } else { err("usb_control/bulk_msg timeout"); status = -ETIMEDOUT; } } } else { status = urb->status; } if (actual_length) { *actual_length = urb->actual_length; } usb_free_urb(urb); return status; } #endif // LINUX_VERSION_CODE // ------------------------------------------------------------------------ // END code largely ripped from 'drivers/usb/usb.c' // ------------------------------------------------------------------------ #ifdef BG_READ static int vb_read_start(struct usb_vb *dev); // device still locked when called // post error to 'vb_read' static void vb_post_read_error(struct usb_vb *dev, int err) { dev->rdr_head = 0 ; // flush input data dev->rdr_tail = 0 ; // flush input data dev->rdr_status = err; } // gets called when read-urb completes static void vb_read_restart(urb_t *urb) { int status; int next_head; int minor = (int) (urb->context); struct usb_vb *dev; u_char *cp; dbg("dev(%d) entry", minor); status = urb->status; down (&minor_table_mutex); // lock table if ((dev = minor_table[minor]) == NULL) { // disconnected and closed dbg("disconnected and closed"); up (&minor_table_mutex); // unlock table usb_free_urb(urb); return; } up (&minor_table_mutex); // unlock table down (&dev->inp_sem); // lock this device object // verify that the device is still open if (dev->open_count <= 0) { // connected but closed dbg("dev(%d) connected but closed", minor); usb_free_urb(urb); dev->have_urb = NULL; up (&dev->inp_sem); // unlock the device return; } // verify that the device wasn't unplugged if (dev->udev == NULL) { // disconnected but still open dbg("dev(%d) disconnected but open", minor); usb_free_urb(urb); dev->have_urb = NULL; vb_post_read_error(dev, -ENODEV); up (&dev->inp_sem); // unlock the device return; } if(status) // has some kind of usb/device error { dbg("dev(%d) status(%d)", minor, status); usb_free_urb(urb); dev->have_urb = NULL; vb_post_read_error(dev, status); up (&dev->inp_sem); // unlock the device return; } if((next_head = (dev->rdr_head + 1)) >= RDR_BUF_CNT) { next_head = 0; } if(next_head == dev->rdr_tail) // would wrap and look empty if true { err("dev(%d) no-room, drop (%d) bytes", minor, urb->actual_length); } else if((urb->actual_length < 1) || (urb->actual_length > dev->inp_size)) { err("dev(%d) bad-length(%d)", minor, urb->actual_length); } else { // if gets here then have room, so store data in the read queue cp = dev->rdr_buffer + (dev->rdr_head * (dev->inp_size + 1)); *cp++ = urb->actual_length; memcpy(cp, dev->inp_buffer, urb->actual_length); dev->rdr_head = next_head; } // TODO: could re-use the urb instead usb_free_urb(urb); dev->have_urb = NULL; if ((status = vb_read_start(dev)) != 0) { dbg("dev(%d) read-error(%d)", minor, status); vb_post_read_error(dev, status); } // wakeup 'vb_read' if sleeping if (waitqueue_active(&dev->rdr_wqh)) wake_up_interruptible(&dev->rdr_wqh); wake_up(&dev->rdr_pwait); // unlock the device up (&dev->inp_sem); dbg("dev(%d) exit", minor); } // should be called with the device locked static int vb_read_start(struct usb_vb *dev) { int status; urb_t *urb; dbg("dev(%d) entry", dev->minor); if(dev->have_urb) { err("dev(%d) already has urb", dev->minor); return -EINVAL; } if ((urb = usb_alloc_urb(0)) == NULL) { err("dev(%d) can't alloc urb", dev->minor); return -ENOMEM; } FILL_BULK_URB(urb, dev->udev, usb_rcvbulkpipe (dev->udev, dev->inp_endpoint & 0x7f), dev->inp_buffer, dev->inp_size, vb_read_restart, 0); urb->context = ((void *)(dev->minor)); if ((status = usb_submit_urb(urb)) != 0) { // something went wrong err("dev(%d) submit error(%d)", dev->minor, status); usb_free_urb(urb); return status; } dev->have_urb = urb; dbg("dev(%d) exit", dev->minor); return 0; } #endif // BG_READ // // usb_vb_debug_data // static inline void usb_vb_debug_data (const char *function, int size, const unsigned char *data) { int i; if (debug < 2) { return; } printk (KERN_DEBUG __FILE__": %s - length = %d, data = ", function, size); for (i = 0; i < size; ++i) { printk ("%.2x ", data[i]); } printk ("\n"); } // // vb_delete // static inline void vb_delete (struct usb_vb *dev) { minor_table[dev->minor] = NULL; #ifdef BG_READ kfree (dev->rdr_buffer); #endif kfree (dev); } // // vb_open // static int vb_open (struct inode *inode, struct file *file) { struct usb_vb *dev = NULL; int subminor; int retval = 0; dbg("entry"); subminor = MINOR (inode->i_rdev) - USB_VB_MINOR_BASE; if ((subminor < 0) || (subminor >= MAX_DEVICES)) { return -ENODEV; } // increment our usage count for the module MOD_INC_USE_COUNT; // lock our minor table and get our local data for this minor down (&minor_table_mutex); dev = minor_table[subminor]; if (dev == NULL) { up (&minor_table_mutex); MOD_DEC_USE_COUNT; return -ENODEV; } // lock this device, both directions down (&dev->out_sem); down (&dev->inp_sem); // unlock the minor table up (&minor_table_mutex); #ifdef BG_READ if(dev->open_count == 0) // on first open, start reader on input pipe { init_waitqueue_head(&dev->rdr_wqh ); init_waitqueue_head(&dev->rdr_pwait); dev->rdr_head = 0 ; dev->rdr_tail = 0 ; dev->rdr_status = 0 ; dev->have_urb = NULL; if ((retval = vb_read_start(dev)) != 0) { // unlock this device, both directions up (&dev->inp_sem); up (&dev->out_sem); MOD_DEC_USE_COUNT; return retval; } } #endif // increment our usage count for the driver ++dev->open_count; // save our object in the file's private structure file->private_data = dev; // unlock this device, both directions up (&dev->inp_sem); up (&dev->out_sem); return retval; } // // vb_release // static int vb_release (struct inode *inode, struct file *file) { struct usb_vb *dev; int retval = 0; dev = (struct usb_vb *)file->private_data; if (dev == NULL) { dbg (__FUNCTION__ " - object is NULL"); return -ENODEV; } dbg(__FUNCTION__ " - minor %d", dev->minor); // lock our minor table down (&minor_table_mutex); // lock our device down (&dev->out_sem); down (&dev->inp_sem); if (dev->open_count <= 0) { dbg (__FUNCTION__ " - device not opened"); retval = -ENODEV; goto exit_not_opened; } // decrement our usage count for the device --dev->open_count; if (dev->open_count <= 0) { dev->open_count = 0; #ifdef BG_READ if(dev->have_urb) { // must unlock since the unlink will cause restart to be called // and will deadlock when it trys to lock up (&dev->inp_sem); up (&minor_table_mutex); usb_unlink_urb(dev->have_urb); // remove urb safely dev->have_urb = NULL; // now can relock down (&minor_table_mutex); down (&dev->inp_sem); } #endif if (dev->udev == NULL) { dbg (__FUNCTION__ " - device unplugged"); // the device was unplugged before the file was released // so held off rest of 'vb_disconnect' till now up (&dev->inp_sem); up (&dev->out_sem); vb_delete (dev); MOD_DEC_USE_COUNT; up (&minor_table_mutex); return 0; } } // decrement our usage count for the module MOD_DEC_USE_COUNT; exit_not_opened: up (&dev->inp_sem); up (&dev->out_sem); up (&minor_table_mutex); return retval; } // // vb_read // static ssize_t vb_read (struct file *file, char *buffer, size_t count, loff_t *ppos) { struct usb_vb *dev; int retval = 0; #ifdef BG_READ u_char *cp; #endif dev = (struct usb_vb *) file->private_data; dbg("minor %d, count = %d", dev->minor, count); down (&dev->inp_sem); // lock this object if (dev->udev == NULL) // verify that the device wasn't unplugged { up (&dev->inp_sem); return -ENODEV; } if (count == 0) { up (&dev->inp_sem); return 0; } #ifdef BG_READ while(dev->rdr_head == dev->rdr_tail) // block while empty { if((retval = dev->rdr_status) != 0) { dbg("dev(%d) error(%d)", dev->minor, retval); up (&dev->inp_sem); // unlock the device return retval; } if(signal_pending(current)) { dbg("dev(%d) have signal-pending", dev->minor); up (&dev->inp_sem); // unlock the device return -EINTR; } up (&dev->inp_sem); // unlock the device interruptible_sleep_on_timeout(&dev->rdr_wqh, RDR_TIMEOUT); down (&dev->inp_sem); // lock the device } // 'inp_sem' still locked at this point cp = dev->rdr_buffer + (dev->rdr_tail * (dev->inp_size + 1)); if((retval = *cp++) > count) { retval = count; } if (copy_to_user (buffer, cp, retval)) { dbg("dev(%d) copy_to_user error", dev->minor); retval = -EFAULT; } if((dev->rdr_tail = (dev->rdr_tail + 1)) >= RDR_BUF_CNT) { dev->rdr_tail = 0; } #else // BG_READ // do an immediate read to get data from the device retval = vb_bulk_msg (dev->udev, usb_rcvbulkpipe (dev->udev, dev->inp_endpoint & 0x7f), dev->inp_buffer, dev->inp_size, &count, 0); // if the read was successful, copy the data to userspace if (!retval && count) { if (copy_to_user (buffer, dev->inp_buffer, count)) retval = -EFAULT; else retval = count; } #endif // BG_READ up (&dev->inp_sem); // unlock the device return retval; } // // vb_write // static ssize_t vb_write (struct file *file, const char *buffer, size_t count, loff_t *ppos) { struct usb_vb *dev; ssize_t write_count; int retval = 0; dev = (struct usb_vb *) file->private_data; dbg("minor %d, count = %d", dev->minor, count); // lock this object down (&dev->out_sem); // verify that the device wasn't unplugged if (dev->udev == NULL) { retval = -ENODEV; goto exit; } // verify that we actually have some data to write if (count == 0) { dbg("write request of 0 bytes"); goto exit; } // we can only write as much as 1 urb will hold write_count = (count > dev->out_size) ? dev->out_size : count; // copy the data from userspace into our urb if (copy_from_user(dev->out_buffer, buffer, write_count)) { retval = -EFAULT; goto exit; } usb_vb_debug_data (__FUNCTION__, write_count, dev->out_buffer); // send the data to the device retval = vb_bulk_msg (dev->udev, usb_sndbulkpipe (dev->udev, dev->out_endpoint & 0x7f), dev->out_buffer, write_count, &count, VB_WRITE_TIMEOUT); if (retval >= 0) { retval = write_count;; } exit: up (&dev->out_sem); // unlock the device return retval; } #ifdef TODO static int vb_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) { struct usb_vb *dev = (struct usb_vb *)file->private_data; int ret = -ENOIOCTLCMD; // Count how much space the descriptors take // return error if not enough, perhaps ioctl to get count // copy descriptors return ret; } #endif #ifdef BG_READ static unsigned int vb_poll(struct file *file, struct poll_table_struct *wait) { struct usb_vb *dev = (struct usb_vb *) file->private_data; if(dev->rdr_head == dev->rdr_tail) // wait if empty { poll_wait(file, &dev->rdr_pwait, wait); } return ((dev->rdr_head == dev->rdr_tail) ? 0 : (POLLIN | POLLRDNORM)); } #endif static struct usb_vb * vb_dev_add(struct usb_device *udev, int minor, int pipe, int osize, int isize) { struct usb_vb *dev; // allocate memory for our device state and intialize it dev = kmalloc (sizeof(struct usb_vb), GFP_KERNEL); if (dev == NULL) { err("Out of memory(%d): struct usb_vb kmalloc", pipe); return NULL; } #ifdef BG_READ dev->rdr_buffer = kmalloc ((isize + 1) * RDR_BUF_CNT, GFP_KERNEL); if (dev->rdr_buffer == NULL) { err("Out of memory(%d): rdr_buffer kmalloc", pipe); kfree(dev); return NULL; } #endif init_MUTEX (&dev->out_sem) ; init_MUTEX (&dev->inp_sem) ; dev->udev = udev ; dev->minor = minor ; dev->out_size = osize ; dev->inp_size = isize ; dev->out_endpoint = pipe ; dev->inp_endpoint = pipe | 0x80; dev->open_count = 0 ; #ifdef BG_READ dev->rdr_head = 0 ; dev->rdr_tail = 0 ; dev->rdr_status = 0 ; dev->have_urb = NULL ; #endif minor_table[minor] = dev; return dev; } // // vb_probe // // Called by the usb core when a new device is connected that it thinks // this driver might be interested in. // #ifdef MODULE_DEVICE_TABLE static void * vb_probe(struct usb_device *udev, unsigned int ifnum, const struct usb_device_id *id) #else static void * vb_probe(struct usb_device *udev, unsigned int ifnum) #endif { struct usb_vb *c_dev = NULL; struct usb_vb *v_dev = NULL; int minor; // See if the device offered us matches what we can accept if ((udev->descriptor.idVendor != USB_VB_VENDOR_ID ) || (udev->descriptor.idProduct != USB_VB_PRODUCT_ID)) { return NULL; } // select a "subminor" number (part of a minor number) // get two entries since have two bidirectoional pipes down (&minor_table_mutex); for (minor = 0; minor < (MAX_DEVICES/2); minor++) { if (minor_table[minor] == NULL) break; } if (minor >= (MAX_DEVICES/2)) { err("Too many devices plugged in, can not handle this device."); goto done; } // allocate memory for our device state and intialize it (cmd/status) c_dev = vb_dev_add (udev, minor, 1, CMD_SIZE, STAT_SIZE); if (c_dev == NULL) { goto done; } // allocate memory for our device state and intialize it (voice-inp/out) v_dev = vb_dev_add (udev, minor + (MAX_DEVICES/2), 2, VOUT_SIZE, VINP_SIZE); if (v_dev == NULL) { vb_delete (c_dev); c_dev = NULL; goto done; } // let the user know what node this device is now attached to info("USB Voip-Blaster (%d) connected, ifnum(%d)", minor, ifnum); done: up (&minor_table_mutex); return c_dev; } // // vb_disconnect // // Called by the usb core when the device is removed from the system. // static void vb_disconnect(struct usb_device *udev, void *ptr) { struct usb_vb *c_dev; struct usb_vb *v_dev; int minor; c_dev = (struct usb_vb *)ptr; down (&minor_table_mutex); down (&c_dev->out_sem); down (&c_dev->inp_sem); minor = c_dev->minor; // get before do any cleanup // if the device is not opened, then we clean up right now if (!c_dev->open_count) { up (&c_dev->inp_sem); up (&c_dev->out_sem); vb_delete (c_dev); } else { c_dev->udev = NULL; // let 'vb_release' do 'vb_delete' later up (&c_dev->inp_sem); up (&c_dev->out_sem); } // now do the second half of the device v_dev = minor_table[minor + (MAX_DEVICES/2)]; down (&v_dev->out_sem); down (&v_dev->inp_sem); // if the device is not opened, then we clean up right now if (!v_dev->open_count) { up (&v_dev->out_sem); up (&v_dev->inp_sem); vb_delete (v_dev); } else { v_dev->udev = NULL; // let 'vb_release' do 'vb_delete' later up (&v_dev->out_sem); up (&v_dev->inp_sem); } info("USB Voip-Blaster (%d) disconnected", minor); up (&minor_table_mutex); } // ------------------------------------------------------------------------ // file operations needed when we register this driver static struct file_operations vb_fops = { #ifdef MODULE_DEVICE_TABLE owner: THIS_MODULE, #endif read: vb_read, write: vb_write, open: vb_open, #ifdef BG_READ poll: vb_poll, #endif release: vb_release, #ifdef TODO ioctl: vb_ioctl, #endif }; // usb specific object needed to register this driver with the usb subsystem static struct usb_driver vb_driver = { name: "usbvb", probe: vb_probe, disconnect: vb_disconnect, fops: &vb_fops, minor: USB_VB_MINOR_BASE, #ifdef MODULE_DEVICE_TABLE id_table: vb_table, #endif }; // ------------------------------------------------------------------------ // // usb_vb_init // static int __init usb_vb_init(void) { int result; // register this driver with the USB subsystem result = usb_register(&vb_driver); if (result < 0) { err("usb_register failed for the "__FILE__" driver. Error number %d", result); return -1; } info(DRIVER_DESC " " DRIVER_VERSION); return 0; } // // usb_vb_exit // static void __exit usb_vb_exit(void) { // deregister this driver with the USB subsystem usb_deregister(&vb_driver); } module_init (usb_vb_init); module_exit (usb_vb_exit); MODULE_AUTHOR(DRIVER_AUTHOR); MODULE_DESCRIPTION(DRIVER_DESC); #ifdef MODULE_LICENSE MODULE_LICENSE("GPL"); #endif // // The End! //