1 /*---------------------------------------------------------------------------*
2 Project: TwlSDK - CTRDG - libraries - ARM9
3 File: ctrdg_task.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
22 Function's description
23
24 ********************************************************/
25 static CTRDGiTaskWork *ctrdgi_task_work = NULL;
26 static CTRDGTaskInfo ctrdgi_task_list;
27
28 static void CTRDGi_TaskThread(void *arg);
29
30 u64 ctrdg_task_stack[CTRDG_TASK_STACK_SIZE / sizeof(u64)];
31 /*---------------------------------------------------------------------------*
32 Name: CTRDGi_InitTaskThread
33
34 Description: Starts a task thread.
35
36 Arguments: p_work: Internal work buffer.
37 Used internally until CTRDGi_EndTaskThread() completes.
38
39 Returns: None.
40 *---------------------------------------------------------------------------*/
CTRDGi_InitTaskThread(void * p_work)41 void CTRDGi_InitTaskThread(void *p_work)
42 {
43 // IRQ interrupt prohibition
44 OSIntrMode bak_cpsr = OS_DisableInterrupts();
45 // Create a thread if this structure is NULL
46 if (!ctrdgi_task_work)
47 {
48 CTRDGiTaskWork *const p = (CTRDGiTaskWork *) p_work;
49
50 // Determines whether the thread has been initialized and can be used
51 SDK_ASSERT(OS_IsThreadAvailable());
52
53 /* Prepare the work structure, stack buffer and task thread. */
54 // At this point, the structure will no longer be NULL, so task threads are not created anew
55 ctrdgi_task_work = p;
56 // Initializes the end_task structure
57 CTRDGi_InitTaskInfo(&p->end_task);
58 // Initializes the ctrdgi_task_list structure
59 CTRDGi_InitTaskInfo(&ctrdgi_task_list);
60 // There should be no waiting task lists at this point, so insert NULL
61 p->list = NULL;
62
63 OS_CreateThread(p->th, CTRDGi_TaskThread, p,
64 ctrdg_task_stack + CTRDG_TASK_STACK_SIZE / sizeof(u64),
65 CTRDG_TASK_STACK_SIZE, CTRDG_TASK_PRIORITY_DEFAULT);
66 OS_WakeupThreadDirect(p->th);
67 }
68 // Restore IRQ interrupt permission
69 (void)OS_RestoreInterrupts(bak_cpsr);
70 }
71
72 /*---------------------------------------------------------------------------*
73 Name: CTRDGi_IsTaskAvailable
74
75 Description: Checks if a task thread is currently available.
76
77 Arguments: None.
78
79 Returns: TRUE if currently available, and FALSE otherwise.
80 *---------------------------------------------------------------------------*/
CTRDGi_IsTaskAvailable(void)81 BOOL CTRDGi_IsTaskAvailable(void)
82 {
83 return (ctrdgi_task_work != NULL);
84 }
85
86 /*---------------------------------------------------------------------------*
87 Name: CTRDGi_InitTaskInfo
88
89 Description: Initializes a task information structure.
90 Must be called once before using.
91
92 Arguments: pt: Uninitialized task information structure
93
94 Returns: None.
95 *---------------------------------------------------------------------------*/
CTRDGi_InitTaskInfo(CTRDGTaskInfo * pt)96 void CTRDGi_InitTaskInfo(CTRDGTaskInfo * pt)
97 {
98 SDK_ASSERT(pt != NULL);
99 MI_CpuClear8(pt, sizeof(*pt));
100 }
101
102 /*---------------------------------------------------------------------------*
103 Name: CTRDGi_IsTaskBusy
104
105 Description: Checks if task information is currently being used.
106
107 Arguments: pt: Task information
108
109 Returns: TRUE if currently being used, and FALSE otherwise.
110 *---------------------------------------------------------------------------*/
CTRDGi_IsTaskBusy(volatile const CTRDGTaskInfo * pt)111 BOOL CTRDGi_IsTaskBusy(volatile const CTRDGTaskInfo * pt)
112 {
113 return pt->busy != FALSE;
114 }
115
CTRDGi_TaskThread(void * arg)116 static void CTRDGi_TaskThread(void *arg)
117 {
118 CTRDGiTaskWork *const p = (CTRDGiTaskWork *) arg;
119 // Loop until a command to end the thread comes
120 for (;;)
121 {
122 // Initializes the structure
123 CTRDGTaskInfo trg;
124 MI_CpuClear8(&trg, sizeof(CTRDGTaskInfo));
125 /* Get the next task */
126 {
127 // IRQ interrupts prohibited
128 OSIntrMode bak_cpsr = OS_DisableInterrupts();
129 /* Sleep if in an idle state. */
130 // Loop and wait until a task comes to the waiting task list
131 while (!p->list)
132 {
133 OS_SleepThread(NULL);
134 }
135 // Because a task has come to the waiting task list, copy that task data structure to trg
136 trg = *p->list;
137 // Restore IRQ interrupt permission
138 (void)OS_RestoreInterrupts(bak_cpsr);
139 }
140 /* Execute task */
141 if (trg.task)
142 // Run the task with the function pointer, with trg as an argument
143 trg.result = (u32)(*trg.task) (&trg);
144 /* Execute task completion callback. */
145 // if you've come here, the task is over, so use the task callback
146 {
147 // IRQ interrupts prohibited
148 OSIntrMode bak_cpsr = OS_DisableInterrupts();
149 // Set the callback function
150 CTRDG_TASK_FUNC callback = trg.callback;
151
152 // FALSE because the task shouldn't be running at this point
153 ctrdgi_task_list.busy = FALSE;
154 // If there is a callback function
155 if (callback)
156 // Call the callback function with the function pointer, and trg as an argument
157 (void)(*callback) (&trg);
158 /*
159 * If an end request, the thread will end with interrupts disabled.
160 * (This disable setting is valid up to the moment of a context switch.)
161 */
162 //if (p->list == &p->end_task)
163 if (ctrdgi_task_work == NULL)
164 break;
165
166 // Initialize the list structure
167 p->list = NULL;
168
169 (void)OS_RestoreInterrupts(bak_cpsr);
170 }
171 }
172 OS_TPrintf("task-thread end.\n");
173 OS_ExitThread();
174 return;
175 }
176
177 /*---------------------------------------------------------------------------*
178 Name: CTRDGi_SetTask
179
180 Description: Adds a task to an internal thread.
181
182 Arguments: pt: Currently unused task information
183 task: Task function
184 callback: Callback when task completes (ignored if NULL)
185
186 Returns: None.
187 *---------------------------------------------------------------------------*/
CTRDGi_SetTask(CTRDGTaskInfo * pt,CTRDG_TASK_FUNC task,CTRDG_TASK_FUNC callback)188 void CTRDGi_SetTask(CTRDGTaskInfo * pt, CTRDG_TASK_FUNC task, CTRDG_TASK_FUNC callback)
189 {
190 // Insert the structure that has the current thread pointer and waiting task list
191 CTRDGiTaskWork *const p_work = ctrdgi_task_work;
192
193 SDK_ASSERT(pt != NULL);
194 SDK_ASSERT(CTRDGi_IsTaskAvailable());
195
196 if (!CTRDGi_IsTaskAvailable())
197 {
198 OS_TPanic("CTRDGi_SetTask() failed! (task-thread is not available now)");
199 }
200
201 // Something is amiss if there is a running task in the thread
202 if (ctrdgi_task_list.busy)
203 {
204 OS_TPanic("CTRDGi_SetTask() failed! (specified structure is busy)");
205 }
206
207 /* Add task */
208 {
209 // Sets the structure's parameters
210 OSIntrMode bak_cpsr = OS_DisableInterrupts();
211 pt->busy = TRUE;
212 pt->task = task;
213 pt->callback = callback;
214 /* Activate the thread if the task is new and in an idle state */
215
216 // If this is the command that ends that task
217 if (pt == &p_work->end_task)
218 {
219 /* Prohibit task thread usage from here */
220 ctrdgi_task_work = NULL;
221 }
222 // Insert this task's structure into the waiting task list and launch the task thread
223 ctrdgi_task_list = *pt;
224 // Stores the actual address
225 p_work->list = &ctrdgi_task_list;
226 OS_WakeupThreadDirect(p_work->th);
227
228 (void)OS_RestoreInterrupts(bak_cpsr);
229 }
230 }
231
232 /*---------------------------------------------------------------------------*
233 Name: CTRDGi_EndTaskThread
234
235 Description: Ends the task thread.
236
237 Arguments: callback: Callback when task thread ends (ignored if NULL)
238 This callback is called in the state just before the task thread ends, while interrupts are still disabled.
239
240 Returns: None.
241 *---------------------------------------------------------------------------*/
CTRDGi_EndTaskThread(CTRDG_TASK_FUNC callback)242 void CTRDGi_EndTaskThread(CTRDG_TASK_FUNC callback)
243 {
244 OSIntrMode bak_cpsr = OS_DisableInterrupts();
245 if (CTRDGi_IsTaskAvailable())
246 {
247 (void)CTRDGi_SetTask(&ctrdgi_task_work->end_task, NULL, callback);
248 }
249 (void)OS_RestoreInterrupts(bak_cpsr);
250 }
251
252 /*---------------------------------------------------------------------------*
253 Name: CTRDG_SetTaskThreadPriority
254
255 Description: Changes the task thread's priority.
256
257 Arguments: priority: Task thread's priority
258
259 Returns: None.
260 *---------------------------------------------------------------------------*/
CTRDG_SetTaskThreadPriority(u32 priority)261 void CTRDG_SetTaskThreadPriority(u32 priority)
262 {
263 if (ctrdgi_task_work)
264 {
265 CTRDGiTaskWork *const p = ctrdgi_task_work;
266 (void)OS_SetThreadPriority(p->th, priority);
267 }
268 }
269