Subversion Repositories f9daq

Rev

Blame | Last modification | View Log | RSS feed

/************************************************************************\
##                                                                      ##
##  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 );
}