/************************************************************************\ 
 
##                                                                      ## 
 
##  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;
 
 
 
        }
 
 
 
        dev = usb_get_intfdata ( interface );
 
 
 
        if ( !dev )
 
        {
 
                retval = -ENODEV;
 
        }
 
 
 
        /* increment our usage count for the device */
 
        kref_get ( &dev -> kref );
 
 
 
        /* save our object in the file's private structure */
 
        file -> private_data = dev;
 
        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 );
 
}