blob: b84aee346035b39e468849168a34d047daaea4eb [file] [log] [blame]
Devin Kimba0e9452012-06-21 14:09:34 -07001/*
2** =========================================================================
3** File:
4** VibeOSKernelLinuxHRTime.c
5**
6** Description:
7** High Resolution Time helper functions for Linux.
8**
9** Portions Copyright (c) 2010-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/*
30** Kernel high-resolution software timer is used as an example but another type
31** of timer (such as HW timer or standard software timer) might be used to achieve
32** the 5ms required rate.
33*/
34
35#ifndef CONFIG_HIGH_RES_TIMERS
36#warning "The Kernel does not have high resolution timers enabled. Either provide a non hr-timer implementation of VibeOSKernelLinuxTime.c or re-compile your kernel with CONFIG_HIGH_RES_TIMERS=y"
37#endif
38
39#include <linux/hrtimer.h>
40#include <linux/mutex.h>
41
42#define WATCHDOG_TIMEOUT 10 /* 10 timer cycles = 50ms */
43
44/* For compatibility with older Kernels */
45#ifndef DEFINE_SEMAPHORE
46#define DEFINE_SEMAPHORE(name) \
47 struct semaphore name = __SEMAPHORE_INITIALIZER(name, 1)
48#endif
49
50/* Global variables */
51static bool g_bTimerStarted = false;
52static struct hrtimer g_tspTimer;
53static ktime_t g_ktFiveMs;
54static int g_nWatchdogCounter = 0;
55
56DEFINE_SEMAPHORE(g_hMutex);
57
58/* Forward declarations */
59static void VibeOSKernelLinuxStartTimer(void);
60static void VibeOSKernelLinuxStopTimer(void);
61static int VibeOSKernelProcessData(void* data);
62#define VIBEOSKERNELPROCESSDATA
63
64static inline int VibeSemIsLocked(struct semaphore *lock)
65{
66#if ((LINUX_VERSION_CODE & 0xFFFFFF) < KERNEL_VERSION(2,6,27))
67 return atomic_read(&lock->count) != 1;
68#else
69 return (lock->count) != 1;
70#endif
71}
72
73static enum hrtimer_restart tsp_timer_interrupt(struct hrtimer *timer)
74{
75 /* Scheduling next timeout value right away */
76 hrtimer_forward_now(timer, g_ktFiveMs);
77
78 if(g_bTimerStarted) {
79 if (VibeSemIsLocked(&g_hMutex))
80 up(&g_hMutex);
81 }
82
83 return HRTIMER_RESTART;
84}
85
86static int VibeOSKernelProcessData(void* data)
87{
88 int i;
89 int nActuatorNotPlaying = 0;
90
91 for (i = 0; i < NUM_ACTUATORS; i++) {
92 actuator_samples_buffer *pCurrentActuatorSample = &(g_SamplesBuffer[i]);
93
94 if (-1 == pCurrentActuatorSample->nIndexPlayingBuffer) {
95 nActuatorNotPlaying++;
96 if ((NUM_ACTUATORS == nActuatorNotPlaying) &&
97 ((++g_nWatchdogCounter) > WATCHDOG_TIMEOUT)) {
98 VibeInt8 cZero[1] = {0};
99
100 /* Nothing to play for all actuators,
101 * turn off the timer when we reach
102 * the watchdog tick count limit
103 */
104 ImmVibeSPI_ForceOut_SetSamples(i, 8, 1, cZero);
105 ImmVibeSPI_ForceOut_AmpDisable(i);
106 VibeOSKernelLinuxStopTimer();
107
108 /* Reset watchdog counter */
109 g_nWatchdogCounter = 0;
110 }
111 }
112 else {
113 /* Play the current buffer */
114 if (VIBE_E_FAIL == ImmVibeSPI_ForceOut_SetSamples(
115 pCurrentActuatorSample-> actuatorSamples[(int)pCurrentActuatorSample->nIndexPlayingBuffer].nActuatorIndex,
116 pCurrentActuatorSample->actuatorSamples[(int)pCurrentActuatorSample->nIndexPlayingBuffer].nBitDepth,
117 pCurrentActuatorSample->actuatorSamples[(int)pCurrentActuatorSample->nIndexPlayingBuffer].nBufferSize,
118 pCurrentActuatorSample->actuatorSamples[(int)pCurrentActuatorSample->nIndexPlayingBuffer].dataBuffer)) {
119 /* VIBE_E_FAIL means NAK has been handled.
120 * Schedule timer to restart 5 ms from now
121 */
122 hrtimer_forward_now(&g_tspTimer, g_ktFiveMs);
123 }
124
125 pCurrentActuatorSample->nIndexOutputValue += pCurrentActuatorSample->actuatorSamples[(int)pCurrentActuatorSample->nIndexPlayingBuffer].nBufferSize;
126
127 if (pCurrentActuatorSample->nIndexOutputValue >= pCurrentActuatorSample->actuatorSamples[(int)pCurrentActuatorSample->nIndexPlayingBuffer].nBufferSize) {
128 /* Reach the end of the current buffer */
129 pCurrentActuatorSample->actuatorSamples[(int)pCurrentActuatorSample->nIndexPlayingBuffer].nBufferSize = 0;
130
131 /* Switch buffer */
132 (pCurrentActuatorSample->nIndexPlayingBuffer) ^= 1;
133 pCurrentActuatorSample->nIndexOutputValue = 0;
134
135 /* Finished playing, disable amp for actuator (i) */
136 if (g_bStopRequested) {
137 pCurrentActuatorSample->nIndexPlayingBuffer = -1;
138
139 ImmVibeSPI_ForceOut_AmpDisable(i);
140 }
141 }
142 }
143 }
144
145 /* If finished playing, stop timer */
146 if (g_bStopRequested) {
147 VibeOSKernelLinuxStopTimer();
148
149 /* Reset watchdog counter */
150 g_nWatchdogCounter = 0;
151
152 if (VibeSemIsLocked(&g_hMutex))
153 up(&g_hMutex);
154 return 1; /* tell the caller this is the last iteration */
155 }
156
157 return 0;
158}
159
160static void VibeOSKernelLinuxInitTimer(void)
161{
162 /* Get a 5,000,000ns = 5ms time value */
163 g_ktFiveMs = ktime_set(0, 5000000);
164
165 hrtimer_init(&g_tspTimer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
166
167 /* Initialize a 5ms-timer with tsp_timer_interrupt as timer callback
168 (interrupt driven)*/
169 g_tspTimer.function = tsp_timer_interrupt;
170}
171
172static void VibeOSKernelLinuxStartTimer(void)
173{
174 int i;
175 int res;
176
177 /* Reset watchdog counter */
178 g_nWatchdogCounter = 0;
179
180 if (!g_bTimerStarted) {
181 if (!VibeSemIsLocked(&g_hMutex))
182 res = down_interruptible(&g_hMutex); /* start locked */
183
184 g_bTimerStarted = true;
185
186 /* Start the timer */
187 hrtimer_start(&g_tspTimer, g_ktFiveMs, HRTIMER_MODE_REL);
188
189 /* Don't block the write() function
190 * after the first sample to allow the host sending
191 * the next samples with no delay
192 */
193 for (i = 0; i < NUM_ACTUATORS; i++) {
194 if ((g_SamplesBuffer[i].actuatorSamples[0].nBufferSize) || (g_SamplesBuffer[i].actuatorSamples[1].nBufferSize)) {
195 g_SamplesBuffer[i].nIndexOutputValue = 0;
196 return;
197 }
198 }
199 }
200
201 if (0 != VibeOSKernelProcessData(NULL))
202 return;
203
204 /*
205 ** Use interruptible version of down to be safe
206 ** (try to not being stuck here if the mutex is not freed for any reason)
207 */
208 /* wait for the mutex to be freed by the timer */
209 res = down_interruptible(&g_hMutex);
210 if (res != 0) {
211 DbgOut((KERN_INFO "VibeOSKernelLinuxStartTimer: down_interruptible interrupted by a signal.\n"));
212 }
213}
214
215static void VibeOSKernelLinuxStopTimer(void)
216{
217 int i;
218
219 if (g_bTimerStarted) {
220 g_bTimerStarted = false;
221 hrtimer_cancel(&g_tspTimer);
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}