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