/************************************************************************\
## ##
## Creation Date: 17 Mar 2007 ##
## Last Update: 18 Mar 2007 ##
## Author: XInstruments ##
## ##
\************************************************************************/
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/usb.h>
#include <asm/uaccess.h>
#include "usmcdrv-driver.h"
#include "usmctypes.h"
#include "usmcpkt.h"
#include <linux/slab.h>
#if HAVE_UNLOCKED_IOCTL
#include <linux/mutex.h>
#else
#include <linux/smp_lock.h>
#endif
#define info( format, arg...) printk(KERN_INFO USMC_DEV_NAME ": " format "\n", ## arg)
#define err( format, arg...) printk(KERN_ERR USMC_DEV_NAME ": " format "\n", ## arg)
MODULE_DESCRIPTION
( "MicroSMC driver for Linux" );
MODULE_AUTHOR
( "XInstruments" );
MODULE_LICENSE
( "GPL" );
MODULE_VERSION
( "1.0" );
static int num_devices
= 0; // Number of currently connected devices.
/*-----------------------------------------------------
Init/Deinit section
-----------------------------------------------------*/
static int usmcdrv_init_module
( void )
{
int res
;
printk
( KERN_DEBUG
"Module usmcdrv init\n" );
res
= usb_register
( &usmc_driver
);
if ( res
)
err
( "usb_register failed. Error number %d", res
);
return res
;
}
static void usmcdrv_exit_module
( void )
{
printk
( KERN_DEBUG
"Module usmcdrv exit\n" );
usb_deregister
( &usmc_driver
);
}
module_init
( usmcdrv_init_module
);
module_exit
( usmcdrv_exit_module
);
/*-----------------------------------------------------
Connect/Disconnect section
-----------------------------------------------------*/
static int usmc_probe
( struct usb_interface
* interface
,
const struct usb_device_id
* id
)
{
struct usb_usmc
* dev
= NULL
;
int retval
= -ENOMEM
;
dev
= kzalloc
( sizeof ( *dev
), GFP_KERNEL
);
if ( dev
== NULL
) {
err
( "Out of memory" );
goto error
;
}
kref_init
( &dev
-> kref
);
sema_init
( &dev
-> limit_sem
, WRITES_IN_FLIGHT
);
dev
-> udev
= usb_get_dev
( interface_to_usbdev
( interface
) );
dev
-> interface
= interface
;
usb_set_intfdata
( interface
, dev
);
retval
= usb_register_dev
( interface
, &usmc_class
);
if ( retval
)
{
err
( "Not able to get a minor for this device." );
usb_set_intfdata
( interface
, NULL
);
goto error
;
}
info
( USMC_DEV_NAME
" device now attached to USBusmc-%d", interface
-> minor
);
num_devices
++;
return 0;
error
:
if ( dev
)
kref_put
( &dev
-> kref
, usmc_delete
);
return retval
;
}
static void usmc_disconnect
( struct usb_interface
* interface
)
{
struct usb_usmc
* dev
;
int minor
= interface
-> minor
;
#if HAVE_UNLOCKED_IOCTL
struct mutex fs_mutex
;
mutex_init
(&fs_mutex
);
mutex_lock
(&fs_mutex
);
#else
lock_kernel
();
#endif
dev
= usb_get_intfdata
( interface
);
usb_set_intfdata
( interface
, NULL
);
/* give back our minor */
usb_deregister_dev
( interface
, &usmc_class
);
num_devices
--;
#if HAVE_UNLOCKED_IOCTL
mutex_unlock
(&fs_mutex
);
#else
unlock_kernel
();
#endif
/* decrement our usage count */
kref_put
( &dev
-> kref
, usmc_delete
);
info
( USMC_DEV_NAME
" #%d now disconnected", minor
);
}
/*-----------------------------------------------------
File operations section
-----------------------------------------------------*/
static int usmc_open
( struct inode
* inode
, struct file
* file
)
{
struct usb_usmc
* dev
;
struct usb_interface
* interface
;
int subminor
;
int retval
= 0;
info
( "usmc_open" );
subminor
= iminor
( inode
);
interface
= usb_find_interface
( &usmc_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 usmc_release
( struct inode
* inode
, struct file
* file
)
{
struct usb_usmc
* dev
;
info
( "usmc_release" );
dev
= ( struct usb_usmc
* ) file
-> private_data
;
if ( dev
== NULL
)
return -ENODEV
;
/* decrement the count on our device */
kref_put
( &dev
-> kref
, usmc_delete
);
return 0;
}
static long usmc_ioctl
( struct file
* file
, unsigned int ioctl_num
, unsigned long ioctl_param
)
{
struct usb_usmc
* dev
;
char * user_buf
;
char * kern_buf
;
char * kern_data_buf
;
__u32 dwTimeOut
;
long dwRes
;
int i
;
unsigned int pipe0
;
__u8 bRequestType
;
__u8 bRequest
;
__u16 wValue
;
__u16 wIndex
;
__u16 wLength
;
dev
= ( struct usb_usmc
* ) file
-> private_data
;
user_buf
= ( char * ) ioctl_param
;
kern_buf
= NULL
;
dwTimeOut
= 30000;
dwRes
= USMC_SUCCESS
;
switch ( ioctl_num
)
{
case IOCTL_GET_DESCRIPTOR_CONFIGURATION
:
info
( "usmc_ioctl. ioctl_num: IOCTL_GET_DESCRIPTOR_CONFIGURATION" );
bsp_GetDescriptor
( GET_DESCRIPTOR_CONFIGURATION
,
&bRequestType
,
&bRequest
,
&wValue
,
&wIndex
,
&wLength
);
break;
case IOCTL_GET_DESCRIPTOR_DEVICE
:
info
( "usmc_ioctl. ioctl_num: IOCTL_GET_DESCRIPTOR_DEVICE" );
bsp_GetDescriptor
( IOCTL_GET_DESCRIPTOR_DEVICE
,
&bRequestType
,
&bRequest
,
&wValue
,
&wIndex
,
&wLength
);
break;
case IOCTL_GET_DESCRIPTOR_STRING
:
info
( "usmc_ioctl. ioctl_num: IOCTL_GET_DESCRIPTOR_STRING" );
bsp_GetDescriptor
( IOCTL_GET_DESCRIPTOR_STRING
,
&bRequestType
,
&bRequest
,
&wValue
,
&wIndex
,
&wLength
);
break;
case IOCTL_GET_STATUS_DEVICE
:
info
( "usmc_ioctl. ioctl_num: IOCTL_GET_STATUS_DEVICE" );
bsp_GetStatus
( IOCTL_GET_STATUS_DEVICE
,
&bRequestType
,
&bRequest
,
&wValue
,
&wIndex
,
&wLength
);
break;
case IOCTL_GET_STATUS_ENDPOINT
:
info
( "usmc_ioctl. ioctl_num: IOCTL_GET_STATUS_ENDPOINT" );
bsp_GetStatus
( IOCTL_GET_STATUS_ENDPOINT
,
&bRequestType
,
&bRequest
,
&wValue
,
&wIndex
,
&wLength
);
break;
case IOCTL_GET_STATUS_INTERFACE
:
info
( "usmc_ioctl. ioctl_num: IOCTL_GET_STATUS_INTERFACE" );
bsp_GetStatus
( IOCTL_GET_STATUS_INTERFACE
,
&bRequestType
,
&bRequest
,
&wValue
,
&wIndex
,
&wLength
);
break;
case IOCTL_GET_VERSION
:
info
( "usmc_ioctl. ioctl_num: IOCTL_GET_VERSION" );
bsp_GetVersion
( &bRequestType
,
&bRequest
,
&wValue
,
&wIndex
,
&wLength
);
break;
case IOCTL_GET_SERIAL
:
info
( "usmc_ioctl. ioctl_num: IOCTL_GET_SERIAL" );
bsp_GetSerial
( &bRequestType
,
&bRequest
,
&wValue
,
&wIndex
,
&wLength
);
break;
case IOCTL_GET_ENCODER_STATE
:
info
( "usmc_ioctl. ioctl_num: IOCTL_GET_ENCODER_STATE" );
bsp_GetEncoderState
( &bRequestType
,
&bRequest
,
&wValue
,
&wIndex
,
&wLength
);
break;
case IOCTL_GET_STATE
:
info
( "usmc_ioctl. ioctl_num: IOCTL_GET_STATE" );
bsp_GetState
( &bRequestType
,
&bRequest
,
&wValue
,
&wIndex
,
&wLength
);
break;
case IOCTL_GO_TO
:
info
( "usmc_ioctl. ioctl_num: IOCTL_GO_TO" );
kern_buf
= bsp_GoTo
( user_buf
,
&bRequestType
,
&bRequest
,
&wValue
,
&wIndex
,
&wLength
);
break;
case IOCTL_SET_MODE
:
info
( "usmc_ioctl. ioctl_num: IOCTL_SET_MODE" );
kern_buf
= bsp_SetMode
( user_buf
,
&bRequestType
,
&bRequest
,
&wValue
,
&wIndex
,
&wLength
);
break;
case IOCTL_SET_PARAMETERS
:
info
( "usmc_ioctl. ioctl_num: IOCTL_SET_PARAMETERS" );
kern_buf
= bsp_SetParameters
( user_buf
,
&bRequestType
,
&bRequest
,
&wValue
,
&wIndex
,
&wLength
);
break;
case IOCTL_DOWNLOAD
:
info
( "usmc_ioctl. ioctl_num: IOCTL_DOWNLOAD" );
kern_buf
= bsp_Download
( user_buf
,
&bRequestType
,
&bRequest
,
&wValue
,
&wIndex
,
&wLength
);
break;
case IOCTL_SET_SERIAL
:
info
( "usmc_ioctl. ioctl_num: IOCTL_SET_SERIAL" );
kern_buf
= bsp_SetSerial
( user_buf
,
&bRequestType
,
&bRequest
,
&wValue
,
&wIndex
,
&wLength
);
break;
case IOCTL_SET_CURRENT_POSITION
:
info
( "usmc_ioctl. ioctl_num: IOCTL_SET_CURRENT_POSITION" );
bsp_SetCurrentPosition
( user_buf
,
&bRequestType
,
&bRequest
,
&wValue
,
&wIndex
,
&wLength
);
break;
case IOCTL_STOP_STEP_MOTOR
:
info
( "usmc_ioctl. ioctl_num: IOCTL_STOP_STEP_MOTOR" );
bsp_StopStepMotor
( &bRequestType
,
&bRequest
,
&wValue
,
&wIndex
,
&wLength
);
break;
case IOCTL_EMULATE_BUTTONS
:
info
( "usmc_ioctl. ioctl_num: IOCTL_EMULATE_BUTTONS" );
bsp_EmulateButtons
( user_buf
,
&bRequestType
,
&bRequest
,
&wValue
,
&wIndex
,
&wLength
);
break;
case IOCTL_SAVE_PARAMETERS
:
info
( "usmc_ioctl. ioctl_num: IOCTL_SAVE_PARAMETERS" );
bsp_SaveParameters
( &bRequestType
,
&bRequest
,
&wValue
,
&wIndex
,
&wLength
);
break;
case IOCTL_GET_NOD
:
info
( "usmc_ioctl. ioctl_num: IOCTL_GET_NOD" );
dwRes
= 1;
kern_buf
= user_to_kernel
( user_buf
, 1 );
kern_buf
[0] = num_devices
;
break;
default:
err
( "usmc_ioctl. unknown ioctl_num: %d", ioctl_num
);
dwRes
= -1;
break;
}
if ( dwRes
== USMC_SUCCESS
)
{
if ( ( bRequestType
& USB_DIR_IN
) != USB_DIR_IN
)
{
kern_data_buf
= wLength
> 0 ? kern_buf
+ 4 : NULL
;
pipe0
= usb_sndctrlpipe
( dev
-> udev
, 0 );
} else {
kern_buf
= ( char * ) kzalloc
( ( ssize_t
) wLength
, GFP_KERNEL
);
kern_data_buf
= kern_buf
;
pipe0
= usb_rcvctrlpipe
( dev
-> udev
, 0 );
}
dwRes
= usb_control_msg
( dev
-> udev
,
pipe0
,
bRequest
,
bRequestType
,
wValue
,
wIndex
,
kern_data_buf
,
wLength
,
dwTimeOut
);
if ( ( bRequestType
& USB_DIR_IN
) == USB_DIR_IN
)
{
for ( i
= 0 ; i
< wLength
; i
++ )
put_user
( kern_data_buf
[i
], user_buf
+ i
);
}
} else if ( dwRes
> 0 ) {
for ( i
= 0 ; i
< dwRes
; i
++ )
put_user
( kern_buf
[i
], user_buf
+ i
);
}
kfree
( kern_buf
);
if ( dwRes
< 0 ) {
err
( "usmc_ioctl failed: dwRes: %d", (int) dwRes
);
} else {
info
( "transmitted %d bytes over control pipe0", (int) dwRes
);
}
return dwRes
;
}
/*-----------------------------------------------------
Util functions section
-----------------------------------------------------*/
static void usmc_delete
( struct kref
* kref
)
{
struct usb_usmc
* dev
= to_usmc_dev
( kref
);
usb_put_dev
( dev
-> udev
);
kfree
( dev
);
}