2005-04-17 02:20:36 +04:00
/*
2006-08-15 05:44:29 +04:00
* USB Skeleton driver - 2.2
2005-04-17 02:20:36 +04:00
*
* Copyright ( C ) 2001 - 2004 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 , version 2.
*
2006-08-15 05:44:29 +04:00
* This driver is based on the 2.6 .3 version of drivers / usb / usb - skeleton . c
2006-07-02 06:06:36 +04:00
* but has been rewritten to be easier to read and use .
2005-04-17 02:20:36 +04:00
*
*/
# 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>
2006-07-02 06:06:36 +04:00
# include <linux/mutex.h>
2005-04-17 02:20:36 +04:00
/* Define these values to match your devices */
# define USB_SKEL_VENDOR_ID 0xfff0
# define USB_SKEL_PRODUCT_ID 0xfff0
/* table of devices that work with this driver */
static struct usb_device_id skel_table [ ] = {
{ USB_DEVICE ( USB_SKEL_VENDOR_ID , USB_SKEL_PRODUCT_ID ) } ,
{ } /* Terminating entry */
} ;
2006-08-15 05:44:29 +04:00
MODULE_DEVICE_TABLE ( usb , skel_table ) ;
2005-04-17 02:20:36 +04:00
/* Get a minor range for your devices from the usb maintainer */
# define USB_SKEL_MINOR_BASE 192
2005-12-21 21:27:29 +03:00
/* our private defines. if this grows any larger, use your own .h file */
2006-08-15 05:44:29 +04:00
# define MAX_TRANSFER (PAGE_SIZE - 512)
2005-12-21 21:27:29 +03:00
# define WRITES_IN_FLIGHT 8
2005-04-17 02:20:36 +04:00
/* Structure to hold all of our device specific stuff */
struct usb_skel {
2006-08-15 05:44:29 +04:00
struct usb_device * dev ; /* the usb device for this device */
struct usb_interface * interface ; /* the interface for this device */
2005-12-21 21:27:29 +03:00
struct semaphore limit_sem ; /* limiting the number of writes in progress */
2006-08-15 05:44:29 +04:00
unsigned char * bulk_in_buffer ; /* the buffer to receive data */
2005-04-17 02:20:36 +04:00
size_t bulk_in_size ; /* the size of the receive buffer */
__u8 bulk_in_endpointAddr ; /* the address of the bulk in endpoint */
__u8 bulk_out_endpointAddr ; /* the address of the bulk out endpoint */
struct kref kref ;
2006-07-02 06:06:36 +04:00
struct mutex io_mutex ; /* synchronize I/O with disconnect */
2005-04-17 02:20:36 +04:00
} ;
# define to_skel_dev(d) container_of(d, struct usb_skel, kref)
static struct usb_driver skel_driver ;
static void skel_delete ( struct kref * kref )
2006-08-15 05:44:29 +04:00
{
2005-04-17 02:20:36 +04:00
struct usb_skel * dev = to_skel_dev ( kref ) ;
usb_put_dev ( dev - > udev ) ;
2006-08-15 05:44:29 +04:00
kfree ( dev - > bulk_in_buffer ) ;
kfree ( dev ) ;
2005-04-17 02:20:36 +04:00
}
static int skel_open ( struct inode * inode , struct file * file )
{
struct usb_skel * dev ;
struct usb_interface * interface ;
int subminor ;
int retval = 0 ;
subminor = iminor ( inode ) ;
interface = usb_find_interface ( & skel_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 ;
exit :
return retval ;
}
static int skel_release ( struct inode * inode , struct file * file )
{
struct usb_skel * dev ;
dev = ( struct usb_skel * ) file - > private_data ;
if ( dev = = NULL )
return - ENODEV ;
/* decrement the count on our device */
kref_put ( & dev - > kref , skel_delete ) ;
return 0 ;
}
static ssize_t skel_read ( struct file * file , char * buffer , size_t count , loff_t * ppos )
{
struct usb_skel * dev ;
2006-08-15 05:44:29 +04:00
int retval ;
2005-04-17 02:20:36 +04:00
int bytes_read ;
dev = ( struct usb_skel * ) file - > private_data ;
2006-07-02 06:06:36 +04:00
mutex_lock ( & dev - > io_mutex ) ;
if ( ! dev - > interface ) { /* disconnect() was called */
retval = - ENODEV ;
goto exit ;
}
2005-04-17 02:20:36 +04:00
/* do a blocking bulk read to get data from the device */
retval = usb_bulk_msg ( dev - > udev ,
usb_rcvbulkpipe ( dev - > udev , dev - > bulk_in_endpointAddr ) ,
dev - > bulk_in_buffer ,
min ( dev - > bulk_in_size , count ) ,
& bytes_read , 10000 ) ;
/* if the read was successful, copy the data to userspace */
if ( ! retval ) {
if ( copy_to_user ( buffer , dev - > bulk_in_buffer , bytes_read ) )
retval = - EFAULT ;
else
retval = bytes_read ;
}
2006-07-02 06:06:36 +04:00
exit :
mutex_unlock ( & dev - > io_mutex ) ;
2005-04-17 02:20:36 +04:00
return retval ;
}
static void skel_write_bulk_callback ( struct urb * urb , struct pt_regs * regs )
{
struct usb_skel * dev ;
dev = ( struct usb_skel * ) urb - > context ;
/* sync/async unlink faults aren't errors */
2006-08-15 05:44:29 +04:00
if ( urb - > status & &
! ( urb - > status = = - ENOENT | |
2005-04-17 02:20:36 +04:00
urb - > status = = - ECONNRESET | |
urb - > status = = - ESHUTDOWN ) ) {
2006-08-15 05:44:29 +04:00
err ( " %s - nonzero write bulk status received: %d " ,
2005-04-17 02:20:36 +04:00
__FUNCTION__ , urb - > status ) ;
}
/* free up our allocated buffer */
2006-08-15 05:44:29 +04:00
usb_buffer_free ( urb - > dev , urb - > transfer_buffer_length ,
2005-04-17 02:20:36 +04:00
urb - > transfer_buffer , urb - > transfer_dma ) ;
2005-12-21 21:27:29 +03:00
up ( & dev - > limit_sem ) ;
2005-04-17 02:20:36 +04:00
}
static ssize_t skel_write ( struct file * file , const char * user_buffer , size_t count , loff_t * ppos )
{
struct usb_skel * dev ;
int retval = 0 ;
struct urb * urb = NULL ;
char * buf = NULL ;
2005-12-23 03:11:02 +03:00
size_t writesize = min ( count , ( size_t ) MAX_TRANSFER ) ;
2005-04-17 02:20:36 +04:00
dev = ( struct usb_skel * ) file - > private_data ;
/* verify that we actually have some data to write */
if ( count = = 0 )
goto exit ;
2005-12-21 21:27:29 +03:00
/* limit the number of URBs in flight to stop a user from using up all RAM */
2005-12-23 03:11:02 +03:00
if ( down_interruptible ( & dev - > limit_sem ) ) {
retval = - ERESTARTSYS ;
goto exit ;
}
2005-12-21 21:27:29 +03:00
2006-07-02 06:06:36 +04:00
mutex_lock ( & dev - > io_mutex ) ;
if ( ! dev - > interface ) { /* disconnect() was called */
retval = - ENODEV ;
goto error ;
}
2005-04-17 02:20:36 +04:00
/* create a urb, and a buffer for it, and copy the data to the urb */
urb = usb_alloc_urb ( 0 , GFP_KERNEL ) ;
if ( ! urb ) {
retval = - ENOMEM ;
goto error ;
}
2005-12-21 21:27:29 +03:00
buf = usb_buffer_alloc ( dev - > udev , writesize , GFP_KERNEL , & urb - > transfer_dma ) ;
2005-04-17 02:20:36 +04:00
if ( ! buf ) {
retval = - ENOMEM ;
goto error ;
}
2005-12-21 21:27:29 +03:00
if ( copy_from_user ( buf , user_buffer , writesize ) ) {
2005-04-17 02:20:36 +04:00
retval = - EFAULT ;
goto error ;
}
/* initialize the urb properly */
usb_fill_bulk_urb ( urb , dev - > udev ,
usb_sndbulkpipe ( dev - > udev , dev - > bulk_out_endpointAddr ) ,
2005-12-21 21:27:29 +03:00
buf , writesize , skel_write_bulk_callback , dev ) ;
2005-04-17 02:20:36 +04:00
urb - > transfer_flags | = URB_NO_TRANSFER_DMA_MAP ;
/* 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 ) ;
2006-07-02 06:06:36 +04:00
mutex_unlock ( & dev - > io_mutex ) ;
2005-12-21 21:27:29 +03:00
return writesize ;
2005-04-17 02:20:36 +04:00
error :
2006-07-02 06:06:36 +04:00
if ( urb ) {
usb_buffer_free ( dev - > udev , writesize , buf , urb - > transfer_dma ) ;
usb_free_urb ( urb ) ;
}
mutex_unlock ( & dev - > io_mutex ) ;
2005-12-21 21:27:29 +03:00
up ( & dev - > limit_sem ) ;
2006-07-02 06:06:36 +04:00
exit :
2005-04-17 02:20:36 +04:00
return retval ;
}
2006-08-06 03:37:11 +04:00
static const struct file_operations skel_fops = {
2005-04-17 02:20:36 +04:00
. owner = THIS_MODULE ,
. read = skel_read ,
. write = skel_write ,
. open = skel_open ,
. release = skel_release ,
} ;
2006-08-15 05:44:29 +04:00
/*
2005-04-17 02:20:36 +04:00
* usb class driver info in order to get a minor number from the usb core ,
2006-01-19 01:36:58 +03:00
* and to have the device registered with the driver core
2005-04-17 02:20:36 +04:00
*/
static struct usb_class_driver skel_class = {
2005-06-21 08:15:16 +04:00
. name = " skel%d " ,
2005-04-17 02:20:36 +04:00
. fops = & skel_fops ,
. minor_base = USB_SKEL_MINOR_BASE ,
} ;
static int skel_probe ( struct usb_interface * interface , const struct usb_device_id * id )
{
2006-08-15 05:44:29 +04:00
struct usb_skel * dev ;
2005-04-17 02:20:36 +04:00
struct usb_host_interface * iface_desc ;
struct usb_endpoint_descriptor * endpoint ;
size_t buffer_size ;
int i ;
int retval = - ENOMEM ;
/* allocate memory for our device state and initialize it */
2005-12-21 21:27:29 +03:00
dev = kzalloc ( sizeof ( * dev ) , GFP_KERNEL ) ;
2006-08-15 05:44:29 +04:00
if ( ! dev ) {
2005-04-17 02:20:36 +04:00
err ( " Out of memory " ) ;
goto error ;
}
kref_init ( & dev - > kref ) ;
2005-12-21 21:27:29 +03:00
sema_init ( & dev - > limit_sem , WRITES_IN_FLIGHT ) ;
2006-07-02 06:06:36 +04:00
mutex_init ( & dev - > io_mutex ) ;
2005-04-17 02:20:36 +04:00
dev - > udev = usb_get_dev ( interface_to_usbdev ( interface ) ) ;
dev - > interface = interface ;
/* set up the endpoint information */
/* use only the first bulk-in and bulk-out endpoints */
iface_desc = interface - > cur_altsetting ;
for ( i = 0 ; i < iface_desc - > desc . bNumEndpoints ; + + i ) {
endpoint = & iface_desc - > endpoint [ i ] . desc ;
if ( ! dev - > bulk_in_endpointAddr & &
2006-08-15 05:44:29 +04:00
usb_endpoint_is_bulk_in ( endpoint ) ) {
2005-04-17 02:20:36 +04:00
/* we found a bulk in endpoint */
buffer_size = le16_to_cpu ( endpoint - > wMaxPacketSize ) ;
dev - > bulk_in_size = buffer_size ;
dev - > bulk_in_endpointAddr = endpoint - > bEndpointAddress ;
dev - > bulk_in_buffer = kmalloc ( buffer_size , GFP_KERNEL ) ;
if ( ! dev - > bulk_in_buffer ) {
err ( " Could not allocate bulk_in_buffer " ) ;
goto error ;
}
}
if ( ! dev - > bulk_out_endpointAddr & &
2006-08-15 05:44:29 +04:00
usb_endpoint_is_bulk_out ( endpoint ) ) {
2005-04-17 02:20:36 +04:00
/* we found a bulk out endpoint */
dev - > bulk_out_endpointAddr = endpoint - > bEndpointAddress ;
}
}
if ( ! ( dev - > bulk_in_endpointAddr & & dev - > bulk_out_endpointAddr ) ) {
err ( " Could not find both bulk-in and bulk-out endpoints " ) ;
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 , & skel_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 Skeleton device now attached to USBSkel-%d " , interface - > minor ) ;
return 0 ;
error :
if ( dev )
kref_put ( & dev - > kref , skel_delete ) ;
return retval ;
}
static void skel_disconnect ( struct usb_interface * interface )
{
struct usb_skel * dev ;
int minor = interface - > minor ;
/* prevent skel_open() from racing skel_disconnect() */
lock_kernel ( ) ;
dev = usb_get_intfdata ( interface ) ;
usb_set_intfdata ( interface , NULL ) ;
/* give back our minor */
usb_deregister_dev ( interface , & skel_class ) ;
2006-07-02 06:06:36 +04:00
/* prevent more I/O from starting */
mutex_lock ( & dev - > io_mutex ) ;
dev - > interface = NULL ;
mutex_unlock ( & dev - > io_mutex ) ;
2005-04-17 02:20:36 +04:00
unlock_kernel ( ) ;
/* decrement our usage count */
kref_put ( & dev - > kref , skel_delete ) ;
info ( " USB Skeleton #%d now disconnected " , minor ) ;
}
static struct usb_driver skel_driver = {
. name = " skeleton " ,
. probe = skel_probe ,
. disconnect = skel_disconnect ,
. id_table = skel_table ,
} ;
static int __init usb_skel_init ( void )
{
int result ;
/* register this driver with the USB subsystem */
result = usb_register ( & skel_driver ) ;
if ( result )
err ( " usb_register failed. Error number %d " , result ) ;
return result ;
}
static void __exit usb_skel_exit ( void )
{
/* deregister this driver with the USB subsystem */
usb_deregister ( & skel_driver ) ;
}
2006-08-15 05:44:29 +04:00
module_init ( usb_skel_init ) ;
module_exit ( usb_skel_exit ) ;
2005-04-17 02:20:36 +04:00
MODULE_LICENSE ( " GPL " ) ;