blob: f3519ad154b80564a2fc3c02881df4ca77d9b45d [file] [log] [blame]
Devin Kimba0e9452012-06-21 14:09:34 -07001/*
Devin Kimc68f4162012-09-18 11:48:20 -07002 * File: VibeOSKernelLinuxTime.c
3 *
4 * Description:
5 * Time helper functions for Linux.
6 *
7 * Portions Copyright (c) 2008-2011 Immersion Corporation. All Rights Reserved.
8 *
9 * This file contains Original Code and/or Modifications of Original Code
10 * as defined in and that are subject to the GNU Public License v2 -
11 * (the 'License'). You may not use this file except in compliance with the
12 * License. You should have received a copy of the GNU General Public License
13 * along with this program; if not, write to the Free Software Foundation, Inc.,
14 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or contact
15 * TouchSenseSales@immersion.com.
16 *
17 * The Original Code and all software distributed under the License are
18 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
19 * EXPRESS OR IMPLIED, AND IMMERSION HEREBY DISCLAIMS ALL SUCH WARRANTIES,
20 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, FITNESS
21 * FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. Please see
22 * the License for the specific language governing rights and limitations
23 * under the License.
24 */
Devin Kimba0e9452012-06-21 14:09:34 -070025
26#error "Please read the following statement"
27/*
Devin Kimc68f4162012-09-18 11:48:20 -070028 * Kernel standard software timer is used as an example but another type
29 * of timer (such as HW timer or high-resolution software timer) might be used
30 * to achieve the 5ms required rate.
31 */
Devin Kimba0e9452012-06-21 14:09:34 -070032#error "End of statement"
33
34#if (HZ != 1000)
35#error The Kernel timer is not configured at 1ms. Please update TIMER_INCR to generate a proper 5ms timer.
36#endif
37
38#include <linux/mutex.h>
39
40#define TIMER_INCR 5 /* run timer at 5 jiffies (== 5ms) */
41#define WATCHDOG_TIMEOUT 10 /* 10 timer cycles = 50ms */
42
43/* For compatibility with older Kernels */
44#ifndef DEFINE_SEMAPHORE
45#define DEFINE_SEMAPHORE(name) \
46 struct semaphore name = __SEMAPHORE_INITIALIZER(name, 1)
47#endif
48
49/* Global variables */
50static bool g_bTimerStarted = false;
51static struct timer_list g_timerList;
52static int g_nWatchdogCounter = 0;
53
54DEFINE_SEMAPHORE(g_hMutex);
55
56/* Forward declarations */
57static void VibeOSKernelLinuxStartTimer(void);
58static void VibeOSKernelLinuxStopTimer(void);
59static int VibeOSKernelProcessData(void* data);
60#define VIBEOSKERNELPROCESSDATA
61
62static inline int VibeSemIsLocked(struct semaphore *lock)
63{
64#if ((LINUX_VERSION_CODE & 0xFFFFFF) < KERNEL_VERSION(2,6,27))
65 return atomic_read(&lock->count) != 1;
66#else
67 return (lock->count) != 1;
68#endif
69}
70
71static void tsp_timer_interrupt(unsigned long param)
72{
73 /* Scheduling next timeout value right away */
74 mod_timer(&g_timerList, jiffies + TIMER_INCR);
75
76 if(g_bTimerStarted) {
77 if (VibeSemIsLocked(&g_hMutex))
78 up(&g_hMutex);
79 }
80}
81
82static int VibeOSKernelProcessData(void* data)
83{
84 int i;
85 int nActuatorNotPlaying = 0;
86
87 for (i = 0; i < NUM_ACTUATORS; i++) {
88 actuator_samples_buffer *pCurrentActuatorSample = &(g_SamplesBuffer[i]);
89
90 if (-1 == pCurrentActuatorSample->nIndexPlayingBuffer) {
91 nActuatorNotPlaying++;
92 if ((NUM_ACTUATORS == nActuatorNotPlaying) &&
93 ((++g_nWatchdogCounter) > WATCHDOG_TIMEOUT)) {
94 VibeInt8 cZero[1] = {0};
95
Devin Kimc68f4162012-09-18 11:48:20 -070096 /* Nothing to play for all actuators,
97 * turn off the timer when we reach
98 * the watchdog tick count limit
99 */
Devin Kimba0e9452012-06-21 14:09:34 -0700100 ImmVibeSPI_ForceOut_SetSamples(i, 8, 1, cZero);
101 ImmVibeSPI_ForceOut_AmpDisable(i);
102 VibeOSKernelLinuxStopTimer();
103
104 /* Reset watchdog counter */
105 g_nWatchdogCounter = 0;
106 }
107 } else {
108 /* Play the current buffer */
109 if (VIBE_E_FAIL == ImmVibeSPI_ForceOut_SetSamples(
110 pCurrentActuatorSample->actuatorSamples[(int)pCurrentActuatorSample->nIndexPlayingBuffer].nActuatorIndex,
111 pCurrentActuatorSample->actuatorSamples[(int)pCurrentActuatorSample->nIndexPlayingBuffer].nBitDepth,
112 pCurrentActuatorSample->actuatorSamples[(int)pCurrentActuatorSample->nIndexPlayingBuffer].nBufferSize,
113 pCurrentActuatorSample->actuatorSamples[(int)pCurrentActuatorSample->nIndexPlayingBuffer].dataBuffer)) {
114 /* VIBE_E_FAIL means NAK has been handled.
115 Schedule timer to restart 5 ms from now */
116 mod_timer(&g_timerList, jiffies + TIMER_INCR);
117 }
118
119 pCurrentActuatorSample->nIndexOutputValue += pCurrentActuatorSample->actuatorSamples[(int)pCurrentActuatorSample->nIndexPlayingBuffer].nBufferSize;
120
121 if (pCurrentActuatorSample->nIndexOutputValue >= pCurrentActuatorSample->actuatorSamples[(int)pCurrentActuatorSample->nIndexPlayingBuffer].nBufferSize) {
122 /* Reach the end of the current buffer */
123 pCurrentActuatorSample->actuatorSamples[(int)pCurrentActuatorSample->nIndexPlayingBuffer].nBufferSize = 0;
124
125 /* Switch buffer */
126 (pCurrentActuatorSample->nIndexPlayingBuffer) ^= 1;
127 pCurrentActuatorSample->nIndexOutputValue = 0;
128
129 /* Finished playing, disable amp for actuator (i) */
130 if (g_bStopRequested) {
131 pCurrentActuatorSample->nIndexPlayingBuffer = -1;
132
133 ImmVibeSPI_ForceOut_AmpDisable(i);
134 }
135 }
136 }
137 }
138
139 /* If finished playing, stop timer */
140 if (g_bStopRequested) {
141 VibeOSKernelLinuxStopTimer();
142
143 /* Reset watchdog counter */
144 g_nWatchdogCounter = 0;
145
146 if (VibeSemIsLocked(&g_hMutex))
147 up(&g_hMutex);
148 return 1; /* tell the caller this is the last iteration */
149 }
150
151 return 0;
152}
153
154static void VibeOSKernelLinuxInitTimer(void)
155{
156 /* Initialize a 5ms-timer with VibeOSKernelTimerProc as timer callback */
157 init_timer(&g_timerList);
158 g_timerList.function = tsp_timer_interrupt;
159}
160
161static void VibeOSKernelLinuxStartTimer(void)
162{
163 int i;
164 int res;
165
166 /* Reset watchdog counter */
167 g_nWatchdogCounter = 0;
168
169 if (!g_bTimerStarted) {
170 if (!VibeSemIsLocked(&g_hMutex))
171 res = down_interruptible(&g_hMutex); /* start locked */
172
173 g_bTimerStarted = true;
174
175 /* Start the timer */
176 g_timerList.expires = jiffies + TIMER_INCR;
177 add_timer(&g_timerList);
178
179 /* Don't block the write() function
180 * after the first sample to allow the host sending
181 * the next samples with no delay
182 */
183 for (i = 0; i < NUM_ACTUATORS; i++) {
184 if ((g_SamplesBuffer[i].actuatorSamples[0].nBufferSize) ||
185 (g_SamplesBuffer[i].actuatorSamples[1].nBufferSize)) {
186 g_SamplesBuffer[i].nIndexOutputValue = 0;
187 return;
188 }
189 }
190 }
191
192 if (0 != VibeOSKernelProcessData(NULL))
193 return;
194
195 /*
Devin Kimc68f4162012-09-18 11:48:20 -0700196 * Use interruptible version of down to be safe
197 * (try to not being stuck here
198 * if the mutex is not freed for any reason)
199 */
Devin Kimba0e9452012-06-21 14:09:34 -0700200 res = down_interruptible(&g_hMutex); /* wait for the mutex to be freed by the timer */
201 if (res != 0) {
202 DbgOut((KERN_INFO "VibeOSKernelLinuxStartTimer: down_interruptible interrupted by a signal.\n"));
203 }
204}
205
206static void VibeOSKernelLinuxStopTimer(void)
207{
208 int i;
209
210 if (g_bTimerStarted) {
211 g_bTimerStarted = false;
212
213 /*
Devin Kimc68f4162012-09-18 11:48:20 -0700214 * Stop the timer.
215 * Use del_timer vs. del_timer_sync
216 * del_timer_sync may cause a Kernel "soft lockup"
217 * on multi-CPU platforms
218 * as VibeOSKernelLinuxStopTimer is called
219 * from the timer tick handler.
220 */
Devin Kimba0e9452012-06-21 14:09:34 -0700221 del_timer(&g_timerList);
222 }
223
224 /* Reset samples buffers */
225 for (i = 0; i < NUM_ACTUATORS; i++) {
226 g_SamplesBuffer[i].nIndexPlayingBuffer = -1;
227 g_SamplesBuffer[i].actuatorSamples[0].nBufferSize = 0;
228 g_SamplesBuffer[i].actuatorSamples[1].nBufferSize = 0;
229 }
230 g_bStopRequested = false;
231 g_bIsPlaying = false;
232}
233
234static void VibeOSKernelLinuxTerminateTimer(void)
235{
236 VibeOSKernelLinuxStopTimer();
237 if (VibeSemIsLocked(&g_hMutex))
238 up(&g_hMutex);
239}