static void VibeOSKernelLinuxStartTimer(void)
{
	/* Reset watchdog counter */
	g_nWatchdogCounter = 0;
	wake_lock_timeout(&g_tspWakelock, 1*HZ);
	if (!g_bTimerStarted)
	{
		/* (Re-)Initialize the semaphore used with the timer */
		sema_init(&g_hSemaphore, NUM_EXTRA_BUFFERS);

		g_bTimerStarted = true;

		/* Start the timer */
		g_ktTimerPeriod = ktime_set(0, g_nTimerPeriodMs * 1000000);
		hrtimer_start(&g_tspTimer, g_ktTimerPeriod, HRTIMER_MODE_REL);
	}
	else
	{
		int ret = 0;
		/*
		** Use interruptible version of down to be safe
		** (try to not being stuck here if the semaphore is not freed for any reason)
		*/
		ret = down_interruptible(&g_hSemaphore);  /* wait for the semaphore to be freed by the timer */
		if (ret != 0)
		{
			printk(KERN_ERR "VibeOSKernelLinuxStartTimer: down_interruptible interrupted by a signal.\n");
		}
	}
	VibeOSKernelProcessData(NULL);
}
Beispiel #2
0
static int ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg)
#endif
{
#ifdef QA_TEST
    int i;
#endif

    switch (cmd)
    {
        case TSPDRV_STOP_KERNEL_TIMER:
            /* 
            ** As we send one sample ahead of time, we need to finish playing the last sample
            ** before stopping the timer. So we just set a flag here.
            */
            if (true == g_bIsPlaying) g_bStopRequested = true;

#ifdef VIBEOSKERNELPROCESSDATA
            /* Last data processing to disable amp and stop timer */
            VibeOSKernelProcessData(NULL);
#endif

#ifdef QA_TEST
            if (g_nForceLogIndex)
            {
                for (i=0; i<g_nForceLogIndex; i++)
                {
                    printk("<6>%d\t%d\n", g_nTime, g_nForceLog[i]);
                    g_nTime += TIME_INCREMENT;
                }
            }
            g_nTime = 0;
            g_nForceLogIndex = 0;
#endif
            break;

        case TSPDRV_MAGIC_NUMBER:
            file->private_data = (void*)TSPDRV_MAGIC_NUMBER;
            break;

        case TSPDRV_ENABLE_AMP:
            ImmVibeSPI_ForceOut_AmpEnable(arg);
            DbgRecorderReset((arg));
            DbgRecord((arg,";------- TSPDRV_ENABLE_AMP ---------\n"));
            break;

        case TSPDRV_DISABLE_AMP:
            /* Small fix for now to handle proper combination of TSPDRV_STOP_KERNEL_TIMER and TSPDRV_DISABLE_AMP together */
            /* If a stop was requested, ignore the request as the amp will be disabled by the timer proc when it's ready */
            if(!g_bStopRequested)
            {
                ImmVibeSPI_ForceOut_AmpDisable(arg);
            }
            break;

        case TSPDRV_GET_NUM_ACTUATORS:
            return NUM_ACTUATORS;
    }

    return 0;
}
Beispiel #3
0
static void VibeOSKernelLinuxStartTimer(void)
{
    /* Reset watchdog counter */
    g_nWatchdogCounter = 0;

    if (!g_bTimerStarted)
    {
        /* (Re-)Initialize the semaphore used with the timer */
        sema_init(&g_hSemaphore, NUM_EXTRA_BUFFERS);

        g_bTimerStarted = true;

        /* Start the timer */
        g_timerList.expires = jiffies + g_nTimerPeriodMs;
        add_timer(&g_timerList);
    }
    else
    {
        int res;

        /* 
        ** Use interruptible version of down to be safe 
        ** (try to not being stuck here if the semaphore is not freed for any reason)
        */
        res = down_interruptible(&g_hSemaphore);  /* wait for the semaphore to be freed by the timer */
        if (res != 0)
        {
            DbgOut((KERN_INFO "VibeOSKernelLinuxStartTimer: down_interruptible interrupted by a signal.\n"));
        }
    }

    VibeOSKernelProcessData(NULL);
}
Beispiel #4
0
static int ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg)
{
#ifdef QA_TEST
	int i;
#endif

	DbgOut((KERN_INFO "tspdrv: ioctl cmd[0x%x].\n", cmd));

	switch (cmd) {
	case TSPDRV_STOP_KERNEL_TIMER:
		/*
		** As we send one sample ahead of time, we need to finish playing the last sample
		** before stopping the timer. So we just set a flag here.
		*/
		if (true == g_bIsPlaying)
			g_bStopRequested = true;

#ifdef VIBEOSKERNELPROCESSDATA
		/* Last data processing to disable amp and stop timer */
		VibeOSKernelProcessData(NULL);
#endif

#ifdef QA_TEST
		if (g_nForceLogIndex) {
			for (i = 0; i < g_nForceLogIndex; i++) {
				printk(KERN_DEBUG "<6>%d\t%d\n", g_nTime, g_nForceLog[i]);
				g_nTime += TIME_INCREMENT;
			}
		}
		g_nTime = 0;
		g_nForceLogIndex = 0;
#endif
		break;

	case TSPDRV_MAGIC_NUMBER:
		file->private_data = (void *)TSPDRV_MAGIC_NUMBER;
		break;

	case TSPDRV_ENABLE_AMP:
		wake_lock(&vib_wake_lock);
		ImmVibeSPI_ForceOut_AmpEnable(arg);
		DbgRecorderReset((arg));
		DbgRecord((arg, ";------- TSPDRV_ENABLE_AMP ---------\n"));
		break;

	case TSPDRV_DISABLE_AMP:
		if (!g_bStopRequested)
			ImmVibeSPI_ForceOut_AmpDisable(arg);
		wake_unlock(&vib_wake_lock);
		break;

	case TSPDRV_GET_NUM_ACTUATORS:
		return NUM_ACTUATORS;
	}

	return 0;
}
static void VibeOSKernelLinuxStartTimer(void)
{
    /* Reset watchdog counter */
    g_nWatchdogCounter = 0;

    if (!g_bTimerStarted)
    {
        /* (Re-)Initialize the semaphore used with the timer */
        sema_init(&g_hSemaphore, NUM_EXTRA_BUFFERS);

        g_bTimerStarted = true;

        /* Start the timer */
        g_ktTimerPeriod = ktime_set(0, g_nTimerPeriodMs * 1000000);
        hrtimer_start(&g_tspTimer, g_ktTimerPeriod, HRTIMER_MODE_REL);
    }
    else
    {
        int res;
        /*
        ** Use interruptible version of down to be safe
        ** (try to not being stuck here if the semaphore is not freed for any reason)
        */
        res = down_interruptible(&g_hSemaphore);  /* wait for the semaphore to be freed by the timer */
        if (res != 0)
        {
            DbgOut((DBL_INFO, "VibeOSKernelLinuxStartTimer: down_interruptible interrupted by a signal.\n"));
        }
    }
    VibeOSKernelProcessData(NULL);
    /*
    ** Because of possible NACK handling, the  VibeOSKernelProcessData() call above could take more than
    ** 5 ms on some piezo devices that are buffering output samples; when this happens, the timer
    ** interrupt will release the g_hSemaphore while VibeOSKernelProcessData is executing and the player
    ** will immediately send the new packet to the SPI layer when VibeOSKernelProcessData exits, which
    ** could cause another NACK right away. To avoid that, we'll create a small delay if the semaphore
    ** was released when VibeOSKernelProcessData exits, by acquiring the mutex again and waiting for
    ** the timer to release it.
    */
#if defined(NUM_EXTRA_BUFFERS) && (NUM_EXTRA_BUFFERS)
    if (g_bTimerStarted && !VibeSemIsLocked(&g_hSemaphore))
    {
        int res;

        res = down_interruptible(&g_hSemaphore);

        if (res != 0)
        {
            DbgOut((DBL_INFO, "VibeOSKernelLinuxStartTimer: down_interruptible interrupted by a signal.\n"));
        }
    }
#endif
}
Beispiel #6
0
static void VibeOSKernelLinuxStartTimer(void)
{
	int i;
	int res;

	/* Reset watchdog counter */
	g_nwatchdog_counter = 0;

	if (!g_btimerstarted) {
		if (!VibeSemIsLocked(&g_mutex))
			res = down_interruptible(&g_mutex); /* start locked */

		g_btimerstarted = true;

		/* Start the timer */
		hrtimer_start(&g_tsptimer, g_ktfivems, HRTIMER_MODE_REL);

		/* Don't block the write() function after the first sample
		to allow the host sending the next samples with no delay */
		for (i = 0; i < NUM_ACTUATORS; i++) {
			if ((g_samples_buffer[i]
				.actuator_samples[0].nbuffer_size) ||
				(g_samples_buffer[i]
				.actuator_samples[1].nbuffer_size)) {
				g_samples_buffer[i].nindex_output_value = 0;
				return;
			}
		}
	}

	if (0 != VibeOSKernelProcessData(NULL))
		return;

	/*
	** Use interruptible version of down to be safe
	** (try to not being stuck here if the mutex is
	** not freed for any reason)
	*/
	/* wait for the mutex to be freed by the timer */
	res = down_interruptible(&g_mutex);
	if (res != 0)
		DbgOut((KERN_INFO
		 "tspdrv: down_interruptible interrupted by a signal.\n"));
}
static void VibeOSKernelLinuxStartTimer(void)
{
    int i;
    int res;

    /* Reset watchdog counter */
    g_nWatchdogCounter = 0;

    if (!g_bTimerStarted)
    {
        if (!VibeSemIsLocked(&g_hMutex)) res = down_interruptible(&g_hMutex); /* start locked */

        g_bTimerStarted = true;

        /* Start the timer */
        g_timerList.expires = jiffies + TIMER_INCR;
        add_timer(&g_timerList);

        /* Don't block the write() function after the first sample to allow the host sending the next samples with no delay */
        for (i = 0; i < NUM_ACTUATORS; i++)
        {
            if ((g_SamplesBuffer[i].actuatorSamples[0].nBufferSize) || (g_SamplesBuffer[i].actuatorSamples[1].nBufferSize))
            {
                g_SamplesBuffer[i].nIndexOutputValue = 0;
                return;
            }
        }
    }

    if (0 != VibeOSKernelProcessData(NULL)) return;

    /*
    ** Use interruptible version of down to be safe
    ** (try to not being stuck here if the mutex is not freed for any reason)
    */
    res = down_interruptible(&g_hMutex);  /* wait for the mutex to be freed by the timer */
    if (res != 0)
    {
        DbgOut((KERN_INFO "VibeOSKernelLinuxStartTimer: down_interruptible interrupted by a signal.\n"));
    }
}
static long ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
{
#ifdef QA_TEST
	int i;
#endif

	/* DbgOut(KERN_INFO "tspdrv: ioctl cmd[0x%x].\n", cmd); */
	switch (cmd) {
	case TSPDRV_STOP_KERNEL_TIMER:
		/*
		** As we send one sample ahead of time, we need to finish
		** playing the last sample before stopping the timer.
		** So we just set a flag here.
		*/
		if (true == g_bisplaying)
			g_bstoprequested = true;

#ifdef VIBEOSKERNELPROCESSDATA
		/* Last data processing to disable amp and stop timer */
		VibeOSKernelProcessData(NULL);
#endif

#ifdef QA_TEST
		if (g_nforcelog_index) {
			for (i = 0; i < g_nforcelog_index; i++) {
				printk(KERN_INFO "%d\t%d\n"
					   , g_ntime, g_nforcelog[i]);
				g_ntime += TIME_INCREMENT;
			}
		}
		g_ntime = 0;
		g_nforcelog_index = 0;
#endif
		break;

	case TSPDRV_MAGIC_NUMBER:
		filp->private_data = (void *)TSPDRV_MAGIC_NUMBER;
		break;

	case TSPDRV_ENABLE_AMP:
		wake_lock(&vib_wake_lock);
		ImmVibeSPI_ForceOut_AmpEnable(arg);
		DbgRecorderReset((arg));
		DbgRecord((arg, ";------- TSPDRV_ENABLE_AMP ---------\n"));
		break;

	case TSPDRV_DISABLE_AMP:
		/*
		** Small fix for now to handle proper combination of
		** TSPDRV_STOP_KERNEL_TIMER and TSPDRV_DISABLE_AMP together
		** If a stop was requested, ignore the request as the amp
		** will be disabled by the timer proc when it's ready
		*/
		if (!g_bstoprequested)
			ImmVibeSPI_ForceOut_AmpDisable(arg);
		wake_unlock(&vib_wake_lock);
		break;

	case TSPDRV_GET_NUM_ACTUATORS:
		return NUM_ACTUATORS;
	}

	return 0;
}
static ssize_t write(struct file *file, const char *buf, size_t count,
					 loff_t *ppos)
{
	int i = 0;

	*ppos = 0;  /* file position not used, always set to 0 */
	/* DbgOut((KERN_ERR "tspdrv: write....\n")); */

	/*
	** Prevent unauthorized caller to write data.
	** TouchSense service is the only valid caller.
	*/
	if (file->private_data != (void *)TSPDRV_MAGIC_NUMBER) {
		DbgOut((KERN_ERR "tspdrv: unauthorized write.\n"));
		return 0;
	}
#ifdef CONFIG_TACTILE_ASSIST
	/* Check buffer size */
	if ((count < SPI_HEADER_SIZE) || (count > SPI_BUFFER_SIZE)) {
		DbgOut((KERN_ERR "tspdrv: invalid write buffer size.\n"));
		return 0;
	}
	if (count == SPI_HEADER_SIZE)
		g_bOutputDataBufferEmpty = 1;
	else
		g_bOutputDataBufferEmpty = 0;

#else
	if ((count <= SPI_HEADER_SIZE) || (count > SPI_BUFFER_SIZE)) {
		DbgOut((KERN_ERR "tspdrv: invalid write buffer size.\n"));
		return 0;
	}
#endif

	/* Copy immediately the input buffer */
	if (0 != copy_from_user(g_cwrite_buffer, buf, count)) {
		/* Failed to copy all the data, exit */
		DbgOut((KERN_ERR "tspdrv: copy_from_user failed.\n"));
		return 0;
	}

	while (i < count) {
		int nindex_free_buffer;   /* initialized below */

		samples_buffer *pinput_buffer =
			(samples_buffer *)(&g_cwrite_buffer[i]);

#ifdef CONFIG_TACTILE_ASSIST
		if ((i + SPI_HEADER_SIZE) > count) {
#else
		if ((i + SPI_HEADER_SIZE) >= count) {
#endif
			/*
			** Index is about to go beyond the buffer size.
			** (Should never happen).
			*/
			DbgOut((KERN_EMERG "tspdrv: invalid buffer index.\n"));
			return 0;
		}

		/* Check bit depth */
		if (8 != pinput_buffer->nbit_depth)
			DbgOut((KERN_WARNING
			"tspdrv: invalid bit depth.Use default value(8).\n"));

		/* The above code not valid if SPI header size is not 3 */
#if (SPI_HEADER_SIZE != 3)
#error "SPI_HEADER_SIZE expected to be 3"
#endif

		/* Check buffer size */
		if ((i + SPI_HEADER_SIZE + pinput_buffer->nbuffer_size)
			> count) {
			/*
			** Index is about to go beyond the buffer size.
			** (Should never happen).
			*/
			DbgOut((KERN_EMERG "tspdrv: invalid data size.\n"));
			return 0;
		}

		/* Check actuator index */
		if (NUM_ACTUATORS <= pinput_buffer->nactuator_index) {
			DbgOut((KERN_ERR "tspdrv: invalid actuator index.\n"));
			i += (SPI_HEADER_SIZE + pinput_buffer->nbuffer_size);
			continue;
		}

		if (0 == g_samples_buffer[pinput_buffer->nactuator_index]
			.actuator_samples[0].nbuffer_size) {
			nindex_free_buffer = 0;
		} else if (0 == g_samples_buffer[pinput_buffer->nactuator_index]
			.actuator_samples[1].nbuffer_size) {
			nindex_free_buffer = 1;
		} else {
			/* No room to store new samples  */
			DbgOut((KERN_ERR
			 "tspdrv: no room to store new samples.\n"));
			return 0;
		}

		/* Store the data in the free buffer of the given actuator */
		memcpy(
			   &(g_samples_buffer[pinput_buffer->nactuator_index]
			   .actuator_samples[nindex_free_buffer]),
			   &g_cwrite_buffer[i],
			   (SPI_HEADER_SIZE + pinput_buffer->nbuffer_size));

		/*  If the no buffer is playing, prepare to play
		 ** g_samples_buffer[pinput_buffer->nactuator_index].
		 ** actuator_samples[nindex_free_buffer]
		 */
		if (-1 == g_samples_buffer[pinput_buffer->nactuator_index]
			.nindex_playing_buffer) {
			g_samples_buffer[pinput_buffer->nactuator_index]
				.nindex_playing_buffer = nindex_free_buffer;
			g_samples_buffer[pinput_buffer->nactuator_index]
				.nindex_output_value = 0;
		}

		/* Increment buffer index */
		i += (SPI_HEADER_SIZE + pinput_buffer->nbuffer_size);
	}

#ifdef QA_TEST
	g_nforcelog[g_nforcelog_index++] = g_cSPIBuffer[0];
	if (g_nforcelog_index >= FORCE_LOG_BUFFER_SIZE) {
		for (i = 0; i < FORCE_LOG_BUFFER_SIZE; i++) {
			printk(KERN_INFO "%d\t%d\n", g_ntime, g_nforcelog[i]);
			g_ntime += TIME_INCREMENT;
		}
		g_nforcelog_index = 0;
	}
#endif

	/* Start the timer after receiving new output force */
	g_bisplaying = true;
	VibeOSKernelLinuxStartTimer();

	return count;
}

static long ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
{
#ifdef QA_TEST
	int i;
#endif
	printk(KERN_DEBUG "tspdrv: %s %d\n", __func__, cmd);
	/* DbgOut(KERN_INFO "tspdrv: ioctl cmd[0x%x].\n", cmd); */
	switch (cmd) {
	case TSPDRV_STOP_KERNEL_TIMER:
		/*
		** As we send one sample ahead of time, we need to finish
		** playing the last sample before stopping the timer.
		** So we just set a flag here.
		*/
		if (true == g_bisplaying)
			g_bstoprequested = true;

#ifdef VIBEOSKERNELPROCESSDATA
		/* Last data processing to disable amp and stop timer */
		VibeOSKernelProcessData(NULL);
#endif

#ifdef QA_TEST
		if (g_nforcelog_index) {
			for (i = 0; i < g_nforcelog_index; i++) {
				printk(KERN_INFO "%d\t%d\n"
					   , g_ntime, g_nforcelog[i]);
				g_ntime += TIME_INCREMENT;
			}
		}
		g_ntime = 0;
		g_nforcelog_index = 0;
#endif
		break;

	case TSPDRV_MAGIC_NUMBER:
#ifdef CONFIG_TACTILE_ASSIST
	case TSPDRV_SET_MAGIC_NUMBER:
#endif
		filp->private_data = (void *)TSPDRV_MAGIC_NUMBER;
		break;

	case TSPDRV_ENABLE_AMP:
		wake_lock(&vib_wake_lock);
		vibe_set_pwm_freq(0);
		vibe_pwm_onoff(1);
		ImmVibeSPI_ForceOut_AmpEnable(arg);
		DbgRecorderReset((arg));
		DbgRecord((arg, ";------- TSPDRV_ENABLE_AMP ---------\n"));
		break;

	case TSPDRV_DISABLE_AMP:
		/*
		** Small fix for now to handle proper combination of
		** TSPDRV_STOP_KERNEL_TIMER and TSPDRV_DISABLE_AMP together
		** If a stop was requested, ignore the request as the amp
		** will be disabled by the timer proc when it's ready
		*/
#ifdef CONFIG_TACTILE_ASSIST
		g_bstoprequested = true;
		/* Last data processing to disable amp and stop timer */
		VibeOSKernelProcessData(NULL);
		g_bisplaying = false;
#else
		if (!g_bstoprequested)
			ImmVibeSPI_ForceOut_AmpDisable(arg);
#endif
		wake_unlock(&vib_wake_lock);
		break;

	case TSPDRV_GET_NUM_ACTUATORS:
		return NUM_ACTUATORS;
	}

	return 0;
}
Beispiel #10
0
static long unlocked_ioctl(struct file *file, unsigned int cmd,
	unsigned long arg)
{
#ifdef QA_TEST
	int i;
#endif

	DbgOut((KERN_INFO "tspdrv: ioctl cmd[0x%x].\n", cmd));

	switch (cmd) {
	case TSPDRV_STOP_KERNEL_TIMER:
		/*
		  * As we send one sample ahead of time,
		  * we need to finish playing the last sample
		  * before stopping the timer. So we just set a flag here.
		  */
		if (true == g_bIsPlaying)
			g_bStopRequested = true;

#ifdef VIBEOSKERNELPROCESSDATA
		/* Last data processing to disable amp and stop timer */
		VibeOSKernelProcessData(NULL);
#endif

#ifdef QA_TEST
		if (g_nForceLogIndex) {
			for (i = 0; i < g_nForceLogIndex; i++) {
				printk(KERN_DEBUG "<6>%d\t%d\n",
					g_nTime, g_nForceLog[i]);
				g_nTime += TIME_INCREMENT;
			}
		}
		g_nTime = 0;
		g_nForceLogIndex = 0;
#endif
		break;

	case TSPDRV_MAGIC_NUMBER:
	case TSPDRV_SET_MAGIC_NUMBER:
		file->private_data = (void *)TSPDRV_MAGIC_NUMBER;
		break;

	case TSPDRV_ENABLE_AMP:
		wake_lock(&vib_wake_lock);
		ImmVibeSPI_ForceOut_AmpEnable(arg);
		DbgRecorderReset((arg));
		DbgRecord((arg, ";------- TSPDRV_ENABLE_AMP ---------\n"));
		break;

	case TSPDRV_DISABLE_AMP:
		/* Small fix for now to handle proper combination of
		  * TSPDRV_STOP_KERNEL_TIMER and TSPDRV_DISABLE_AMP together
		  * If a stop was requested, ignore the request as the amp
		  * will be disabled by the timer proc when it's ready
		  */
#if 0
		if (!g_bStopRequested) {
			ImmVibeSPI_ForceOut_AmpDisable(arg);
#endif
		g_bStopRequested = true;
		/* Last data processing to disable amp and stop timer */
		VibeOSKernelProcessData(NULL);
		g_bIsPlaying = false;
		wake_unlock(&vib_wake_lock);

		break;

	case TSPDRV_GET_NUM_ACTUATORS:
		return NUM_ACTUATORS;
	}

	return 0;
}

static int suspend(struct platform_device *pdev, pm_message_t state)
{
	int ret;

	if (g_bIsPlaying) {
		ret = -EBUSY;
	} else {
		ret = 0;
	}

	DbgOut((KERN_DEBUG "tspdrv: %s (%d).\n", __func__, ret));
	return ret;
}
//static int ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg)
static long unlocked_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{
#ifdef QA_TEST
    int i;
#endif

	DbgOut((KERN_INFO "tspdrv: ioctl cmd[0x%x].\n", cmd));

    switch (cmd)
    {
#if 0 // We don't used linear motor.     
#ifdef VIBE_TUNING
#if 1 //def CONFIG_KTTECH_MODEL_O6
             case TSPDRV_TUNING_ARG1: //M     // 0x52b9 
                    printk("[tspdrv] TSPDRV_TUNING_ARG1 arg : %lu\n", arg);
                    g_nLRA_PWM_M = arg;
                    break;
             case TSPDRV_TUNING_ARG2: //N & D  // ox52ba 
                    printk("[tspdrv] TSPDRV_TUNING_ARG2 arg : %lu\n", arg);
                    g_nLRA_PWM_N = arg;
                    g_nLRA_PWM_D= arg >> 1;
                    break;
             case TSPDRV_TUNING_ARG3: //Multiplier  // 0x52bb 
                    printk("[tspdrv] TSPDRV_TUNING_ARG2 arg : %lu\n", arg);
                    g_nLRA_PWM_Multiplier = arg;
                    break;
#endif					
#ifdef CONFIG_KTTECH_MODEL_O3
        case TSPDRV_TUNING_ARG1:
        	g_nLRA_PWM_M = arg; /* set value of g_PWM_duty_max in ImmVibeSPI */
	DbgOut((KERN_INFO "tspdrv: ioctl cmd[185].\n"));
        	break;
        case TSPDRV_TUNING_ARG2:
        	g_nLRA_PWM_N = arg; /* set value of g_PWM_ctrl in ImmVibeSPI */
        	g_nLRA_PWM_D = g_nLRA_PWM_N>>1; //D = N/2
	DbgOut((KERN_INFO "tspdrv: ioctl cmd[186].\n"));
        	break;
        case TSPDRV_TUNING_ARG3:
        	g_nLRA_PWM_Multiplier = arg; /* set value of g_PWM_multiplier in ImmVibeSPI */
	DbgOut((KERN_INFO "tspdrv: ioctl cmd[187].\n"));
        	break;
#endif			
#endif /* VIBE_TUNING */
#endif 
        case TSPDRV_STOP_KERNEL_TIMER:   // 0x5201
            /* 
            ** As we send one sample ahead of time, we need to finish playing the last sample
            ** before stopping the timer. So we just set a flag here.
            */
            if (true == g_bIsPlaying) g_bStopRequested = true;

#ifdef VIBEOSKERNELPROCESSDATA
            /* Last data processing to disable amp and stop timer */
            VibeOSKernelProcessData(NULL);
#endif

#ifdef QA_TEST
            if (g_nForceLogIndex)
            {
                for (i=0; i<g_nForceLogIndex; i++)
                {
                    printk("<6>%d\t%d\n", g_nTime, g_nForceLog[i]);
                    g_nTime += TIME_INCREMENT;
                }
            }
            g_nTime = 0;
            g_nForceLogIndex = 0;
#endif
            break;

        case TSPDRV_MAGIC_NUMBER:
            file->private_data = (void*)TSPDRV_MAGIC_NUMBER;
            break;

        case TSPDRV_ENABLE_AMP:   // 0x5203 
#ifdef CONFIG_SPIDER_SADR
            sadr_device_set_data(vib_dev_id,0xFFFFFFFF);
#else
            ImmVibeSPI_ForceOut_AmpEnable(arg);
#endif
            DbgRecorderReset((arg));
            DbgRecord((arg,";------- TSPDRV_ENABLE_AMP ---------\n"));
            break;

        case TSPDRV_DISABLE_AMP:   // 0x5204 
            /* Small fix for now to handle proper combination of TSPDRV_STOP_KERNEL_TIMER and TSPDRV_DISABLE_AMP together */
            /* If a stop was requested, ignore the request as the amp will be disabled by the timer proc when it's ready */
            if(!g_bStopRequested)
            {
#ifdef CONFIG_SPIDER_SADR
                sadr_device_set_data(vib_dev_id,0);
#else
                ImmVibeSPI_ForceOut_AmpDisable(arg);
#endif
            }
            break;

        case TSPDRV_GET_NUM_ACTUATORS:  // 0x5205 
            return NUM_ACTUATORS;
    }

    return 0;
}