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