2008-09-30 11:39:38 -07:00
/*****************************************************************************
* File : drivers / usb / misc / vstusb . c
*
* Purpose : Support for the bulk USB Vernier Spectrophotometers
*
* Author : Johnnie Peters
* Axian Consulting
* Beaverton , OR , USA 97005
*
* Modified by : EQware Engineering , Inc .
* Oregon City , OR , USA 97045
*
* Copyright : 2007 , 2008
* Vernier Software & Technology
* Beaverton , OR , USA 97005
*
* Web : www . vernier . com
*
* This program is free software ; you can redistribute it and / or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation .
*
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
# include <linux/kernel.h>
# include <linux/errno.h>
# include <linux/init.h>
# include <linux/slab.h>
# include <linux/module.h>
# include <linux/mutex.h>
# include <linux/uaccess.h>
# include <linux/usb.h>
# include <linux/usb/vstusb.h>
# define DRIVER_VERSION "VST USB Driver Version 1.5"
# define DRIVER_DESC "Vernier Software Technology Bulk USB Driver"
# ifdef CONFIG_USB_DYNAMIC_MINORS
# define VSTUSB_MINOR_BASE 0
# else
# define VSTUSB_MINOR_BASE 199
# endif
# define USB_VENDOR_OCEANOPTICS 0x2457
# define USB_VENDOR_VERNIER 0x08F7 /* Vernier Software & Technology */
# define USB_PRODUCT_USB2000 0x1002
# define USB_PRODUCT_ADC1000_FW 0x1003 /* firmware download (renumerates) */
# define USB_PRODUCT_ADC1000 0x1004
# define USB_PRODUCT_HR2000_FW 0x1009 /* firmware download (renumerates) */
# define USB_PRODUCT_HR2000 0x100A
# define USB_PRODUCT_HR4000_FW 0x1011 /* firmware download (renumerates) */
# define USB_PRODUCT_HR4000 0x1012
# define USB_PRODUCT_USB650 0x1014 /* "Red Tide" */
# define USB_PRODUCT_QE65000 0x1018
# define USB_PRODUCT_USB4000 0x1022
# define USB_PRODUCT_USB325 0x1024 /* "Vernier Spectrometer" */
# define USB_PRODUCT_LABPRO 0x0001
# define USB_PRODUCT_LABQUEST 0x0005
2008-10-08 10:53:56 -07:00
# define VST_MAXBUFFER (64*1024)
2008-09-30 11:39:38 -07:00
static struct usb_device_id id_table [ ] = {
{ USB_DEVICE ( USB_VENDOR_OCEANOPTICS , USB_PRODUCT_USB2000 ) } ,
{ USB_DEVICE ( USB_VENDOR_OCEANOPTICS , USB_PRODUCT_HR4000 ) } ,
{ USB_DEVICE ( USB_VENDOR_OCEANOPTICS , USB_PRODUCT_USB650 ) } ,
{ USB_DEVICE ( USB_VENDOR_OCEANOPTICS , USB_PRODUCT_USB4000 ) } ,
{ USB_DEVICE ( USB_VENDOR_OCEANOPTICS , USB_PRODUCT_USB325 ) } ,
{ USB_DEVICE ( USB_VENDOR_VERNIER , USB_PRODUCT_LABQUEST ) } ,
{ USB_DEVICE ( USB_VENDOR_VERNIER , USB_PRODUCT_LABPRO ) } ,
{ } ,
} ;
MODULE_DEVICE_TABLE ( usb , id_table ) ;
struct vstusb_device {
2008-10-08 10:53:56 -07:00
struct kref kref ;
2008-09-30 11:39:38 -07:00
struct mutex lock ;
struct usb_device * usb_dev ;
char present ;
char isopen ;
struct usb_anchor submitted ;
int rd_pipe ;
int rd_timeout_ms ;
int wr_pipe ;
int wr_timeout_ms ;
} ;
2008-10-08 10:53:56 -07:00
# define to_vst_dev(d) container_of(d, struct vstusb_device, kref)
2008-09-30 11:39:38 -07:00
static struct usb_driver vstusb_driver ;
2008-10-08 10:53:56 -07:00
static void vstusb_delete ( struct kref * kref )
{
struct vstusb_device * vstdev = to_vst_dev ( kref ) ;
usb_put_dev ( vstdev - > usb_dev ) ;
kfree ( vstdev ) ;
}
2008-09-30 11:39:38 -07:00
static int vstusb_open ( struct inode * inode , struct file * file )
{
struct vstusb_device * vstdev ;
struct usb_interface * interface ;
interface = usb_find_interface ( & vstusb_driver , iminor ( inode ) ) ;
if ( ! interface ) {
printk ( KERN_ERR KBUILD_MODNAME
" : %s - error, can't find device for minor %d \n " ,
__func__ , iminor ( inode ) ) ;
return - ENODEV ;
}
vstdev = usb_get_intfdata ( interface ) ;
if ( ! vstdev )
return - ENODEV ;
/* lock this device */
mutex_lock ( & vstdev - > lock ) ;
/* can only open one time */
if ( ( ! vstdev - > present ) | | ( vstdev - > isopen ) ) {
mutex_unlock ( & vstdev - > lock ) ;
return - EBUSY ;
}
2008-10-08 10:53:56 -07:00
/* increment our usage count */
kref_get ( & vstdev - > kref ) ;
2008-09-30 11:39:38 -07:00
vstdev - > isopen = 1 ;
/* save device in the file's private structure */
file - > private_data = vstdev ;
dev_dbg ( & vstdev - > usb_dev - > dev , " %s: opened \n " , __func__ ) ;
mutex_unlock ( & vstdev - > lock ) ;
return 0 ;
}
2008-10-08 10:53:56 -07:00
static int vstusb_release ( struct inode * inode , struct file * file )
2008-09-30 11:39:38 -07:00
{
struct vstusb_device * vstdev ;
vstdev = file - > private_data ;
if ( vstdev = = NULL )
return - ENODEV ;
mutex_lock ( & vstdev - > lock ) ;
vstdev - > isopen = 0 ;
2008-10-08 10:53:56 -07:00
dev_dbg ( & vstdev - > usb_dev - > dev , " %s: released \n " , __func__ ) ;
mutex_unlock ( & vstdev - > lock ) ;
kref_put ( & vstdev - > kref , vstusb_delete ) ;
2008-09-30 11:39:38 -07:00
return 0 ;
}
static void usb_api_blocking_completion ( struct urb * urb )
{
struct completion * completeit = urb - > context ;
complete ( completeit ) ;
}
static int vstusb_fill_and_send_urb ( struct urb * urb ,
struct usb_device * usb_dev ,
unsigned int pipe , void * data ,
unsigned int len , struct completion * done )
{
struct usb_host_endpoint * ep ;
struct usb_host_endpoint * * hostep ;
unsigned int pipend ;
int status ;
hostep = usb_pipein ( pipe ) ? usb_dev - > ep_in : usb_dev - > ep_out ;
pipend = usb_pipeendpoint ( pipe ) ;
ep = hostep [ pipend ] ;
if ( ! ep | | ( len = = 0 ) )
return - EINVAL ;
if ( ( ep - > desc . bmAttributes & USB_ENDPOINT_XFERTYPE_MASK )
= = USB_ENDPOINT_XFER_INT ) {
pipe = ( pipe & ~ ( 3 < < 30 ) ) | ( PIPE_INTERRUPT < < 30 ) ;
usb_fill_int_urb ( urb , usb_dev , pipe , data , len ,
( usb_complete_t ) usb_api_blocking_completion ,
NULL , ep - > desc . bInterval ) ;
} else
usb_fill_bulk_urb ( urb , usb_dev , pipe , data , len ,
( usb_complete_t ) usb_api_blocking_completion ,
NULL ) ;
init_completion ( done ) ;
urb - > context = done ;
urb - > actual_length = 0 ;
status = usb_submit_urb ( urb , GFP_KERNEL ) ;
return status ;
}
static int vstusb_complete_urb ( struct urb * urb , struct completion * done ,
int timeout , int * actual_length )
{
unsigned long expire ;
int status ;
expire = timeout ? msecs_to_jiffies ( timeout ) : MAX_SCHEDULE_TIMEOUT ;
if ( ! wait_for_completion_interruptible_timeout ( done , expire ) ) {
usb_kill_urb ( urb ) ;
status = urb - > status = = - ENOENT ? - ETIMEDOUT : urb - > status ;
dev_dbg ( & urb - > dev - > dev ,
" %s timed out on ep%d%s len=%d/%d, urb status = %d \n " ,
current - > comm ,
usb_pipeendpoint ( urb - > pipe ) ,
usb_pipein ( urb - > pipe ) ? " in " : " out " ,
urb - > actual_length ,
urb - > transfer_buffer_length ,
urb - > status ) ;
} else {
if ( signal_pending ( current ) ) {
/* if really an error */
if ( urb - > status & & ! ( ( urb - > status = = - ENOENT ) | |
( urb - > status = = - ECONNRESET ) | |
( urb - > status = = - ESHUTDOWN ) ) ) {
status = - EINTR ;
usb_kill_urb ( urb ) ;
} else {
status = 0 ;
}
dev_dbg ( & urb - > dev - > dev ,
" %s: signal pending on ep%d%s len=%d/%d, "
" urb status = %d \n " ,
current - > comm ,
usb_pipeendpoint ( urb - > pipe ) ,
usb_pipein ( urb - > pipe ) ? " in " : " out " ,
urb - > actual_length ,
urb - > transfer_buffer_length ,
urb - > status ) ;
} else {
status = urb - > status ;
}
}
if ( actual_length )
* actual_length = urb - > actual_length ;
return status ;
}
static ssize_t vstusb_read ( struct file * file , char __user * buffer ,
size_t count , loff_t * ppos )
{
struct vstusb_device * vstdev ;
int cnt = - 1 ;
void * buf ;
int retval = 0 ;
struct urb * urb ;
struct usb_device * dev ;
unsigned int pipe ;
int timeout ;
DECLARE_COMPLETION_ONSTACK ( done ) ;
vstdev = file - > private_data ;
if ( vstdev = = NULL )
return - ENODEV ;
/* verify that we actually want to read some data */
2008-10-08 10:53:56 -07:00
if ( ( count = = 0 ) | | ( count > VST_MAXBUFFER ) )
2008-09-30 11:39:38 -07:00
return - EINVAL ;
/* lock this object */
if ( mutex_lock_interruptible ( & vstdev - > lock ) )
return - ERESTARTSYS ;
/* anyone home */
if ( ! vstdev - > present ) {
mutex_unlock ( & vstdev - > lock ) ;
printk ( KERN_ERR KBUILD_MODNAME
" : %s: device not present \n " , __func__ ) ;
return - ENODEV ;
}
/* pull out the necessary data */
dev = vstdev - > usb_dev ;
pipe = usb_rcvbulkpipe ( dev , vstdev - > rd_pipe ) ;
timeout = vstdev - > rd_timeout_ms ;
buf = kmalloc ( count , GFP_KERNEL ) ;
if ( buf = = NULL ) {
mutex_unlock ( & vstdev - > lock ) ;
return - ENOMEM ;
}
urb = usb_alloc_urb ( 0 , GFP_KERNEL ) ;
if ( ! urb ) {
kfree ( buf ) ;
mutex_unlock ( & vstdev - > lock ) ;
return - ENOMEM ;
}
usb_anchor_urb ( urb , & vstdev - > submitted ) ;
retval = vstusb_fill_and_send_urb ( urb , dev , pipe , buf , count , & done ) ;
mutex_unlock ( & vstdev - > lock ) ;
if ( retval ) {
usb_unanchor_urb ( urb ) ;
dev_err ( & dev - > dev , " %s: error %d filling and sending urb %d \n " ,
__func__ , retval , pipe ) ;
goto exit ;
}
retval = vstusb_complete_urb ( urb , & done , timeout , & cnt ) ;
if ( retval ) {
dev_err ( & dev - > dev , " %s: error %d completing urb %d \n " ,
__func__ , retval , pipe ) ;
goto exit ;
}
if ( copy_to_user ( buffer , buf , cnt ) ) {
dev_err ( & dev - > dev , " %s: can't copy_to_user \n " , __func__ ) ;
retval = - EFAULT ;
} else {
retval = cnt ;
dev_dbg ( & dev - > dev , " %s: read %d bytes from pipe %d \n " ,
__func__ , cnt , pipe ) ;
}
exit :
usb_free_urb ( urb ) ;
kfree ( buf ) ;
return retval ;
}
static ssize_t vstusb_write ( struct file * file , const char __user * buffer ,
size_t count , loff_t * ppos )
{
struct vstusb_device * vstdev ;
int cnt = - 1 ;
void * buf ;
int retval = 0 ;
struct urb * urb ;
struct usb_device * dev ;
unsigned int pipe ;
int timeout ;
DECLARE_COMPLETION_ONSTACK ( done ) ;
vstdev = file - > private_data ;
if ( vstdev = = NULL )
return - ENODEV ;
/* verify that we actually have some data to write */
2008-10-08 10:53:56 -07:00
if ( ( count = = 0 ) | | ( count > VST_MAXBUFFER ) )
2008-09-30 11:39:38 -07:00
return retval ;
/* lock this object */
if ( mutex_lock_interruptible ( & vstdev - > lock ) )
return - ERESTARTSYS ;
/* anyone home */
if ( ! vstdev - > present ) {
mutex_unlock ( & vstdev - > lock ) ;
printk ( KERN_ERR KBUILD_MODNAME
" : %s: device not present \n " , __func__ ) ;
return - ENODEV ;
}
/* pull out the necessary data */
dev = vstdev - > usb_dev ;
pipe = usb_sndbulkpipe ( dev , vstdev - > wr_pipe ) ;
timeout = vstdev - > wr_timeout_ms ;
buf = kmalloc ( count , GFP_KERNEL ) ;
if ( buf = = NULL ) {
mutex_unlock ( & vstdev - > lock ) ;
return - ENOMEM ;
}
urb = usb_alloc_urb ( 0 , GFP_KERNEL ) ;
if ( ! urb ) {
kfree ( buf ) ;
mutex_unlock ( & vstdev - > lock ) ;
return - ENOMEM ;
}
if ( copy_from_user ( buf , buffer , count ) ) {
2009-03-11 21:47:39 +01:00
mutex_unlock ( & vstdev - > lock ) ;
2008-09-30 11:39:38 -07:00
dev_err ( & dev - > dev , " %s: can't copy_from_user \n " , __func__ ) ;
retval = - EFAULT ;
goto exit ;
}
usb_anchor_urb ( urb , & vstdev - > submitted ) ;
retval = vstusb_fill_and_send_urb ( urb , dev , pipe , buf , count , & done ) ;
mutex_unlock ( & vstdev - > lock ) ;
if ( retval ) {
usb_unanchor_urb ( urb ) ;
dev_err ( & dev - > dev , " %s: error %d filling and sending urb %d \n " ,
__func__ , retval , pipe ) ;
goto exit ;
}
retval = vstusb_complete_urb ( urb , & done , timeout , & cnt ) ;
if ( retval ) {
dev_err ( & dev - > dev , " %s: error %d completing urb %d \n " ,
__func__ , retval , pipe ) ;
goto exit ;
} else {
retval = cnt ;
dev_dbg ( & dev - > dev , " %s: sent %d bytes to pipe %d \n " ,
__func__ , cnt , pipe ) ;
}
exit :
usb_free_urb ( urb ) ;
kfree ( buf ) ;
return retval ;
}
static long vstusb_ioctl ( struct file * file , unsigned int cmd , unsigned long arg )
{
int retval = 0 ;
int cnt = - 1 ;
void __user * data = ( void __user * ) arg ;
struct vstusb_args usb_data ;
struct vstusb_device * vstdev ;
void * buffer = NULL ; /* must be initialized. buffer is
* referenced on exit but not all
* ioctls allocate it */
struct urb * urb = NULL ; /* must be initialized. urb is
* referenced on exit but not all
* ioctls allocate it */
struct usb_device * dev ;
unsigned int pipe ;
int timeout ;
DECLARE_COMPLETION_ONSTACK ( done ) ;
vstdev = file - > private_data ;
if ( _IOC_TYPE ( cmd ) ! = VST_IOC_MAGIC ) {
dev_warn ( & vstdev - > usb_dev - > dev ,
" %s: ioctl command %x, bad ioctl magic %x, "
" expected %x \n " , __func__ , cmd ,
_IOC_TYPE ( cmd ) , VST_IOC_MAGIC ) ;
return - EINVAL ;
}
if ( vstdev = = NULL )
return - ENODEV ;
if ( copy_from_user ( & usb_data , data , sizeof ( struct vstusb_args ) ) ) {
dev_err ( & vstdev - > usb_dev - > dev , " %s: can't copy_from_user \n " ,
__func__ ) ;
return - EFAULT ;
}
/* lock this object */
if ( mutex_lock_interruptible ( & vstdev - > lock ) ) {
retval = - ERESTARTSYS ;
goto exit ;
}
/* anyone home */
if ( ! vstdev - > present ) {
mutex_unlock ( & vstdev - > lock ) ;
dev_err ( & vstdev - > usb_dev - > dev , " %s: device not present \n " ,
__func__ ) ;
retval = - ENODEV ;
goto exit ;
}
/* pull out the necessary data */
dev = vstdev - > usb_dev ;
switch ( cmd ) {
case IOCTL_VSTUSB_CONFIG_RW :
vstdev - > rd_pipe = usb_data . rd_pipe ;
vstdev - > rd_timeout_ms = usb_data . rd_timeout_ms ;
vstdev - > wr_pipe = usb_data . wr_pipe ;
vstdev - > wr_timeout_ms = usb_data . wr_timeout_ms ;
mutex_unlock ( & vstdev - > lock ) ;
dev_dbg ( & dev - > dev , " %s: setting pipes/timeouts, "
" rdpipe = %d, rdtimeout = %d, "
" wrpipe = %d, wrtimeout = %d \n " , __func__ ,
vstdev - > rd_pipe , vstdev - > rd_timeout_ms ,
vstdev - > wr_pipe , vstdev - > wr_timeout_ms ) ;
break ;
case IOCTL_VSTUSB_SEND_PIPE :
2008-10-08 10:53:56 -07:00
if ( ( usb_data . count = = 0 ) | | ( usb_data . count > VST_MAXBUFFER ) ) {
2008-09-30 11:39:38 -07:00
mutex_unlock ( & vstdev - > lock ) ;
retval = - EINVAL ;
goto exit ;
}
buffer = kmalloc ( usb_data . count , GFP_KERNEL ) ;
if ( buffer = = NULL ) {
mutex_unlock ( & vstdev - > lock ) ;
retval = - ENOMEM ;
goto exit ;
}
urb = usb_alloc_urb ( 0 , GFP_KERNEL ) ;
if ( ! urb ) {
mutex_unlock ( & vstdev - > lock ) ;
retval = - ENOMEM ;
goto exit ;
}
timeout = usb_data . timeout_ms ;
pipe = usb_sndbulkpipe ( dev , usb_data . pipe ) ;
if ( copy_from_user ( buffer , usb_data . buffer , usb_data . count ) ) {
dev_err ( & dev - > dev , " %s: can't copy_from_user \n " ,
__func__ ) ;
mutex_unlock ( & vstdev - > lock ) ;
retval = - EFAULT ;
goto exit ;
}
usb_anchor_urb ( urb , & vstdev - > submitted ) ;
retval = vstusb_fill_and_send_urb ( urb , dev , pipe , buffer ,
usb_data . count , & done ) ;
mutex_unlock ( & vstdev - > lock ) ;
if ( retval ) {
usb_unanchor_urb ( urb ) ;
dev_err ( & dev - > dev ,
" %s: error %d filling and sending urb %d \n " ,
__func__ , retval , pipe ) ;
goto exit ;
}
retval = vstusb_complete_urb ( urb , & done , timeout , & cnt ) ;
if ( retval ) {
dev_err ( & dev - > dev , " %s: error %d completing urb %d \n " ,
__func__ , retval , pipe ) ;
}
break ;
case IOCTL_VSTUSB_RECV_PIPE :
2008-10-08 10:53:56 -07:00
if ( ( usb_data . count = = 0 ) | | ( usb_data . count > VST_MAXBUFFER ) ) {
2008-09-30 11:39:38 -07:00
mutex_unlock ( & vstdev - > lock ) ;
retval = - EINVAL ;
goto exit ;
}
buffer = kmalloc ( usb_data . count , GFP_KERNEL ) ;
if ( buffer = = NULL ) {
mutex_unlock ( & vstdev - > lock ) ;
retval = - ENOMEM ;
goto exit ;
}
urb = usb_alloc_urb ( 0 , GFP_KERNEL ) ;
if ( ! urb ) {
mutex_unlock ( & vstdev - > lock ) ;
retval = - ENOMEM ;
goto exit ;
}
timeout = usb_data . timeout_ms ;
pipe = usb_rcvbulkpipe ( dev , usb_data . pipe ) ;
usb_anchor_urb ( urb , & vstdev - > submitted ) ;
retval = vstusb_fill_and_send_urb ( urb , dev , pipe , buffer ,
usb_data . count , & done ) ;
mutex_unlock ( & vstdev - > lock ) ;
if ( retval ) {
usb_unanchor_urb ( urb ) ;
dev_err ( & dev - > dev ,
" %s: error %d filling and sending urb %d \n " ,
__func__ , retval , pipe ) ;
goto exit ;
}
retval = vstusb_complete_urb ( urb , & done , timeout , & cnt ) ;
if ( retval ) {
dev_err ( & dev - > dev , " %s: error %d completing urb %d \n " ,
__func__ , retval , pipe ) ;
goto exit ;
}
if ( copy_to_user ( usb_data . buffer , buffer , cnt ) ) {
dev_err ( & dev - > dev , " %s: can't copy_to_user \n " ,
__func__ ) ;
retval = - EFAULT ;
goto exit ;
}
usb_data . count = cnt ;
if ( copy_to_user ( data , & usb_data , sizeof ( struct vstusb_args ) ) ) {
dev_err ( & dev - > dev , " %s: can't copy_to_user \n " ,
__func__ ) ;
retval = - EFAULT ;
} else {
2008-10-31 10:09:57 -07:00
dev_dbg ( & dev - > dev , " %s: recv %zd bytes from pipe %d \n " ,
2008-09-30 11:39:38 -07:00
__func__ , usb_data . count , usb_data . pipe ) ;
}
break ;
default :
mutex_unlock ( & vstdev - > lock ) ;
dev_warn ( & dev - > dev , " ioctl_vstusb: invalid ioctl cmd %x \n " ,
cmd ) ;
return - EINVAL ;
break ;
}
exit :
usb_free_urb ( urb ) ;
kfree ( buffer ) ;
return retval ;
}
static const struct file_operations vstusb_fops = {
. owner = THIS_MODULE ,
. read = vstusb_read ,
. write = vstusb_write ,
. unlocked_ioctl = vstusb_ioctl ,
. compat_ioctl = vstusb_ioctl ,
. open = vstusb_open ,
2008-10-08 10:53:56 -07:00
. release = vstusb_release ,
2008-09-30 11:39:38 -07:00
} ;
static struct usb_class_driver usb_vstusb_class = {
. name = " usb/vstusb%d " ,
. fops = & vstusb_fops ,
. minor_base = VSTUSB_MINOR_BASE ,
} ;
static int vstusb_probe ( struct usb_interface * intf ,
const struct usb_device_id * id )
{
struct usb_device * dev = interface_to_usbdev ( intf ) ;
struct vstusb_device * vstdev ;
int i ;
int retval = 0 ;
/* allocate memory for our device state and intialize it */
vstdev = kzalloc ( sizeof ( * vstdev ) , GFP_KERNEL ) ;
if ( vstdev = = NULL )
return - ENOMEM ;
2008-10-08 10:53:56 -07:00
/* must do usb_get_dev() prior to kref_init() since the kref_put()
* release function will do a usb_put_dev ( ) */
usb_get_dev ( dev ) ;
kref_init ( & vstdev - > kref ) ;
2008-09-30 11:39:38 -07:00
mutex_init ( & vstdev - > lock ) ;
i = dev - > descriptor . bcdDevice ;
dev_dbg ( & intf - > dev , " Version %1d%1d.%1d%1d found at address %d \n " ,
( i & 0xF000 ) > > 12 , ( i & 0xF00 ) > > 8 ,
( i & 0xF0 ) > > 4 , ( i & 0xF ) , dev - > devnum ) ;
vstdev - > present = 1 ;
vstdev - > isopen = 0 ;
vstdev - > usb_dev = dev ;
init_usb_anchor ( & vstdev - > submitted ) ;
usb_set_intfdata ( intf , vstdev ) ;
retval = usb_register_dev ( intf , & usb_vstusb_class ) ;
if ( retval ) {
dev_err ( & intf - > dev ,
" %s: Not able to get a minor for this device. \n " ,
__func__ ) ;
usb_set_intfdata ( intf , NULL ) ;
2008-10-08 10:53:56 -07:00
kref_put ( & vstdev - > kref , vstusb_delete ) ;
2008-09-30 11:39:38 -07:00
return retval ;
}
/* let the user know what node this device is now attached to */
dev_info ( & intf - > dev ,
" VST USB Device #%d now attached to major %d minor %d \n " ,
( intf - > minor - VSTUSB_MINOR_BASE ) , USB_MAJOR , intf - > minor ) ;
dev_info ( & intf - > dev , " %s, %s \n " , DRIVER_DESC , DRIVER_VERSION ) ;
return retval ;
}
static void vstusb_disconnect ( struct usb_interface * intf )
{
struct vstusb_device * vstdev = usb_get_intfdata ( intf ) ;
usb_deregister_dev ( intf , & usb_vstusb_class ) ;
usb_set_intfdata ( intf , NULL ) ;
if ( vstdev ) {
mutex_lock ( & vstdev - > lock ) ;
vstdev - > present = 0 ;
usb_kill_anchored_urbs ( & vstdev - > submitted ) ;
2008-10-08 10:53:56 -07:00
mutex_unlock ( & vstdev - > lock ) ;
2008-09-30 11:39:38 -07:00
2008-10-08 10:53:56 -07:00
kref_put ( & vstdev - > kref , vstusb_delete ) ;
2008-09-30 11:39:38 -07:00
}
2008-10-08 10:53:56 -07:00
2008-09-30 11:39:38 -07:00
}
static int vstusb_suspend ( struct usb_interface * intf , pm_message_t message )
{
struct vstusb_device * vstdev = usb_get_intfdata ( intf ) ;
int time ;
if ( ! vstdev )
return 0 ;
mutex_lock ( & vstdev - > lock ) ;
time = usb_wait_anchor_empty_timeout ( & vstdev - > submitted , 1000 ) ;
if ( ! time )
usb_kill_anchored_urbs ( & vstdev - > submitted ) ;
mutex_unlock ( & vstdev - > lock ) ;
return 0 ;
}
static int vstusb_resume ( struct usb_interface * intf )
{
return 0 ;
}
static struct usb_driver vstusb_driver = {
. name = " vstusb " ,
. probe = vstusb_probe ,
. disconnect = vstusb_disconnect ,
. suspend = vstusb_suspend ,
. resume = vstusb_resume ,
. id_table = id_table ,
} ;
static int __init vstusb_init ( void )
{
int rc ;
rc = usb_register ( & vstusb_driver ) ;
if ( rc )
printk ( KERN_ERR " %s: failed to register (%d) " , __func__ , rc ) ;
return rc ;
}
static void __exit vstusb_exit ( void )
{
usb_deregister ( & vstusb_driver ) ;
}
module_init ( vstusb_init ) ;
module_exit ( vstusb_exit ) ;
MODULE_AUTHOR ( " Dennis O'Brien/Stephen Ware " ) ;
MODULE_DESCRIPTION ( DRIVER_VERSION ) ;
MODULE_LICENSE ( " GPL " ) ;