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