1 /*---------------------------------------------------------------------------*
2   Project:  TwlSDK - MB - libraries
3   File:     mb_wm.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 
19 #include <nitro.h>
20 #include "mb_common.h"
21 #include "mb_wm.h"
22 #include "mb_child.h"
23 #include "mb_wm_base.h"
24 #include "mb_block.h"
25 
26 //===========================================================================
27 // Prototype Declarations
28 //===========================================================================
29 
30 static BOOL IsSendEnabled(void);
31 static void MBi_WMStateOutStartConnect(void *arg);
32 static void ChildStateOutStartMP(void *arg);
33 static void ChildPortCallback(void *arg);
34 static void StateOutMPSendToParent(void *arg);
35 
36 static void MBi_WMStateOutStartConnect(void *arg);
37 
38 static void MBi_WMStateOutEndMP(void *arg);
39 
40 static void MBi_WMStateOutDisconnect(void *arg);
41 static void MBi_WMStateInDisconnect(void);
42 
43 static void MBi_WMStateOutReset(void *arg);
44 
45 static void MBi_WMSendCallback(u16 type, void *arg);
46 static void MBi_WMErrorCallback(u16 apiid, u16 error_code);
47 static void MBi_WMApiErrorCallback(u16 apiid, u16 error_code);
48 
49 
50 //===========================================================================
51 // Variable Declarations
52 //===========================================================================
53 
54 static MBWMWork *wmWork = NULL;
55 
56 
57 //===========================================================================
58 // Function Definitions
59 //===========================================================================
60 
61 /*---------------------------------------------------------------------------*
62   Name:         MBi_WMSetBuffer
63 
64   Description:  Sets the buffer that MB_WM uses for MP communication.
65                 You must provide only the size of MBWMWork.
66 
67   Arguments:    buf:     Pointer to the region used as a work buffer.
68 
69   Returns:      None.
70  *---------------------------------------------------------------------------*/
MBi_WMSetBuffer(void * buf)71 void MBi_WMSetBuffer(void *buf)
72 {
73     SDK_NULL_ASSERT(buf);
74     SDK_ASSERT(((u32)buf & 0x1f) == 0); // Whether 32-byte aligned or not.
75 
76     wmWork = (MBWMWork *) buf;
77     wmWork->start_mp_busy = 0;         // Prevent multiple parent StartMP calls.
78     wmWork->mpStarted = 0;
79     wmWork->child_bitmap = 0;
80     wmWork->mpBusy = 0;
81     wmWork->endReq = 0;
82     wmWork->sendBuf = NULL;
83     wmWork->recvBuf = NULL;
84     wmWork->mpCallback = NULL;
85 }
86 
87 
88 /*---------------------------------------------------------------------------*
89   Name:         MBi_WMSetCallback
90 
91   Description:  Sets a callback.
92 
93   Arguments:    callback:    Callback function.
94 
95   Returns:      None.
96  *---------------------------------------------------------------------------*/
MBi_WMSetCallback(MBWMCallbackFunc callback)97 void MBi_WMSetCallback(MBWMCallbackFunc callback)
98 {
99     OSIntrMode enabled = OS_DisableInterrupts();
100 
101     wmWork->mpCallback = callback;
102 
103     (void)OS_RestoreInterrupts(enabled);
104 }
105 
106 
107 /*---------------------------------------------------------------------------*
108   Name:         MBi_WMStartConnect
109 
110   Description:  Begins a connection to a parent device.
111 
112   Arguments:    bssDesc:   BssDesc of the parent device to connect.
113 
114   Returns:      None.
115  *---------------------------------------------------------------------------*/
MBi_WMStartConnect(WMBssDesc * bssDesc)116 void MBi_WMStartConnect(WMBssDesc *bssDesc)
117 {
118     WMErrCode result;
119 
120     wmWork->mpStarted = 0;
121     wmWork->endReq = 0;
122 
123     wmWork->sendBufSize = (u16)WM_SIZE_MP_CHILD_SEND_BUFFER(bssDesc->gameInfo.childMaxSize, FALSE);
124     wmWork->recvBufSize =
125         (u16)WM_SIZE_MP_CHILD_RECEIVE_BUFFER(bssDesc->gameInfo.parentMaxSize, FALSE);
126     wmWork->pSendLen = bssDesc->gameInfo.parentMaxSize;
127     wmWork->pRecvLen = bssDesc->gameInfo.childMaxSize;
128     wmWork->blockSizeMax = (u16)MB_COMM_CALC_BLOCK_SIZE(wmWork->pSendLen);
129     MBi_SetChildMPMaxSize(wmWork->pRecvLen);
130 
131     result = WM_StartConnect(MBi_WMStateOutStartConnect, bssDesc, NULL);
132 
133     if (result != WM_ERRCODE_OPERATING)
134     {
135         MBi_WMSendCallback(MB_CALLBACK_CONNECT_FAILED, NULL);
136     }
137 }
138 
139 
140 /*---------------------------------------------------------------------------*
141   Name:         MBi_WMStateOutStartConnect
142 
143   Description:  Connection callback to the parent.
144 
145   Arguments:    arg:     WM_StartConnect callback argument.
146 
147   Returns:      None.
148  *---------------------------------------------------------------------------*/
MBi_WMStateOutStartConnect(void * arg)149 static void MBi_WMStateOutStartConnect(void *arg)
150 {
151     WMStartConnectCallback *cb = (WMStartConnectCallback *)arg;
152 
153     if (cb->errcode != WM_ERRCODE_SUCCESS)
154     {
155         MBi_WMSendCallback(MB_CALLBACK_CONNECT_FAILED, arg);
156         return;
157     }
158 
159     switch (cb->state)
160     {
161     case WM_STATECODE_BEACON_LOST:
162         break;
163     case WM_STATECODE_CONNECT_START:
164         break;
165     case WM_STATECODE_DISCONNECTED:
166         MBi_WMSendCallback(MB_CALLBACK_DISCONNECTED_FROM_PARENT, NULL);
167         break;
168     case WM_STATECODE_DISCONNECTED_FROM_MYSELF:
169         // Do nothing if it disconnects itself.
170         break;
171     case WM_STATECODE_CONNECTED:
172         // When authentication is complete.
173         MBi_WMSendCallback(MB_CALLBACK_CONNECTED_TO_PARENT, arg);
174         break;
175     }
176 }
177 
178 
179 /*---------------------------------------------------------------------------*
180   Name:         MBi_ChildStartMP
181 
182   Description:  Start MP communication
183 
184   Arguments:    sendBuf:     Pointer to the region to set as the send buffer.
185                 recvBuf:     Pointer to the region to set as the receive buffer.
186 
187   Returns:      Error code, normally WM_ERRCODE_OPERATING.
188  *---------------------------------------------------------------------------*/
MBi_ChildStartMP(u16 * sendBuf,u16 * recvBuf)189 void MBi_ChildStartMP(u16 *sendBuf, u16 *recvBuf)
190 {
191     WMErrCode result;
192 
193     wmWork->sendBuf = (u32 *)sendBuf;
194     wmWork->recvBuf = (u32 *)recvBuf;
195 
196     result = WM_SetPortCallback(WM_PORT_BT, ChildPortCallback, NULL);
197     if (result != WM_ERRCODE_SUCCESS)
198     {
199         MBi_WMApiErrorCallback(WM_APIID_START_MP, result);
200         return;
201     }
202 
203     result = WM_StartMPEx(ChildStateOutStartMP, recvBuf, wmWork->recvBufSize, sendBuf, wmWork->sendBufSize, 1,  // mpFreq
204                           0,           // defaultRetryCount
205                           FALSE,       // minPollBmpMode
206                           FALSE,       // singlePacketMode
207                           TRUE,        // fixFrameMode
208                           TRUE);       // ignoreFatalError
209 
210     if (result != WM_ERRCODE_OPERATING)
211     {
212         MBi_WMApiErrorCallback(WM_APIID_START_MP, result);
213     }
214 }
215 
216 /*---------------------------------------------------------------------------*
217   Name:         ChildStateOutStartMP
218 
219   Description:  Child WM_StartMPEx callback function.
220 
221   Arguments:    arg:     WM_StartMP callback argument.
222 
223   Returns:      Error code, normally WM_ERRCODE_OPERATING.
224  *---------------------------------------------------------------------------*/
ChildStateOutStartMP(void * arg)225 static void ChildStateOutStartMP(void *arg)
226 {
227     WMStartMPCallback *cb = (WMStartMPCallback *)arg;
228 
229     if (cb->errcode != WM_ERRCODE_SUCCESS)
230     {
231         // End when the error notification does not require error handling.
232         if (cb->errcode == WM_ERRCODE_SEND_FAILED)
233         {
234             return;
235         }
236         else if (cb->errcode == WM_ERRCODE_TIMEOUT)
237         {
238             return;
239         }
240         else if (cb->errcode == WM_ERRCODE_INVALID_POLLBITMAP)
241         {
242             return;
243         }
244 
245         MBi_WMErrorCallback(cb->apiid, cb->errcode);
246         return;
247     }
248 
249     switch (cb->state)
250     {
251     case WM_STATECODE_MP_START:
252         wmWork->mpStarted = 1;         // Set the flag that indicates MP has started.
253         wmWork->mpBusy = 0;
254         wmWork->child_bitmap = 0;
255         MBi_WMSendCallback(MB_CALLBACK_MP_STARTED, NULL);
256         {
257             // MP send-enabled callback.
258             MBi_WMSendCallback(MB_CALLBACK_MP_SEND_ENABLE, NULL);
259         }
260         break;
261 
262     case WM_STATECODE_MP_IND:
263         // None.
264         break;
265 
266     case WM_STATECODE_MPACK_IND:
267         // None.
268         break;
269 
270     case WM_STATECODE_MPEND_IND:      // Only occurs on a parent device.
271     default:
272         MBi_WMErrorCallback(cb->apiid, WM_ERRCODE_FAILED);
273         break;
274     }
275 }
276 
277 /*---------------------------------------------------------------------------*
278   Name:         MBi_WMDisconnect
279 
280   Description:  Child MP disconnect processing. Disconnects from the parent after WM_EndMP completes.
281 
282   Arguments:    None.
283 
284   Returns:      None.
285  *---------------------------------------------------------------------------*/
MBi_WMDisconnect(void)286 void MBi_WMDisconnect(void)
287 {
288     WMErrCode result;
289 
290     wmWork->endReq = 1;
291 
292     result = WM_EndMP(MBi_WMStateOutEndMP);
293 
294     if (result != WM_ERRCODE_OPERATING)
295     {
296         wmWork->endReq = 0;
297         MBi_WMApiErrorCallback(WM_APIID_END_MP, result);
298     }
299 }
300 
301 
302 /*---------------------------------------------------------------------------*
303   Name:         MBi_WMStateOutEndMP
304 
305   Description:  The callback function for WM_EndMP.
306 
307   Arguments:    arg:     The callback argument for WM_EndMP.
308 
309   Returns:      None.
310  *---------------------------------------------------------------------------*/
MBi_WMStateOutEndMP(void * arg)311 static void MBi_WMStateOutEndMP(void *arg)
312 {
313     WMCallback *cb = (WMCallback *)arg;
314 
315     if (cb->errcode != WM_ERRCODE_SUCCESS)
316     {
317         wmWork->endReq = 0;
318         MBi_WMErrorCallback(cb->apiid, cb->errcode);
319         return;
320     }
321 
322     wmWork->mpStarted = 0;
323     MBi_WMStateInDisconnect();
324 }
325 
326 
327 /*---------------------------------------------------------------------------*
328   Name:         MBi_WMStateInDisconnect
329 
330   Description:  Disconnects a child from the parent and transitions to the IDLE state.
331 
332   Arguments:    None.
333 
334   Returns:      None.
335  *---------------------------------------------------------------------------*/
MBi_WMStateInDisconnect(void)336 static void MBi_WMStateInDisconnect(void)
337 {
338     WMErrCode result;
339 
340     result = WM_Disconnect(MBi_WMStateOutDisconnect, 0);
341 
342     if (result != WM_ERRCODE_OPERATING)
343     {
344         wmWork->endReq = 0;
345         MBi_WMApiErrorCallback(WM_APIID_DISCONNECT, result);
346     }
347 }
348 
349 
350 /*---------------------------------------------------------------------------*
351   Name:         MBi_WMStateInDisconnect
352 
353   Description:  WM_Disconnect callback argument.
354 
355   Arguments:    arg:  WM_Disconnect callback argument.
356 
357   Returns:      None.
358  *---------------------------------------------------------------------------*/
MBi_WMStateOutDisconnect(void * arg)359 static void MBi_WMStateOutDisconnect(void *arg)
360 {
361     WMCallback *cb = (WMCallback *)arg;
362 
363     wmWork->endReq = 0;
364     if (cb->errcode != WM_ERRCODE_SUCCESS)
365     {
366         MBi_WMErrorCallback(cb->apiid, cb->errcode);
367         return;
368     }
369     MBi_WMSendCallback(MB_CALLBACK_DISCONNECT_COMPLETE, NULL);
370 }
371 
372 
373 /*---------------------------------------------------------------------------*
374   Name:         MBi_WMReset
375 
376   Description:  Resets the child, and causes a transition to the IDLE state.
377 
378   Arguments:    None.
379 
380   Returns:      None.
381  *---------------------------------------------------------------------------*/
MBi_WMReset(void)382 void MBi_WMReset(void)
383 {
384     WMErrCode result;
385 
386     result = WM_Reset(MBi_WMStateOutReset);
387     if (result != WM_ERRCODE_OPERATING)
388     {
389         MBi_WMApiErrorCallback(WM_APIID_RESET, result);
390     }
391 }
392 
393 
394 /*---------------------------------------------------------------------------*
395   Name:         MBi_WMStateOutReset
396 
397   Description:  Resets the child, and causes a transition to the IDLE state.
398 
399   Arguments:    None.
400 
401   Returns:      None.
402  *---------------------------------------------------------------------------*/
MBi_WMStateOutReset(void * arg)403 static void MBi_WMStateOutReset(void *arg)
404 {
405     WMCallback *cb = (WMCallback *)arg;
406 
407     if (cb->errcode != WM_ERRCODE_SUCCESS)
408     {
409         MBi_WMErrorCallback(cb->apiid, cb->errcode);
410         return;
411     }
412     // Reset sets to the idling (standby) state without starting the next state.
413     MBi_WMSendCallback(MB_CALLBACK_DISCONNECT_COMPLETE, NULL);
414 }
415 
416 
417 /*
418  * Check MP transmit permission
419 
420    Added the mpBusy flag, which is set when SetMP is executed, to the determination elements, so that MP will not be set again after SetMP and before the callback returns.
421 
422 
423  */
424 /*---------------------------------------------------------------------------*
425   Name:         IsSendEnabled
426 
427   Description:  This function determines whether or not it is OK to set new MP data at the present time.
428                 Added the mpBusy flag, which is set when SetMP is executed, to the determination elements, so that MP will not be set again after SetMP and before the callback returns.
429 
430 
431   Arguments:    None.
432 
433   Returns:      TRUE if it is OK to set new data.
434                 Otherwise returns FALSE.
435  *---------------------------------------------------------------------------*/
IsSendEnabled(void)436 static BOOL IsSendEnabled(void)
437 {
438     return (wmWork->mpStarted == 1) && (wmWork->mpBusy == 0) && (wmWork->endReq == 0);
439 }
440 
441 
442 /*---------------------------------------------------------------------------*
443   Name:         ChildPortCallback
444 
445   Description:  Child MP port callback function.
446 
447   Arguments:    arg:     Port callback argument for MP communication.
448 
449   Returns:      None.
450  *---------------------------------------------------------------------------*/
ChildPortCallback(void * arg)451 static void ChildPortCallback(void *arg)
452 {
453     WMPortRecvCallback *cb = (WMPortRecvCallback *)arg;
454 
455     if (cb->errcode != WM_ERRCODE_SUCCESS)
456     {
457         return;
458     }
459 
460     switch (cb->state)
461     {
462     case WM_STATECODE_PORT_RECV:
463         // Notify the program that data was received.
464         MBi_WMSendCallback(MB_CALLBACK_MP_CHILD_RECV, cb);
465         break;
466     case WM_STATECODE_CONNECTED:
467         // Connection notification
468         break;
469     case WM_STATECODE_PORT_INIT:
470     case WM_STATECODE_DISCONNECTED:
471     case WM_STATECODE_DISCONNECTED_FROM_MYSELF:
472         break;
473     }
474 }
475 
476 
477 /*---------------------------------------------------------------------------*
478   Name:         MBi_MPSendToParent
479 
480   Description:  Sends buffer contents to the parent.
481 
482   Arguments:    body_len:  Data size.
483                 pollbmp:  Poll bitmap of the other party (irrelevant for a child).
484                 sendbuf:  Pointer to send data.
485   Returns:      If send processing started successfully, returns WM_ERRCODE_OPERATING. If it failed, some other code is returned.
486 
487  *---------------------------------------------------------------------------*/
MBi_MPSendToParent(u32 body_len,u16 pollbmp,u32 * sendbuf)488 WMErrCode MBi_MPSendToParent(u32 body_len, u16 pollbmp, u32 *sendbuf)
489 {
490     WMErrCode result;
491 
492     // 32 byte align check
493     SDK_ASSERT(((u32)sendbuf & 0x1F) == 0);
494 
495     DC_FlushRange(sendbuf, sizeof(body_len));
496     DC_WaitWriteBufferEmpty();
497 
498     if (!IsSendEnabled())
499     {
500         return WM_ERRCODE_FAILED;
501     }
502 
503     result = WM_SetMPDataToPort(StateOutMPSendToParent,
504                                 (u16 *)sendbuf,
505                                 (u16)body_len, pollbmp, WM_PORT_BT, WM_PRIORITY_LOW);
506     if (result != WM_ERRCODE_OPERATING)
507     {
508         return result;
509     }
510 
511     wmWork->mpBusy = 1;
512     return WM_ERRCODE_OPERATING;
513 }
514 
515 
516 /*---------------------------------------------------------------------------*
517   Name:         StateOutMPSendToParent
518 
519   Description:  Notification callback for a completed MP transmission.
520 
521   Arguments:    arg:     WM_SetMPDataToPort callback argument.
522 
523   Returns:      None.
524  *---------------------------------------------------------------------------*/
StateOutMPSendToParent(void * arg)525 static void StateOutMPSendToParent(void *arg)
526 {
527     WMCallback *cb = (WMCallback *)arg;
528 
529     wmWork->mpBusy = 0;
530     if (cb->errcode == WM_ERRCODE_SUCCESS)
531     {
532         MBi_WMSendCallback(MB_CALLBACK_MP_CHILD_SENT, arg);
533     }
534     else if (cb->errcode == WM_ERRCODE_TIMEOUT)
535     {
536         MBi_WMSendCallback(MB_CALLBACK_MP_CHILD_SENT_TIMEOUT, arg);
537     }
538     else
539     {
540         MBi_WMSendCallback(MB_CALLBACK_MP_CHILD_SENT_ERR, arg);
541     }
542     // Allow the next transmission.
543     MBi_WMSendCallback(MB_CALLBACK_MP_SEND_ENABLE, NULL);
544 }
545 
546 
547 /*---------------------------------------------------------------------------*
548   Name:         MBi_WMSendCallback
549 
550   Description:  Performs callback notification in the WM layer.
551 
552   Arguments:    type  The callback type
553                 arg :     Callback argument
554 
555   Returns:      None.
556  *---------------------------------------------------------------------------*/
MBi_WMSendCallback(u16 type,void * arg)557 static inline void MBi_WMSendCallback(u16 type, void *arg)
558 {
559     if (wmWork->mpCallback == NULL)
560     {
561         return;
562     }
563     wmWork->mpCallback(type, arg);
564 }
565 
566 /*---------------------------------------------------------------------------*
567   Name:         MBi_WMErrorCallback
568 
569   Description:  Performs error notification in the WM layer.
570 
571   Arguments:    apiid:       WM_APIID that was the cause.
572                 error_code  Error code
573 
574   Returns:      None.
575  *---------------------------------------------------------------------------*/
MBi_WMErrorCallback(u16 apiid,u16 error_code)576 static inline void MBi_WMErrorCallback(u16 apiid, u16 error_code)
577 {
578     MBErrorCallback arg;
579 
580     if (wmWork->mpCallback == NULL)
581     {
582         return;
583     }
584 
585     arg.apiid = apiid;
586     arg.errcode = error_code;
587 
588     wmWork->mpCallback(MB_CALLBACK_ERROR, &arg);
589 }
590 
591 /*---------------------------------------------------------------------------*
592   Name:         MBi_WMApiErrorCallback
593 
594   Description:  Performs a notification when an error occurs with the return value of a WM API call.
595 
596   Arguments:    apiid:       WM_APIID that was the cause.
597                 error_code  Error code
598 
599   Returns:      None.
600  *---------------------------------------------------------------------------*/
MBi_WMApiErrorCallback(u16 apiid,u16 error_code)601 static inline void MBi_WMApiErrorCallback(u16 apiid, u16 error_code)
602 {
603     MBErrorCallback arg;
604 
605     if (wmWork->mpCallback == NULL)
606     {
607         return;
608     }
609 
610     arg.apiid = apiid;
611     arg.errcode = error_code;
612 
613     wmWork->mpCallback(MB_CALLBACK_API_ERROR, &arg);
614 }
615 
616 
617 /*---------------------------------------------------------------------------*
618   Name:         MBi_WMApiErrorCallback
619 
620   Description:  Performs a notification when an error occurs with the return value of a WM API call.
621 
622   Arguments:    apiid:       WM_APIID that was the cause.
623                 error_code  Error code
624 
625   Returns:      None.
626  *---------------------------------------------------------------------------*/
MBi_WMClearCallback(void)627 void MBi_WMClearCallback(void)
628 {
629     (void)WM_SetPortCallback(WM_PORT_BT, NULL, NULL);
630 }
631