1 /*---------------------------------------------------------------------------*
2 Project: TwlSDK - CTRDG - libraries - ARM9
3 File: ctrdg_vib.c
4
5 Copyright 2008 Nintendo. All rights reserved.
6
7 These coded instructions, statements, and computer programs contain
8 proprietary information of Nintendo of America Inc. and/or Nintendo
9 Company Ltd., and are protected by Federal copyright law. They may
10 not be disclosed to third parties or copied or duplicated in any form,
11 in whole or in part, without the prior written consent of Nintendo.
12
13 $Date:: 2007-11-15#$
14 $Rev: 2414 $
15 $Author: hatamoto_minoru $
16 *---------------------------------------------------------------------------*/
17
18 #include <nitro.h>
19
20 /*-----------------------------------------------------------------------*
21 Type, Constant
22 *-----------------------------------------------------------------------*/
23 #ifdef SDK_FINALROM
24 #define VIBi_FatalError(...) (void)0;
25 #else
26 #define VIBi_FatalError(...) OS_Panic(__VA_ARGS__)
27 #endif
28
29 #define VIBi_INTR_DELAY_TICK (19)
30
31 #define VIBi_BITID (2)
32
33 typedef struct
34 {
35 u32 current_pos; /* Current position in pulse set */
36 u32 rest_pos; /* Position that is the pause time. */
37 u32 rest_tick; /* Length of the pause time. 1 = 1 Tick */
38 u32 on_tick[VIB_PULSE_NUM_MAX]; /* Length of the activation time. 1 = 1 Tick */
39 u32 off_tick[VIB_PULSE_NUM_MAX];/* Length of the stop time. 1 = 1 Tick */
40 BOOL is_enable; /* TRUE if oscillating. */
41 u32 repeat_num; /* Number of times to repeat pulse set. When 0, repeats endlessly. */
42 u32 current_count; /* Indicates the number of times that the pulse set was repeated. */
43 VIBCartridgePulloutCallback cartridge_pullout_callback; /* Cartridge removal callback */
44 }
45 VIBiPulseInfo;
46
47 /*-----------------------------------------------------------------------*
48 Function prototypes
49 *-----------------------------------------------------------------------*/
50
51 //--- Auto Function Prototype --- Don't comment here.
52 BOOL VIB_Init(void);
53 void VIB_End(void);
54 void VIB_StartPulse(const VIBPulseState * state);
55 void VIB_StopPulse(void);
56 BOOL VIB_IsExecuting(void);
57 void VIB_SetCartridgePulloutCallback(VIBCartridgePulloutCallback func);
58 BOOL VIB_IsCartridgeEnabled(void);
59 static inline u32 VIBi_PulseTimeToTicks(u32 pulse_time);
60 static BOOL VIBi_PulledOutCallbackCartridge(void);
61 static void VIBi_MotorOnOff(VIBiPulseInfo * pulse_vib);
62 static void VIBi_SleepCallback(void *);
63 //--- End of Auto Function Prototype
64
65 /*-----------------------------------------------------------------------*
66 Variables
67 *-----------------------------------------------------------------------*/
68
69 /* The cache is accessed in 32-byte units, so align the front. */
70 static VIBiPulseInfo pulse_vib ATTRIBUTE_ALIGN(32);
71 static PMSleepCallbackInfo sc_info;
72
73 /*-----------------------------------------------------------------------*
74 Global Function Definitions
75 *-----------------------------------------------------------------------*/
76
77 /*!
78 Initializes pulse rumble. \n
79 When called twice, it is equivalent to VIB_IsCartridgeEnabled.
80
81 Inside this function, before entering sleep mode, the callback that stops vibration is registered using NitroSDK's PM_AppendPreSleepCallback function.
82
83
84 @retval TRUE: Initialization succeeded.
85 @retval FALSE: Initialization failed.
86 */
VIB_Init(void)87 BOOL VIB_Init(void)
88 {
89
90 static BOOL is_initialized;
91
92 if (is_initialized)
93 {
94 return VIB_IsCartridgeEnabled();
95 }
96 is_initialized = TRUE;
97
98 if (CTRDGi_IsBitIDAtInit(VIBi_BITID))
99 {
100 MI_CpuClearFast(&pulse_vib, sizeof(pulse_vib));
101 CTRDG_SetPulledOutCallback(VIBi_PulledOutCallbackCartridge);
102
103 /* Register the callback used before entering sleep. */
104 PM_SetSleepCallbackInfo(&sc_info, VIBi_SleepCallback, NULL);
105 PM_AppendPreSleepCallback(&sc_info);
106
107 return TRUE;
108 }
109 else
110 {
111 return FALSE;
112 }
113 }
114
115 /*!
116 Stop usage of the Rumble Pak library.
117
118 Stop the pulse rumble, delete the pre-sleep callback registered with the VIB_Init function, and delete the cartridge-removal callback.
119
120
121
122 */
VIB_End(void)123 void VIB_End(void)
124 {
125
126 VIB_StopPulse();
127 PM_DeletePreSleepCallback(&sc_info);
128 CTRDG_SetPulledOutCallback(NULL);
129 }
130
131 /*!
132 Starts the pulse rumble. \n
133 If the previous pulse rumble has not been completed, this lets it complete and then starts. \n
134 Because the status is copied by the library, there is no need to allocate memory.
135
136 Check the hardware restrictions before rumble starts.
137 If the restrictions are violated and this is a debug or release build, display a message using the OS_Panic function, and halt the program.
138 If this is a final ROM build, pulse rumble is not started.
139
140 @sa VIBPulseState
141
142 @param state: Pulse rumble status.
143 */
VIB_StartPulse(const VIBPulseState * state)144 void VIB_StartPulse(const VIBPulseState * state)
145 {
146
147 if (!VIB_IsCartridgeEnabled())
148 return;
149
150 VIB_StopPulse();
151 {
152 int i;
153
154 /* Check the ON time */
155 for (i = 0; i < state->pulse_num; i++)
156 {
157 /* Whether or not the ON time is 0 */
158 if (state->on_time[i] == 0)
159 {
160 VIBi_FatalError("pulse_vib: on_time[%d] must not be 0.\n", i);
161 return;
162 }
163 /* Does the ON time exceed VIB_ON_TIME_MAX? */
164 if (state->on_time[i] > VIB_ON_TIME_MAX)
165 {
166 VIBi_FatalError("pulse_vib: on_time[%d] is over VIB_ON_TIME_MAX.\n", i);
167 return;
168 }
169 }
170
171 /* Check the OFF time */
172 for (i = 0; i < state->pulse_num - 1; i++)
173 {
174 /* Whether or not the OFF time is 0 */
175 if (state->off_time[i] == 0)
176 {
177 VIBi_FatalError("pulse_vib: off_time[%d] must not be 0.\n", i);
178 return;
179 }
180 /* Does the OFF time exceed the previous ON time? */
181 if (state->on_time[i] > state->off_time[i])
182 {
183 VIBi_FatalError("pulse_vib: on_time[%d] is over off_time[%d].\n", i, i);
184 return;
185 }
186 }
187 /* Is the REST time less than VIB_REST_TIME_MIN? */
188 if (state->rest_time < VIB_REST_TIME_MIN)
189 {
190 VIBi_FatalError("pulse_vib: rest_time is less than VIB_REST_TIME_MIN.\n", i);
191 return;
192 }
193 }
194
195 pulse_vib.rest_tick = VIBi_PulseTimeToTicks(state->rest_time) - VIBi_INTR_DELAY_TICK;
196 pulse_vib.repeat_num = state->repeat_num;
197 pulse_vib.current_count = 0;
198
199 pulse_vib.current_pos = 0;
200
201 {
202 u32 i;
203
204 for (i = 0; i < VIB_PULSE_NUM_MAX; i++)
205 {
206 pulse_vib.on_tick[i] = VIBi_PulseTimeToTicks(state->on_time[i]) - VIBi_INTR_DELAY_TICK;
207 pulse_vib.off_tick[i] =
208 VIBi_PulseTimeToTicks(state->off_time[i]) - VIBi_INTR_DELAY_TICK;
209 }
210 }
211 pulse_vib.rest_pos = state->pulse_num * 2 - 1;
212
213 pulse_vib.is_enable = TRUE;
214 /* Send a pulse_vib structure pointer */
215 VIBi_MotorOnOff(&pulse_vib);
216 }
217
218 /*!
219 Stops pulse rumble.
220 */
VIB_StopPulse(void)221 void VIB_StopPulse(void)
222 {
223
224 if (pulse_vib.is_enable)
225 {
226
227 pulse_vib.is_enable = FALSE;
228 /* Send a pulse_vib structure pointer */
229 VIBi_MotorOnOff(&pulse_vib);
230 }
231 }
232
233 /*!
234 Returns whether or not pulse rumble has completed. It is considered to have completed at the point when the last rest_time has finished.
235
236 @retval TRUE: Pulse rumble has not completed.
237 @retval FALSE: Pulse rumble has completed.
238 */
VIB_IsExecuting(void)239 BOOL VIB_IsExecuting(void)
240 {
241
242 return pulse_vib.is_enable;
243 }
244
245 /*!
246 Registers the Game Pak removal callback.
247
248 When the Game Pak is pulled out, the library immediately stops the pulse rumble. @n
249 If a callback was registered using this function, it will be called afterwards.
250
251
252 @param func: Game Pak removal callback
253 */
VIB_SetCartridgePulloutCallback(VIBCartridgePulloutCallback func)254 void VIB_SetCartridgePulloutCallback(VIBCartridgePulloutCallback func)
255 {
256
257 pulse_vib.cartridge_pullout_callback = func;
258 }
259
260 /*!
261 Returns whether or not the Rumble Pak is enabled.
262 (If removed and inserted once, the function does not return TRUE)
263
264 @retval TRUE: The Rumble Pak is enabled.
265 @retval FALSE: The Rumble Pak is not enabled.
266 */
VIB_IsCartridgeEnabled(void)267 BOOL VIB_IsCartridgeEnabled(void)
268 {
269
270 return CTRDG_IsBitID(VIBi_BITID);
271 }
272
273 /*-----------------------------------------------------------------------*
274 Local Function Definition
275 *-----------------------------------------------------------------------*/
276
VIBi_PulseTimeToTicks(u32 pulse_time)277 static inline u32 VIBi_PulseTimeToTicks(u32 pulse_time)
278 {
279
280 return ((OS_SYSTEM_CLOCK / 1000) * (pulse_time)) / 64 / 10;
281 }
282
VIBi_PulledOutCallbackCartridge(void)283 static BOOL VIBi_PulledOutCallbackCartridge(void)
284 {
285
286 VIB_StopPulse();
287 if (pulse_vib.cartridge_pullout_callback)
288 {
289 pulse_vib.cartridge_pullout_callback();
290 }
291
292 return FALSE; /* Do not stop the software right away. */
293 }
294
295 /*!
296 Turn vibration on and off.
297
298 pulse_vib->is_enable
299 @li TRUE: Turn vibration on.
300 @li FALSE: Turn vibration off.
301 */
VIBi_MotorOnOff(VIBiPulseInfo * pulse_vib)302 static void VIBi_MotorOnOff(VIBiPulseInfo * pulse_vib)
303 {
304 /* Flush the shared memory that has been set */
305 DC_FlushRange(pulse_vib, sizeof(VIBiPulseInfo));
306 if (pulse_vib->is_enable == TRUE)
307 {
308 CTRDG_SendToARM7(pulse_vib);
309 }
310 else
311 {
312 CTRDG_SendToARM7(NULL);
313 }
314 }
315
VIBi_SleepCallback(void *)316 static void VIBi_SleepCallback(void *)
317 {
318
319 VIB_StopPulse();
320 }
321