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