1 /*---------------------------------------------------------------------------*
2 Project: TwlSDK - MB - libraries
3 File: mb_task.c
4
5 Copyright 2007-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:: 2008-09-18#$
14 $Rev: 8573 $
15 $Author: okubata_ryoma $
16 *---------------------------------------------------------------------------*/
17
18 #include <nitro.h>
19
20 #include "mb_task.h"
21
22
23 /* Struct ------------------------------------------------------------------ */
24
25 typedef struct
26 {
27 OSThread th[1]; /* Thread context */
28 MBiTaskInfo *volatile list; /* Waiting task list */
29 MBiTaskInfo end_task; /* Task structure for end-command */
30 }
31 MBiTaskWork;
32
33 /* Variable ---------------------------------------------------------------- */
34
35 static MBiTaskWork *mbi_task_work = NULL;
36
37
38 /* Function ---------------------------------------------------------------- */
39
MBi_TaskThread(void * arg)40 static void MBi_TaskThread(void *arg)
41 {
42 MBiTaskWork *const p = (MBiTaskWork *) arg;
43 for (;;)
44 {
45 MBiTaskInfo *trg = NULL;
46 /* Get the next task. */
47 {
48 OSIntrMode bak_cpsr = OS_DisableInterrupts();
49 /* Sleep if in an idle state. */
50 while (!p->list)
51 {
52 (void)OS_SetThreadPriority(p->th, OS_THREAD_PRIORITY_MIN);
53 OS_SleepThread(NULL);
54 }
55 trg = p->list;
56 p->list = p->list->next;
57 (void)OS_SetThreadPriority(p->th, trg->priority);
58 (void)OS_RestoreInterrupts(bak_cpsr);
59 }
60 /* Execute task. */
61 if (trg->task)
62 (*trg->task) (trg);
63 /* Execute task completion callback. */
64 {
65 OSIntrMode bak_cpsr = OS_DisableInterrupts();
66 MB_TASK_FUNC callback = trg->callback;
67 /*
68 * Here, we operate cautiously with regard to thread priority levels.
69 * 1. If there is no next task, specify the highest (wait sleep).
70 * 2. If there is a next task that is higher than the current one, change to that.
71 * 3. If there is a next task that is lower than the current one, leave as is.
72 * The priority level will never be lower than the current state.
73 */
74 const u32 cur_priority = OS_GetThreadPriority(p->th);
75 u32 new_priority;
76 if (!p->list)
77 new_priority = OS_THREAD_PRIORITY_MIN;
78 else if (cur_priority < p->list->priority)
79 new_priority = p->list->priority;
80 else
81 new_priority = cur_priority;
82 if (new_priority != cur_priority)
83 (void)OS_SetThreadPriority(p->th, new_priority);
84 trg->next = NULL;
85 trg->busy = FALSE;
86 if (callback)
87 (*callback) (trg);
88 /*
89 * If an end request, the thread will end with interrupts prohibited.
90 * (This disable setting is valid up to the moment of a context switch.)
91 */
92 if (trg == &p->end_task)
93 break;
94 (void)OS_RestoreInterrupts(bak_cpsr);
95 }
96 }
97 OS_TPrintf("task-thread end.\n");
98 OS_ExitThread();
99 return;
100 }
101
102 /*---------------------------------------------------------------------------*
103 Name: MBi_InitTaskThread
104
105 Description: Starts a task thread.
106
107 Arguments: p_work: Internal work buffer.
108 Used internally until MBi_EndTaskThread() completes.
109 size: Byte size of p_work.
110 Must be greater than MB_TASK_WORK_MIN; size - MB_TASK_WORK_MIN is used by the stack.
111
112
113 Returns: None.
114 *---------------------------------------------------------------------------*/
MBi_InitTaskThread(void * p_work,u32 size)115 void MBi_InitTaskThread(void *p_work, u32 size)
116 {
117 OSIntrMode bak_cpsr = OS_DisableInterrupts();
118 if (!mbi_task_work)
119 {
120 MBiTaskWork *const p = (MBiTaskWork *) p_work;
121
122 SDK_ASSERT(size >= MB_TASK_WORK_MIN);
123 SDK_ASSERT(OS_IsThreadAvailable());
124
125 /* Prepare the work structure, stack buffer and task thread. */
126 mbi_task_work = p;
127 MBi_InitTaskInfo(&p->end_task);
128 p->list = NULL;
129 size = (u32)((size - sizeof(MBiTaskWork)) & ~3);
130 OS_CreateThread(p->th, MBi_TaskThread, p,
131 (u8 *)(p + 1) + size, size, OS_THREAD_PRIORITY_MIN);
132 OS_WakeupThreadDirect(p->th);
133 }
134 (void)OS_RestoreInterrupts(bak_cpsr);
135 }
136
137 /*---------------------------------------------------------------------------*
138 Name: MBi_IsTaskAvailable
139
140 Description: Checks if a task thread is currently available.
141
142 Arguments: None.
143
144 Returns: TRUE if currently available, and FALSE otherwise.
145 *---------------------------------------------------------------------------*/
MBi_IsTaskAvailable(void)146 BOOL MBi_IsTaskAvailable(void)
147 {
148 return (mbi_task_work != NULL);
149 }
150
151 /*---------------------------------------------------------------------------*
152 Name: MBi_InitTaskInfo
153
154 Description: Initializes a task information structure.
155 Must be called once before using.
156
157 Arguments: pt: Uninitialized task information structure
158
159 Returns: None.
160 *---------------------------------------------------------------------------*/
MBi_InitTaskInfo(MBiTaskInfo * pt)161 void MBi_InitTaskInfo(MBiTaskInfo * pt)
162 {
163 SDK_ASSERT(pt != NULL);
164 MI_CpuClear8(pt, sizeof(*pt));
165 }
166
167 /*---------------------------------------------------------------------------*
168 Name: MBi_IsTaskBusy
169
170 Description: Checks if task information is currently being used.
171
172 Arguments: pt: Task information.
173
174 Returns: TRUE if currently being used, and FALSE otherwise.
175 *---------------------------------------------------------------------------*/
MBi_IsTaskBusy(volatile const MBiTaskInfo * pt)176 BOOL MBi_IsTaskBusy(volatile const MBiTaskInfo * pt)
177 {
178 return pt->busy != FALSE;
179 }
180
181 /*---------------------------------------------------------------------------*
182 Name: MBi_SetTask
183
184 Description: Adds a task to an internal thread.
185
186 Arguments: pt: Currently-unused task information
187 task: Task function
188 callback: Callback when task completes (ignored if NULL)
189 priority: Thread priority while executing a task
190
191 Returns: None.
192 *---------------------------------------------------------------------------*/
MBi_SetTask(MBiTaskInfo * pt,MB_TASK_FUNC task,MB_TASK_FUNC callback,u32 priority)193 void MBi_SetTask(MBiTaskInfo * pt, MB_TASK_FUNC task, MB_TASK_FUNC callback, u32 priority)
194 {
195 MBiTaskWork *const p_work = mbi_task_work;
196
197 SDK_ASSERT(pt != NULL);
198
199 /* Changed so that processing is silently ignored when the library shuts down or a card is removed. */
200 if (!MBi_IsTaskAvailable())
201 {
202 OS_TWarning("MBi_SetTask() ignored... (task-thread is not available now)");
203 return;
204 }
205 if (pt->busy)
206 {
207 OS_TWarning("MBi_SetTask() ignored... (specified structure is busy)");
208 return;
209 }
210
211 /* Support for expanded definitions of priority levels. */
212 if (priority > OS_THREAD_PRIORITY_MAX)
213 {
214 const u32 cur_priority = OS_GetThreadPriority(p_work->th);
215 if (priority == MB_TASK_PRIORITY_ABOVE)
216 {
217 /* Only one priority level higher than the caller. */
218 priority = (u32)((cur_priority > OS_THREAD_PRIORITY_MIN) ?
219 (cur_priority - 1) : OS_THREAD_PRIORITY_MIN);
220 }
221 else if (priority == MB_TASK_PRIORITY_BELOW)
222 {
223 /* Only one priority level lower than the caller. */
224 priority = (u32)((cur_priority < OS_THREAD_PRIORITY_MAX) ?
225 (cur_priority + 1) : OS_THREAD_PRIORITY_MAX);
226 }
227 else if (priority == MB_TASK_PRIORITY_NORMAL)
228 {
229 /* Same priority level as the caller. */
230 priority = cur_priority;
231 }
232 else
233 {
234 /* Merely an invalid specification. */
235 priority = OS_THREAD_PRIORITY_MAX;
236 }
237 }
238 /* Add task. */
239 {
240 OSIntrMode bak_cpsr = OS_DisableInterrupts();
241 pt->busy = TRUE;
242 pt->priority = priority;
243 pt->task = task;
244 pt->callback = callback;
245 /* Activate the thread if the task is new and in an idle state. */
246 if (!p_work->list)
247 {
248
249 if (pt == &p_work->end_task)
250 {
251 /* Prohibit task thread usage from here. */
252 mbi_task_work = NULL;
253 }
254
255 p_work->list = pt;
256 OS_WakeupThreadDirect(p_work->th);
257 }
258 else
259 {
260 /* Insert if the list is not empty. */
261 MBiTaskInfo *pos = p_work->list;
262 /* Always add to the end if this is an end command. */
263 if (pt == &p_work->end_task)
264 {
265 while (pos->next)
266 pos = pos->next;
267 pos->next = pt;
268 /* Prohibit task thread usage from here. */
269 mbi_task_work = NULL;
270 }
271 /* Insert in order of priority if this is a normal command. */
272 else
273 {
274 if (pos->priority > priority)
275 {
276 p_work->list = pt;
277 pt->next = pos;
278 }
279 else
280 {
281 while (pos->next && (pos->next->priority <= priority))
282 pos = pos->next;
283 pt->next = pos->next;
284 pos->next = pt;
285 }
286 }
287 }
288 (void)OS_RestoreInterrupts(bak_cpsr);
289 }
290 }
291
292 /*---------------------------------------------------------------------------*
293 Name: MBi_EndTaskThread
294
295 Description: Ends the task thread.
296
297 Arguments: callback: Callback when task thread ends (ignored if NULL)
298 This callback is called in the state just before the task thread ends, while interrupts are still disabled.
299
300 Returns: None.
301 *---------------------------------------------------------------------------*/
MBi_EndTaskThread(MB_TASK_FUNC callback)302 void MBi_EndTaskThread(MB_TASK_FUNC callback)
303 {
304 OSIntrMode bak_cpsr = OS_DisableInterrupts();
305 if (MBi_IsTaskAvailable())
306 {
307 MBi_SetTask(&mbi_task_work->end_task, NULL, callback, OS_THREAD_PRIORITY_MIN);
308 }
309 (void)OS_RestoreInterrupts(bak_cpsr);
310 }
311