1 /*---------------------------------------------------------------------------*
2 Project: TwlSDK - CARD - libraries
3 File: card_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:: 2008-11-21#$
14 $Rev: 9385 $
15 $Author: yosizaki $
16
17 *---------------------------------------------------------------------------*/
18
19
20 #include <nitro.h>
21
22 #include "../include/card_task.h"
23
24
25 /*---------------------------------------------------------------------------*/
26 /* Functions */
27
28 /*---------------------------------------------------------------------------*
29 Name: CARDi_InitTaskQueue
30
31 Description: Initialize task queue structure
32
33 Arguments: queue: CARDTaskQueue structure
34
35 Returns: None.
36 *---------------------------------------------------------------------------*/
CARDi_InitTaskQueue(CARDTaskQueue * queue)37 void CARDi_InitTaskQueue(CARDTaskQueue *queue)
38 {
39 queue->list = NULL;
40 queue->quit = FALSE;
41 OS_InitThreadQueue(queue->workers);
42 }
43
44 /*---------------------------------------------------------------------------*
45 Name: CARDi_QuitTaskQueue
46
47 Description: Ends all threads that monitor the task queue
48
49 Arguments: queue: CARDTaskQueue structure
50
51 Returns: None.
52 *---------------------------------------------------------------------------*/
CARDi_QuitTaskQueue(CARDTaskQueue * queue)53 SDK_INLINE void CARDi_QuitTaskQueue(CARDTaskQueue *queue)
54 {
55 OSIntrMode bak_cpsr = OS_DisableInterrupts();
56 queue->quit = TRUE;
57 OS_WakeupThread(queue->workers);
58 (void)OS_RestoreInterrupts(bak_cpsr);
59 }
60
61 /*---------------------------------------------------------------------------*
62 Name: CARDi_InitTask
63
64 Description: Initialize task structure
65
66 Arguments: task: CARDTask structure
67 priority: Thread priority
68 userdata: Arbitrary user-defined data associated with task
69 function: Function pointer for the task to execute
70 callback: Callback that is called after task completion. NULL if not necessary.
71
72 Returns: None.
73 *---------------------------------------------------------------------------*/
CARDi_InitTask(CARDTask * task,u32 priority,void * userdata,CARDTaskFunction function,CARDTaskFunction callback)74 void CARDi_InitTask(CARDTask *task, u32 priority, void *userdata,
75 CARDTaskFunction function, CARDTaskFunction callback)
76 {
77 task->next = NULL;
78 task->priority = priority;
79 task->userdata = userdata;
80 task->function = function;
81 task->callback = callback;
82 }
83
84 /*---------------------------------------------------------------------------*
85 Name: CARDi_ProcessTask
86
87 Description: Whether to run or add a task structure to the end of the task queue.
88
89 Arguments: queue: CARDTaskQueue structure
90 task: Task that is added or run
91 block: TRUE when there is a blocking operation.
92 In such cases, run here without adding to the queue.
93 changePriority: TRUE when following priority set in the task
94
95 Returns: None.
96 *---------------------------------------------------------------------------*/
CARDi_ProcessTask(CARDTaskQueue * queue,CARDTask * task,BOOL blocking,BOOL changePriority)97 void CARDi_ProcessTask(CARDTaskQueue *queue, CARDTask *task, BOOL blocking, BOOL changePriority)
98 {
99 // If there is a non-blocking operation, add to the end of the queue
100 if (!blocking)
101 {
102 OSIntrMode bak_cpsr = OS_DisableInterrupts();
103 CARDTask **pp = (CARDTask **)&queue->list;
104 for(; *pp; pp = &(*pp)->next)
105 {
106 }
107 *pp = task;
108 // If it is the first task for an empty queue, notify the work thread
109 if (pp == &queue->list)
110 {
111 OS_WakeupThread(queue->workers);
112 }
113 (void)OS_RestoreInterrupts(bak_cpsr);
114 }
115 // If there is a blocking operation, run the task and callback here
116 else
117 {
118 // If necessary, run under the priority set in the task
119 OSThread *curth = OS_GetCurrentThread();
120 u32 prio = 0;
121 if (changePriority)
122 {
123 prio = OS_GetThreadPriority(curth);
124 (void)OS_SetThreadPriority(curth, task->priority);
125 }
126 if (task->function)
127 {
128 (*task->function)(task);
129 }
130 // Make so that it is acceptable to set the next task in the callback
131 if (task->callback)
132 {
133 (*task->callback)(task);
134 }
135 // Restore priority that was temporarily changed
136 if (changePriority)
137 {
138 (void)OS_SetThreadPriority(curth, prio);
139 }
140 }
141 }
142
143 /*---------------------------------------------------------------------------*
144 Name: CARDi_ReceiveTask
145
146 Description: Gets next task from the task list.
147
148 Arguments: queue : CARDTaskQueue structure
149 block: TRUE when blocking when this does not exist
150
151 Returns: Returns obtained new task or NULL.
152 *---------------------------------------------------------------------------*/
CARDi_ReceiveTask(CARDTaskQueue * queue,BOOL blocking)153 CARDTask* CARDi_ReceiveTask(CARDTaskQueue *queue, BOOL blocking)
154 {
155 CARDTask *retval = NULL;
156 OSIntrMode bak_cpsr = OS_DisableInterrupts();
157 while (!queue->quit)
158 {
159 retval = queue->list;
160 if ((retval != NULL) || !blocking)
161 {
162 break;
163 }
164 OS_SleepThread(queue->workers);
165 }
166 if (retval)
167 {
168 queue->list = retval->next;
169 }
170 (void)OS_RestoreInterrupts(bak_cpsr);
171 return retval;
172 }
173
174 /*---------------------------------------------------------------------------*
175 Name: CARDi_TaskWorkerProcedure
176
177 Description: Procedure of the work thread that follows processing of the task list.
178
179 Arguments: arg: CARDTaskQueue structure
180
181 Returns: None.
182 *---------------------------------------------------------------------------*/
CARDi_TaskWorkerProcedure(void * arg)183 void CARDi_TaskWorkerProcedure(void *arg)
184 {
185 CARDTaskQueue *queue = (CARDTaskQueue*)arg;
186 // To accept any priority level of tasks, remains at a high level while idle
187 (void)OS_SetThreadPriority(OS_GetCurrentThread(), 0);
188 for (;;)
189 {
190 // Monitors queue and idles until the next task arrives
191 CARDTask *task = CARDi_ReceiveTask(queue, TRUE);
192 // If in a QUIT state, NULL is returned even in blocking mode
193 if (!task)
194 {
195 break;
196 }
197 // Run the task and callback based on the specified priority
198 CARDi_ProcessTask(queue, task, TRUE, TRUE);
199 }
200 }
201
202
203
204
205
206 #if defined(SDK_ARM9)
207 /*---------------------------------------------------------------------------*
208 * Old task thread processing
209 *---------------------------------------------------------------------------*/
210
211 #include "../include/card_common.h"
212
213 /*---------------------------------------------------------------------------*
214 Name: CARDi_ExecuteOldTypeTask
215
216 Description: If asynchronous, throws to the work thread, and if synchronous, runs here
217 (The caller of this function guarantees that the task thread is already exclusively controlled by CARDi_WaitTask().)
218
219
220 Arguments: task: Task function to set
221 async: TRUE if wanting an asynchronous process
222
223 Returns: None.
224 *---------------------------------------------------------------------------*/
CARDi_ExecuteOldTypeTask(void (* task)(CARDiCommon *),BOOL async)225 BOOL CARDi_ExecuteOldTypeTask(void (*task) (CARDiCommon *), BOOL async)
226 {
227 CARDiCommon *p = &cardi_common;
228 if (async)
229 {
230 // First, change priority of task threads that are sleeping
231 (void)OS_SetThreadPriority(p->thread.context, p->priority);
232 // Set the task to process and wake the thread
233 p->task_func = task;
234 p->flag |= CARD_STAT_TASK;
235 OS_WakeupThreadDirect(p->thread.context);
236 }
237 else
238 {
239 (*task)(p);
240 CARDi_EndTask(p);
241 }
242 return async ? TRUE : (p->cmd->result == CARD_RESULT_SUCCESS);
243 }
244
245 /*---------------------------------------------------------------------------*
246 Name: CARDi_OldTypeTaskThread
247
248 Description: Main function for task thread
249
250 Arguments: arg: Not used
251
252 Returns: None.
253 *---------------------------------------------------------------------------*/
CARDi_OldTypeTaskThread(void * arg)254 void CARDi_OldTypeTaskThread(void *arg)
255 {
256 CARDiCommon *p = &cardi_common;
257 (void)arg;
258 for (;;)
259 {
260 // Wait for the next process
261 OSIntrMode bak_psr = OS_DisableInterrupts();
262 for (;;)
263 {
264 if ((p->flag & CARD_STAT_TASK) != 0)
265 {
266 break;
267 }
268 OS_SleepThread(NULL);
269 }
270 (void)OS_RestoreInterrupts(bak_psr);
271 // Request processing
272 (void)CARDi_ExecuteOldTypeTask(p->task_func, FALSE);
273 }
274 }
275
276 /*---------------------------------------------------------------------------*
277 Name: CARDi_WaitForTask
278
279 Description: Waits until it can obtain usage rights for the task thread.
280 (Locking of the specified bus is guaranteed by the caller of this function.)
281
282 Arguments: p: Library work buffer
283 restart: If starting even the next task, TRUE
284 callback: Callback function after completion of access
285 callback_arg: Argument of callback function
286
287 Returns: None.
288 *---------------------------------------------------------------------------*/
CARDi_WaitForTask(CARDiCommon * p,BOOL restart,MIDmaCallback callback,void * callback_arg)289 BOOL CARDi_WaitForTask(CARDiCommon *p, BOOL restart, MIDmaCallback callback, void *callback_arg)
290 {
291 OSIntrMode bak_psr = OS_DisableInterrupts();
292 // Idle until current process is complete
293 while ((p->flag & CARD_STAT_BUSY) != 0)
294 {
295 OS_SleepThread(p->busy_q);
296 }
297 // If executing the next command immediately, go to reprocessing here
298 if (restart)
299 {
300 p->flag |= CARD_STAT_BUSY;
301 p->callback = callback;
302 p->callback_arg = callback_arg;
303 }
304 (void)OS_RestoreInterrupts(bak_psr);
305 return (p->cmd->result == CARD_RESULT_SUCCESS);
306 }
307
308 /*---------------------------------------------------------------------------*
309 Name: CARDi_EndTask
310
311 Description: Notifies that the task is complete and releases the usage rights of the task thread.
312
313 Arguments: p: Library's work buffer (passed by argument for efficiency)
314
315 Returns: None.
316 *---------------------------------------------------------------------------*/
CARDi_EndTask(CARDiCommon * p)317 void CARDi_EndTask(CARDiCommon *p)
318 {
319 MIDmaCallback callback = p->callback;
320 void *userdata = p->callback_arg;
321
322 OSIntrMode bak_psr = OS_DisableInterrupts();
323 // Set to completed state and notify idling thread
324 p->flag &= ~(CARD_STAT_BUSY | CARD_STAT_TASK | CARD_STAT_CANCEL);
325 OS_WakeupThread(p->busy_q);
326 (void)OS_RestoreInterrupts(bak_psr);
327 // Call the necessary callback
328 if (callback)
329 {
330 (*callback) (userdata);
331 }
332 }
333
334 #endif // defined(SDK_ARM9)
335