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