1 /*---------------------------------------------------------------------------*
2   Project:  TwlSDK - WXC - libraries -
3   File:     wxc_driver.c
4 
5   Copyright 2005-2009 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-12-16#$
14   $Rev: 9661 $
15   $Author: okubata_ryoma $
16  *---------------------------------------------------------------------------*/
17 
18 #include <nitro.h>
19 
20 #include <nitro/wxc/common.h>
21 #include <nitro/wxc/driver.h>
22 
23 
24 /*****************************************************************************/
25 /* Declaration */
26 
27 /* Error check function group */
28 static void WXCi_RecoverWmApiFailure(WXCDriverWork *, WMApiid id, WMErrCode err);
29 static BOOL WXCi_CheckWmApiResult(WXCDriverWork * driver, WMApiid id, WMErrCode err);
30 static BOOL WXCi_CheckWmCallbackResult(WXCDriverWork *, void *arg);
31 static void WXCi_ErrorQuit(WXCDriverWork *);
32 
33 /*
34  * Continuous transition procedure between wireless states.
35  * It is first called with an argument of NULL and then makes repeated callbacks to the same function.
36  * An event notification callback occurs when the intended state is reached.
37  *
38  * Note:
39  *   These functions have been designed to ultimately make threading easy, so during maintenance you should not split up individual WM function calls within these functions nor separate them into StateIn- and StateOut-.
40  *
41  *
42  */
43 static void WXCi_InitProc(void *arg);  /* (end) -> STOP             */
44 static void WXCi_StartProc(void *arg); /* STOP -> IDLE              */
45 static void WXCi_StopProc(void *arg);  /* IDLE -> STOP              */
46 static void WXCi_EndProc(void *arg);   /* STOP  -> (end)            */
47 static void WXCi_ResetProc(void *arg); /* (any) -> IDLE             */
48 static void WXCi_StartParentProc(void *arg);    /* IDLE  -> MP_PARENT        */
49 static void WXCi_StartChildProc(void *arg);     /* IDLE  -> MP_CHILD         */
50 static void WXCi_ScanProc(void *arg);  /* IDLE -> SCAN -> IDLE      */
51 static void WXCi_MeasureProc(void *arg);        /* IDLE -> (measure) -> IDLE */
52 
53 /* State transition control */
54 static void WXCi_OnStateChanged(WXCDriverWork *, WXCDriverState state, void *arg);
55 
56 /* Other callbacks or indicators */
57 static void WXCi_IndicateCallback(void *arg);
58 static void WXCi_PortCallback(void *arg);
59 static void WXCi_MPSendCallback(void *arg);
60 static void WXCi_ParentIndicate(void *arg);
61 static void WXCi_ChildIndicate(void *arg);
62 
63 /*****************************************************************************/
64 /* Constants */
65 
66 /* In SDK 3.0 RC and later, behavior was changed so that an indicator is generated even for the local host's disconnection. */
67 #define VERSION_TO_INT(major, minor, relstep) \
68     (((major) << 24) | ((minor) << 16) | ((relstep) << 0))
69 #if VERSION_TO_INT(SDK_VERSION_MAJOR, SDK_VERSION_MINOR, SDK_VERSION_RELSTEP) < VERSION_TO_INT(3, 0, 20100)
70 #define WM_STATECODE_DISCONNECTED_FROM_MYSELF ((WMStateCode)26)
71 #endif
72 
73 
74 /*****************************************************************************/
75 /* Variables */
76 
77 /*
78  * Work memory for the wireless driver.
79  * - This variable has been provided to use as "this" only in WM callbacks.
80  *   (Normally, WXCDriverWork is taken as an argument to explicitly specify the invocation target whenever possible.)
81  * - Because directly referencing a global pointer causes it to be treated as semi-volatile and is inefficient, it is better to temporarily copy this pointer into a local variable in functions that use it frequently.
82  *
83  */
84 static WXCDriverWork *work;
85 
86 
87 /*****************************************************************************/
88 /* Functions */
89 
90 /*---------------------------------------------------------------------------*
91   Name:         WXCi_ErrorQuit
92 
93   Description:  Resets when an error is detected.
94 
95   Arguments:    driver: WXCDriverWork structure
96 
97   Returns:      None.
98  *---------------------------------------------------------------------------*/
WXCi_ErrorQuit(WXCDriverWork * driver)99 static void WXCi_ErrorQuit(WXCDriverWork * driver)
100 {
101     /* This usually ends up as an abnormal termination, so ignore BUSY */
102     if (driver->state == WXC_DRIVER_STATE_BUSY)
103     {
104         driver->state = driver->target;
105     }
106     WXCi_End(driver);
107 }
108 
109 /*---------------------------------------------------------------------------*
110   Name:         WXCi_RecoverWmApiFailure
111 
112   Description:  Attempts state recovery after WM function errors.
113 
114   Arguments:    driver: WXCDriverWork structure
115                 id: WM function types
116                 err: Error code returned from the function
117 
118   Returns:      None.
119  *---------------------------------------------------------------------------*/
WXCi_RecoverWmApiFailure(WXCDriverWork * driver,WMApiid id,WMErrCode err)120 static void WXCi_RecoverWmApiFailure(WXCDriverWork * driver, WMApiid id, WMErrCode err)
121 {
122     /* Just in case, success is also determined here */
123     if (err == WM_ERRCODE_SUCCESS)
124     {
125         return;
126     }
127 
128     switch (id)
129     {
130     default:
131         /* Functions which should not be used in WXC */
132         OS_TWarning("WXC library error (unknown WM API : %d)\n", id);
133         WXCi_ErrorQuit(driver);
134         break;
135 
136     case WM_APIID_ENABLE:
137     case WM_APIID_POWER_ON:
138     case WM_APIID_POWER_OFF:
139     case WM_APIID_DISABLE:
140     case WM_APIID_RESET:
141         /* If it failed to even initialize, end it */
142         WXCi_ErrorQuit(driver);
143         break;
144 
145     case WM_APIID_MEASURE_CHANNEL:
146     case WM_APIID_SET_P_PARAM:
147     case WM_APIID_START_PARENT:
148     case WM_APIID_START_SCAN_EX:
149     case WM_APIID_END_SCAN:
150     case WM_APIID_DISCONNECT:
151         /* If an unexpected failure occurred in a major state transition function, end */
152         WXCi_ErrorQuit(driver);
153         break;
154 
155     /* If the parent disconnected at the WM_StartMP stage, WM_PowerOff will unfortunately be called without the system going into the IDLE state, so forcefully change the WXC state so that WM_Reset is run.
156         */
157     case WM_APIID_START_MP:
158         driver->state = WXC_DRIVER_STATE_CHILD; // Cancel BUSY
159         WXCi_ErrorQuit(driver);
160         break;
161 
162     case WM_APIID_START_CONNECT:
163         /* It is possible for connection to fail when StartConnect completes, so go to IDLE again */
164         if ((err == WM_ERRCODE_FAILED) ||
165             (err == WM_ERRCODE_NO_ENTRY) || (err == WM_ERRCODE_OVER_MAX_ENTRY))
166         {
167             /* Only for this moment, cancel BUSY */
168             driver->state = WXC_DRIVER_STATE_CHILD;
169             WXCi_Reset(driver);
170         }
171         /* Judge anything else to be a fatal error, and end */
172         else
173         {
174             WXCi_ErrorQuit(driver);
175         }
176         break;
177 
178     case WM_APIID_INDICATION:
179         /* If a fatal notification such as FIFO_ERROR or FLASH_ERROR is received, end */
180         WXCi_ErrorQuit(driver);
181         break;
182 
183     case WM_APIID_SET_MP_DATA:
184     case WM_APIID_PORT_SEND:
185         /*
186          * The following are cases in which SetMPData fails.
187          *   ILLEGAL_STATE, INVALID_PARAM, FIFO_ERROR: Always
188          *   NO_CHILD: When the function is called
189          *   SEND_QUEUE_FULL, SEND_FAILED: During callback
190          * This problem is resolvable by retrying, so ignore it.
191          */
192         break;
193 
194     }
195 }
196 
197 /*---------------------------------------------------------------------------*
198   Name:         WXCi_CheckWmApiResult
199 
200   Description:  Evaluates the return value of WM functions.
201 
202   Arguments:    driver: WXCDriverWork structure
203                 id: WM function types
204                 err: Error code returned from the function
205 
206   Returns:      If WM_ERRCODE_SUCCESS: TRUE.
207                 Otherwise, output error: FALSE.
208  *---------------------------------------------------------------------------*/
WXCi_CheckWmApiResult(WXCDriverWork * driver,WMApiid id,WMErrCode err)209 static BOOL WXCi_CheckWmApiResult(WXCDriverWork * driver, WMApiid id, WMErrCode err)
210 {
211     BOOL    ret = WXC_CheckWmApiResult(id, err);
212     /* Recovery operation for each API when there has been an error */
213     if (!ret)
214     {
215         WXCi_RecoverWmApiFailure(driver, id, err);
216     }
217     return ret;
218 }
219 
220 /*---------------------------------------------------------------------------*
221   Name:         WXCi_CheckWmCallbackResult
222 
223   Description:  Evaluates the return value of WM callbacks.
224 
225   Arguments:    driver: WXCDriverWork structure
226                 arg: Argument returned from the function
227 
228   Returns:      If WM_ERRCODE_SUCCESS: TRUE.
229                 Otherwise, output error: FALSE.
230  *---------------------------------------------------------------------------*/
WXCi_CheckWmCallbackResult(WXCDriverWork * driver,void * arg)231 static BOOL WXCi_CheckWmCallbackResult(WXCDriverWork * driver, void *arg)
232 {
233     BOOL    ret = WXC_CheckWmCallbackResult(arg);
234     /* Recovery operation for each API when there has been an error */
235     if (!ret)
236     {
237         const WMCallback *cb = (const WMCallback *)arg;
238         WXCi_RecoverWmApiFailure(driver, (WMApiid)cb->apiid, (WMErrCode)cb->errcode);
239     }
240     return ret;
241 }
242 
243 /*---------------------------------------------------------------------------*
244   Name:         WXCi_CallDriverEvent
245 
246   Description:  Notifies of a wireless driver event.
247 
248   Arguments:    driver: WXCDriverWork structure
249                 event: Notified event
250                 arg: Argument defined corresponding to event
251 
252   Returns:      u32-type event result value, defined corresponding to each event.
253  *---------------------------------------------------------------------------*/
WXCi_CallDriverEvent(WXCDriverWork * driver,WXCDriverEvent event,void * arg)254 static inline u32 WXCi_CallDriverEvent(WXCDriverWork * driver, WXCDriverEvent event, void *arg)
255 {
256     u32     result = 0;
257     if (driver->callback)
258     {
259         result = (*driver->callback) (event, arg);
260     }
261     return result;
262 }
263 
264 /*---------------------------------------------------------------------------*
265   Name:         WXCi_CallSendEvent
266 
267   Description:  Callback function for the data send callback event.
268 
269   Arguments:    driver: WXCDriverWork structure
270 
271   Returns:      None.
272  *---------------------------------------------------------------------------*/
WXCi_CallSendEvent(WXCDriverWork * driver)273 static void WXCi_CallSendEvent(WXCDriverWork * driver)
274 {
275     if (!driver->send_busy)
276     {
277         /*
278          * Set information for the send packet buffer
279          * TODO:
280          * Would this be more efficient if it saved once when MP communications are established?
281          * The same process actually exists in wxc_api.c.
282          */
283         const u16 max_length = (u16)((driver->own_aid == 0) ?
284             driver->parent_param->parentMaxSize : driver->target_bss->gameInfo.childMaxSize);
285         WXCPacketInfo packet;
286         packet.bitmap = driver->peer_bitmap;
287         packet.length = max_length;
288         packet.buffer = driver->current_send_buf;
289         /* Have the user set send data */
290         (void)WXCi_CallDriverEvent(driver, WXC_DRIVER_EVENT_DATA_SEND, &packet);
291         /* Send the data */
292         if (packet.length <= max_length)
293         {
294             WMErrCode ret;
295             ret = WM_SetMPDataToPort(WXCi_MPSendCallback,
296                                      (u16 *)packet.buffer, packet.length,
297                                      packet.bitmap, WXC_DEFAULT_PORT, WXC_DEFAULT_PORT_PRIO);
298             driver->send_busy = WXCi_CheckWmApiResult(driver, WM_APIID_SET_MP_DATA, ret);
299         }
300     }
301 }
302 
303 /*---------------------------------------------------------------------------*
304   Name:         WXCi_OnStateChanged
305 
306   Description:  Process when system is stable in the specified state.
307 
308   Arguments:    driver: WXCDriverWork structure
309                 state: Transition is the defined state
310                 arg: Argument for each event
311 
312   Returns:      u32-type event result value, defined corresponding to each event.
313  *---------------------------------------------------------------------------*/
WXCi_OnStateChanged(WXCDriverWork * driver,WXCDriverState state,void * arg)314 static void WXCi_OnStateChanged(WXCDriverWork * driver, WXCDriverState state, void *arg)
315 {
316     driver->state = state;
317 
318     /* Notify the user if the current state is the transition target */
319     if (driver->target == state)
320     {
321         switch (state)
322         {
323         case WXC_DRIVER_STATE_END:
324             (void)WXCi_CallDriverEvent(driver, WXC_DRIVER_EVENT_STATE_END, NULL);
325             break;
326 
327         case WXC_DRIVER_STATE_STOP:
328             (void)WXCi_CallDriverEvent(driver, WXC_DRIVER_EVENT_STATE_STOP, NULL);
329             break;
330 
331         case WXC_DRIVER_STATE_IDLE:
332             (void)WXCi_CallDriverEvent(driver, WXC_DRIVER_EVENT_STATE_IDLE, NULL);
333             break;
334 
335         case WXC_DRIVER_STATE_PARENT:
336             driver->send_busy = FALSE;
337             (void)WXCi_CallDriverEvent(driver, WXC_DRIVER_EVENT_STATE_PARENT, NULL);
338             break;
339 
340         case WXC_DRIVER_STATE_CHILD:
341             driver->send_busy = FALSE;
342             (void)WXCi_CallDriverEvent(driver, WXC_DRIVER_EVENT_STATE_CHILD, NULL);
343             /* Here, the connection with the parent is notified */
344             driver->peer_bitmap |= (u16)(1 << 0);
345             (void)WXCi_CallDriverEvent(driver, WXC_DRIVER_EVENT_CONNECTED, arg);
346             WXCi_CallSendEvent(driver);
347             break;
348         }
349     }
350 
351     /*
352      * Otherwise, continue transitioning
353      * +-----+      +------+       +------+                     +--------+
354      * |     > Init |      > Start >      > Measure/StartParent > PARENT |
355      * |     |      |      |       |      <               Reset <        |
356      * |     |      |      |       |      |                     +--------+
357      * | END |      | STOP |       | IDLE > Scan/StartChild     > CHILD  |
358      * |     |      |      |       |      <               Reset <        |
359      * |     |      |      <  Stop <      |                     +--------+
360      * |     <  End <      <   End <      |
361      * +-----+      +------+       +------+
362      */
363     else
364     {
365         switch (state)
366         {
367 
368         case WXC_DRIVER_STATE_END:
369             WXCi_InitProc(NULL);
370             break;
371 
372         case WXC_DRIVER_STATE_STOP:
373             switch (driver->target)
374             {
375             case WXC_DRIVER_STATE_END:
376                 WXCi_EndProc(NULL);
377                 break;
378             case WXC_DRIVER_STATE_IDLE:
379             case WXC_DRIVER_STATE_PARENT:
380             case WXC_DRIVER_STATE_CHILD:
381                 WXCi_StartProc(NULL);
382                 break;
383             }
384             break;
385 
386         case WXC_DRIVER_STATE_IDLE:
387             switch (driver->target)
388             {
389             case WXC_DRIVER_STATE_END:
390             case WXC_DRIVER_STATE_STOP:
391                 WXCi_StopProc(NULL);
392                 break;
393             case WXC_DRIVER_STATE_PARENT:
394                 driver->need_measure_channel = TRUE;
395                 if (driver->need_measure_channel)
396                 {
397                     WXCi_MeasureProc(NULL);
398                 }
399                 break;
400             case WXC_DRIVER_STATE_CHILD:
401                 WXCi_ScanProc(NULL);
402                 break;
403             }
404             break;
405 
406         case WXC_DRIVER_STATE_PARENT:
407         case WXC_DRIVER_STATE_CHILD:
408             WXCi_ResetProc(NULL);
409             break;
410 
411         }
412     }
413 }
414 
415 /*---------------------------------------------------------------------------*
416   Name:         WXCi_MPSendCallback
417 
418   Description:  The callback function to WM_SetMPData.
419 
420   Arguments:    arg: Pointer to the callback structure
421 
422   Returns:      None.
423  *---------------------------------------------------------------------------*/
WXCi_MPSendCallback(void * arg)424 static void WXCi_MPSendCallback(void *arg)
425 {
426     WXCDriverWork *const driver = work;
427 
428     (void)WXCi_CheckWmCallbackResult(driver, arg);
429     driver->send_busy = FALSE;
430     if (driver->peer_bitmap != 0)
431     {
432         WXCi_CallSendEvent(driver);
433     }
434 }
435 
436 /*---------------------------------------------------------------------------*
437   Name:         WXCi_IndicateCallback
438 
439   Description:  The callback function called when Indicate occurs.
440 
441   Arguments:    arg: Pointer to the callback structure
442 
443   Returns:      None.
444  *---------------------------------------------------------------------------*/
WXCi_IndicateCallback(void * arg)445 static void WXCi_IndicateCallback(void *arg)
446 {
447     /* TODO: Can't this be integrated with CheckError? */
448     WXCDriverWork *const driver = work;
449     WMIndCallback *cb = (WMIndCallback *)arg;
450     if (cb->errcode == WM_ERRCODE_FIFO_ERROR)
451     {
452         WXC_DRIVER_LOG("WM_ERRCODE_FIFO_ERROR Indication!\n");
453         /* Unrecoverable error */
454         driver->target = WXC_DRIVER_STATE_ERROR;
455         driver->state = WXC_DRIVER_STATE_ERROR;
456     }
457 }
458 
459 /*---------------------------------------------------------------------------*
460   Name:         WXCi_PortCallback
461 
462   Description:  The reception notification to the port.
463 
464   Arguments:    arg: Pointer to the callback structure
465 
466   Returns:      None.
467  *---------------------------------------------------------------------------*/
WXCi_PortCallback(void * arg)468 static void WXCi_PortCallback(void *arg)
469 {
470     WXCDriverWork *const driver = work;
471 
472     if (WXCi_CheckWmCallbackResult(driver, arg))
473     {
474         WMPortRecvCallback *cb = (WMPortRecvCallback *)arg;
475         switch (cb->state)
476         {
477         case WM_STATECODE_PORT_RECV:
478             {
479                 WXCPacketInfo packet;
480                 packet.bitmap = (u16)(1 << cb->aid);
481                 packet.length = cb->length;
482                 packet.buffer = (u8 *)cb->data;
483                 (void)WXCi_CallDriverEvent(driver, WXC_DRIVER_EVENT_DATA_RECV, &packet);
484             }
485             break;
486         case WM_STATECODE_CONNECTED:
487             break;
488         case WM_STATECODE_DISCONNECTED_FROM_MYSELF:
489         case WM_STATECODE_DISCONNECTED:
490             WXC_DRIVER_LOG("disconnected(%02X-=%02X)\n", driver->peer_bitmap, (1 << cb->aid));
491             driver->peer_bitmap &= (u16)~(1 << cb->aid);
492             (void)WXCi_CallDriverEvent(driver, WXC_DRIVER_EVENT_DISCONNECTED,
493                                        (void *)(1 << cb->aid));
494             break;
495         }
496     }
497 }
498 
499 /*---------------------------------------------------------------------------*
500   Name:         WXCi_InitProc
501 
502   Description:  Successive transition from READY -> STOP.
503 
504   Arguments:    arg: Callback argument (specify NULL on invocation)
505 
506   Returns:      None.
507  *---------------------------------------------------------------------------*/
WXCi_InitProc(void * arg)508 static void WXCi_InitProc(void *arg)
509 {
510     WXCDriverWork *const driver = work;
511     WMCallback *cb = (WMCallback *)arg;
512 
513     if (!arg || WXCi_CheckWmCallbackResult(driver, arg))
514     {
515         WMErrCode wmResult = WM_ERRCODE_SUCCESS;
516         /* (outof) -> STOP */
517         if (!arg)
518         {
519             /* Currently, MeasureChannel only runs once at startup */
520             driver->need_measure_channel = TRUE;
521             driver->state = WXC_DRIVER_STATE_BUSY;
522             wmResult = WM_Init(driver->wm_work, driver->wm_dma);
523             (void)WXCi_CheckWmApiResult(driver, WM_APIID_INITIALIZE, wmResult);
524             wmResult = WM_Enable(WXCi_InitProc);
525             (void)WXCi_CheckWmApiResult(driver, WM_APIID_ENABLE, wmResult);
526         }
527         /* End */
528         else if (cb->apiid == WM_APIID_ENABLE)
529         {
530             /* Receive each type of notification */
531             wmResult = WM_SetIndCallback(WXCi_IndicateCallback);
532             if (WXCi_CheckWmApiResult(driver, WM_APIID_INDICATION, wmResult))
533             {
534                 /* Sets the port callback */
535                 wmResult = WM_SetPortCallback(WXC_DEFAULT_PORT, WXCi_PortCallback, NULL);
536                 if (WXCi_CheckWmApiResult(driver, WM_APIID_PORT_SEND, wmResult))
537                 {
538                     WXCi_OnStateChanged(driver, WXC_DRIVER_STATE_STOP, NULL);
539                 }
540             }
541         }
542     }
543 }
544 
545 /*---------------------------------------------------------------------------*
546   Name:         WXCi_StartProc
547 
548   Description:  Successive transition from STOP -> IDLE.
549 
550   Arguments:    arg: Callback argument (specify NULL on invocation)
551 
552   Returns:      None.
553  *---------------------------------------------------------------------------*/
WXCi_StartProc(void * arg)554 static void WXCi_StartProc(void *arg)
555 {
556     WXCDriverWork *const driver = work;
557     WMCallback *cb = (WMCallback *)arg;
558 
559     if (!arg || WXCi_CheckWmCallbackResult(driver, arg))
560     {
561         WMErrCode wmResult = WM_ERRCODE_SUCCESS;
562         /* STOP -> IDLE */
563         if (!arg)
564         {
565             driver->state = WXC_DRIVER_STATE_BUSY;
566             wmResult = WM_PowerOn(WXCi_StartProc);
567             (void)WXCi_CheckWmApiResult(driver, WM_APIID_POWER_ON, wmResult);
568         }
569         /* End */
570         else if (cb->apiid == WM_APIID_POWER_ON)
571         {
572             WXCi_OnStateChanged(driver, WXC_DRIVER_STATE_IDLE, NULL);
573         }
574     }
575 }
576 
577 /*---------------------------------------------------------------------------*
578   Name:         WXCi_StopProc
579 
580   Description:  Successive transition from IDLE -> STOP.
581 
582   Arguments:    arg: Callback argument (specify NULL on invocation)
583 
584   Returns:      None.
585  *---------------------------------------------------------------------------*/
WXCi_StopProc(void * arg)586 static void WXCi_StopProc(void *arg)
587 {
588     WXCDriverWork *const driver = work;
589     WMCallback *cb = (WMCallback *)arg;
590 
591     if (!arg || WXCi_CheckWmCallbackResult(driver, arg))
592     {
593         WMErrCode wmResult = WM_ERRCODE_SUCCESS;
594         /* IDLE -> STOP */
595         if (!arg)
596         {
597             driver->state = WXC_DRIVER_STATE_BUSY;
598             wmResult = WM_PowerOff(WXCi_StopProc);
599             (void)WXCi_CheckWmApiResult(driver, WM_APIID_POWER_OFF, wmResult);
600         }
601         /* End */
602         else if (cb->apiid == WM_APIID_POWER_OFF)
603         {
604             WXCi_OnStateChanged(driver, WXC_DRIVER_STATE_STOP, NULL);
605         }
606     }
607 }
608 
609 /*---------------------------------------------------------------------------*
610   Name:         WXCi_EndProc
611 
612   Description:  Successive transition from STOP -> READY.
613 
614   Arguments:    arg: Callback argument (specify NULL on invocation)
615 
616   Returns:      None.
617  *---------------------------------------------------------------------------*/
WXCi_EndProc(void * arg)618 static void WXCi_EndProc(void *arg)
619 {
620     WXCDriverWork *const driver = work;
621     WMCallback *cb = (WMCallback *)arg;
622 
623     if (!arg || WXCi_CheckWmCallbackResult(driver, arg))
624     {
625         WMErrCode wmResult = WM_ERRCODE_SUCCESS;
626         /* STOP -> READY */
627         if (!arg)
628         {
629             driver->state = WXC_DRIVER_STATE_BUSY;
630             wmResult = WM_Disable(WXCi_EndProc);
631             (void)WXCi_CheckWmApiResult(driver, WM_APIID_DISABLE, wmResult);
632         }
633         /* End */
634         else if (cb->apiid == WM_APIID_DISABLE)
635         {
636             /* Notification of a complete release of wireless */
637             wmResult = WM_Finish();
638             if (WXCi_CheckWmApiResult(driver, WM_APIID_END, wmResult))
639             {
640                 work = NULL;
641                 WXCi_OnStateChanged(driver, WXC_DRIVER_STATE_END, NULL);
642             }
643         }
644     }
645 }
646 
647 /*---------------------------------------------------------------------------*
648   Name:         WXCi_ResetProc
649 
650   Description:  Successive transition from (any) -> IDLE.
651 
652   Arguments:    arg: Callback argument (specify NULL on invocation)
653 
654   Returns:      None.
655  *---------------------------------------------------------------------------*/
WXCi_ResetProc(void * arg)656 static void WXCi_ResetProc(void *arg)
657 {
658     WXCDriverWork *const driver = work;
659     WMCallback *cb = (WMCallback *)arg;
660 
661     if (!arg || WXCi_CheckWmCallbackResult(driver, arg))
662     {
663         WMErrCode wmResult = WM_ERRCODE_SUCCESS;
664         /* (any) -> IDLE */
665         if (!arg)
666         {
667             driver->state = WXC_DRIVER_STATE_BUSY;
668             wmResult = WM_Reset(WXCi_ResetProc);
669             (void)WXCi_CheckWmApiResult(driver, WM_APIID_RESET, wmResult);
670         }
671         /* End */
672         else if (cb->apiid == WM_APIID_RESET)
673         {
674             driver->own_aid = 0;
675             WXCi_OnStateChanged(driver, WXC_DRIVER_STATE_IDLE, NULL);
676         }
677     }
678 }
679 
680 /*---------------------------------------------------------------------------*
681   Name:         WXCi_ParentIndicate
682 
683   Description:  Parent device's StartParent indicator.
684 
685   Arguments:    arg: Callback argument
686 
687   Returns:      None.
688  *---------------------------------------------------------------------------*/
WXCi_ParentIndicate(void * arg)689 static void WXCi_ParentIndicate(void *arg)
690 {
691     WXCDriverWork *const driver = work;
692     WMStartParentCallback *cb = (WMStartParentCallback *)arg;
693 
694     /* When making just a state transition, ignore this and continue the sequence */
695     if (cb->state == WM_STATECODE_PARENT_START)
696     {
697         WXCi_StartParentProc(arg);
698     }
699     /* Otherwise, they're indicators, so hook to wxc_api.c */
700     else if (cb->errcode == WM_ERRCODE_SUCCESS)
701     {
702         switch (cb->state)
703         {
704         case WM_STATECODE_PARENT_START:
705             break;
706         case WM_STATECODE_CONNECTED:
707             {
708                 BOOL    mp_start = (driver->peer_bitmap == 0);
709                 WXC_DRIVER_LOG("connected(%02X+=%02X)\n", driver->peer_bitmap, (1 << cb->aid));
710                 driver->peer_bitmap |= (u16)(1 << cb->aid);
711                 (void)WXCi_CallDriverEvent(driver, WXC_DRIVER_EVENT_CONNECTED, cb);
712                 /* If it's the first child device, notify of the send timing */
713                 if (mp_start)
714                 {
715                     WXCi_CallSendEvent(driver);
716                 }
717             }
718             break;
719         case WM_STATECODE_DISCONNECTED_FROM_MYSELF:
720         case WM_STATECODE_DISCONNECTED:
721             /* Disconnect notification operations were unified in PortCallback */
722             break;
723         case WM_STATECODE_BEACON_SENT:
724             (void)WXCi_CallDriverEvent(driver, WXC_DRIVER_EVENT_BEACON_SEND, driver->parent_param);
725             break;
726         }
727     }
728 }
729 
730 /*---------------------------------------------------------------------------*
731   Name:         WXCi_StartParentProc
732 
733   Description:  Successive transition from IDLE -> PARENT -> MP_PARENT.
734 
735   Arguments:    arg: Callback argument (specify NULL on invocation)
736 
737   Returns:      None.
738  *---------------------------------------------------------------------------*/
WXCi_StartParentProc(void * arg)739 static void WXCi_StartParentProc(void *arg)
740 {
741     WXCDriverWork *const driver = work;
742     WMCallback *cb = (WMCallback *)arg;
743 
744     if (!arg || WXCi_CheckWmCallbackResult(driver, arg))
745     {
746         WMErrCode wmResult = WM_ERRCODE_SUCCESS;
747         /* IDLE -> IDLE (WM_SetParentParameter) */
748         if (!arg)
749         {
750             driver->state = WXC_DRIVER_STATE_BUSY;
751             /* Updates channels and startup information */
752             driver->parent_param->channel = driver->current_channel;
753             driver->parent_param->tgid = WM_GetNextTgid();
754             WXC_DRIVER_LOG("start parent. (%2dch, TGID=%02X, GGID=%04X)\n",
755                            driver->current_channel, driver->parent_param->tgid,
756                            driver->parent_param->ggid);
757             wmResult = WM_SetParentParameter(WXCi_StartParentProc, driver->parent_param);
758             (void)WXCi_CheckWmApiResult(driver, WM_APIID_SET_P_PARAM, wmResult);
759         }
760         /* IDLE -> PARENT */
761         else if (cb->apiid == WM_APIID_SET_P_PARAM)
762         {
763             /*
764              * Because of the indicator, redirect the callback to WXCi_ParentIndicate()
765              *
766              */
767             wmResult = WM_StartParent(WXCi_ParentIndicate);
768             (void)WXCi_CheckWmApiResult(driver, WM_APIID_START_PARENT, wmResult);
769         }
770         /* PARENT -> MP_PARENT */
771         else if (cb->apiid == WM_APIID_START_PARENT)
772         {
773             /*
774              * WM_STATECODE_PARENT_START is always the only notification sent from WXCi_ParentIndicate() here
775              *
776              */
777             driver->own_aid = 0;
778             driver->peer_bitmap = 0;
779             wmResult = WM_StartMP(WXCi_StartParentProc,
780                                   (u16 *)driver->mp_recv_work, driver->recv_size_max,
781                                   (u16 *)driver->mp_send_work, driver->send_size_max,
782                                   (u16)(driver->parent_param->CS_Flag ? 0 : 1));
783             (void)WXCi_CheckWmApiResult(driver, WM_APIID_START_MP, wmResult);
784         }
785         /* End */
786         else if (cb->apiid == WM_APIID_START_MP)
787         {
788             WMStartMPCallback *cb = (WMStartMPCallback *)arg;
789             switch (cb->state)
790             {
791             case WM_STATECODE_MP_START:
792                 WXCi_OnStateChanged(driver, WXC_DRIVER_STATE_PARENT, NULL);
793                 break;
794             }
795         }
796     }
797 }
798 
799 /*---------------------------------------------------------------------------*
800   Name:         WXCi_ChildIndicate
801 
802   Description:  Child device's StartConnect indicator.
803 
804   Arguments:    arg: Callback argument
805 
806   Returns:      None.
807  *---------------------------------------------------------------------------*/
WXCi_ChildIndicate(void * arg)808 static void WXCi_ChildIndicate(void *arg)
809 {
810     WXCDriverWork *const driver = work;
811 
812     if (WXCi_CheckWmCallbackResult(driver, arg))
813     {
814         WMStartConnectCallback *cb = (WMStartConnectCallback *)arg;
815         switch (cb->state)
816         {
817         case WM_STATECODE_CONNECT_START:
818         case WM_STATECODE_BEACON_LOST:
819             break;
820 
821         case WM_STATECODE_CONNECTED:
822             /* During state transitions, ignore this and continue the sequence */
823             if (driver->state != WXC_DRIVER_STATE_CHILD)
824             {
825                 WXCi_StartChildProc(arg);
826             }
827             break;
828 
829         case WM_STATECODE_DISCONNECTED_FROM_MYSELF:
830         case WM_STATECODE_DISCONNECTED:
831             /* If a transition is not currently in progress, reset here */
832             if (driver->state != WXC_DRIVER_STATE_BUSY)
833             {
834                 driver->target = WXC_DRIVER_STATE_PARENT;
835                 WXCi_ResetProc(NULL);
836             }
837             else
838             {
839                 driver->target = WXC_DRIVER_STATE_IDLE;
840             }
841             break;
842 
843         default:
844             WXCi_ErrorQuit(driver);
845             break;
846         }
847     }
848 }
849 
850 /*---------------------------------------------------------------------------*
851   Name:         WXCi_StartChildProc
852 
853   Description:  Successive transition from IDLE -> CHILD -> MP_CHILD.
854 
855   Arguments:    arg: Callback argument (specify NULL on invocation)
856 
857   Returns:      None.
858  *---------------------------------------------------------------------------*/
WXCi_StartChildProc(void * arg)859 static void WXCi_StartChildProc(void *arg)
860 {
861     WXCDriverWork *const driver = work;
862     WMCallback *cb = (WMCallback *)arg;
863 
864     if (!arg || WXCi_CheckWmCallbackResult(driver, arg))
865     {
866         WMErrCode wmResult = WM_ERRCODE_SUCCESS;
867         /* IDLE -> CHILD */
868         if (!arg)
869         {
870             /*
871              * This is a hook invocation in order to set the SSID and other information before connecting.
872              */
873             u8      ssid_bak[WM_SIZE_BSSID];
874             MI_CpuCopy8(driver->target_bss->ssid, ssid_bak, sizeof(ssid_bak));
875             (void)WXCi_CallDriverEvent(driver, WXC_DRIVER_EVENT_CONNECTING, driver->target_bss);
876             MI_CpuCopy8(driver->target_bss->ssid + 8, driver->ssid, WM_SIZE_CHILD_SSID);
877             MI_CpuCopy8(ssid_bak, driver->target_bss->ssid, sizeof(ssid_bak));
878             /*
879              * Because of the indicator, redirect the callback to WXCi_ChildIndicate()
880              *
881              */
882             driver->state = WXC_DRIVER_STATE_BUSY;
883             wmResult = WM_StartConnect(WXCi_ChildIndicate, driver->target_bss, driver->ssid);
884             (void)WXCi_CheckWmApiResult(driver, WM_APIID_START_CONNECT, wmResult);
885         }
886         /* CHILD -> MP_CHILD */
887         else if (cb->apiid == WM_APIID_START_CONNECT)
888         {
889             WMStartConnectCallback *cb = (WMStartConnectCallback *)arg;
890             /*
891              * WM_STATECODE_CONNECTED is always the only notification sent from WXCi_ChildIndicate() here
892              *
893              */
894             driver->own_aid = cb->aid;
895             wmResult = WM_StartMP(WXCi_StartChildProc,
896                                   (u16 *)driver->mp_recv_work, driver->recv_size_max,
897                                   (u16 *)driver->mp_send_work, driver->send_size_max,
898                                   (u16)(driver->parent_param->CS_Flag ? 0 : 1));
899             (void)WXCi_CheckWmApiResult(driver, WM_APIID_START_MP, wmResult);
900         }
901         /* End */
902         else if (cb->apiid == WM_APIID_START_MP)
903         {
904             WMStartMPCallback *cb = (WMStartMPCallback *)arg;
905             switch (cb->state)
906             {
907             case WM_STATECODE_MP_START:
908                 WXCi_OnStateChanged(driver, WXC_DRIVER_STATE_CHILD, cb);
909                 break;
910             }
911         }
912     }
913 }
914 
915 /*---------------------------------------------------------------------------*
916   Name:         WXCi_MeasureProc
917 
918   Description:  Successive transition from IDLE -> (measure) -> IDLE.
919 
920   Arguments:    arg: Callback argument (specify NULL on invocation)
921 
922   Returns:      None.
923  *---------------------------------------------------------------------------*/
WXCi_MeasureProc(void * arg)924 static void WXCi_MeasureProc(void *arg)
925 {
926     WXCDriverWork *const driver = work;
927     WMMeasureChannelCallback *cb = (WMMeasureChannelCallback *)arg;
928     u16     channel = 0;
929 
930     /* Initializes the measurement value */
931     if (!arg)
932     {
933         driver->state = WXC_DRIVER_STATE_BUSY;
934         driver->current_channel = 0;
935         driver->measure_ratio_min = WXC_MAX_RATIO + 1;
936     }
937     else if (WXCi_CheckWmCallbackResult(driver, cb))
938         /* Measurement complete callback */
939     {
940         channel = cb->channel;
941         /* Get a channel with a lower usage rate (initial value is 101%, so be sure to select the top) */
942         if (driver->measure_ratio_min > cb->ccaBusyRatio)
943         {
944             driver->measure_ratio_min = cb->ccaBusyRatio;
945             driver->current_channel = channel;
946         }
947         /* All channels measurement completed */
948         if (channel == (32 - MATH_CountLeadingZeros(WM_GetAllowedChannel())))
949         {
950             driver->need_measure_channel = FALSE;
951             /* If PARENT is still currently the target, transition to connection */
952             //WXCi_OnStateChanged(driver, WXC_DRIVER_STATE_IDLE, NULL);
953             WXCi_StartParentProc(NULL);
954             return;
955         }
956     }
957     /* Measurement failure (go to error shutdown) */
958     else
959     {
960         driver->need_measure_channel = FALSE;
961     }
962 
963     /* Measures all channels in order from the last */
964     if (driver->need_measure_channel)
965     {
966         /* The time interval in ms for picking up the signal for making one communication for one frame */
967         const u16 WH_MEASURE_TIME = 30;
968         /* Bitwise OR of the carrier sense and the ED value */
969         const u16 WH_MEASURE_CS_OR_ED = 3;
970         /* The recommended ED threshold value that has been empirically shown to be effective */
971         const u16 WH_MEASURE_ED_THRESHOLD = 17;
972         WMErrCode ret;
973 
974         channel = WXC_GetNextAllowedChannel(channel);
975         ret = WM_MeasureChannel(WXCi_MeasureProc,
976                                 WH_MEASURE_CS_OR_ED, WH_MEASURE_ED_THRESHOLD,
977                                 channel, WH_MEASURE_TIME);
978         (void)WXCi_CheckWmApiResult(driver, WM_APIID_MEASURE_CHANNEL, ret);
979     }
980 
981 }
982 
983 /*---------------------------------------------------------------------------*
984   Name:         WXCi_ScanProc
985 
986   Description:  Successive transition from IDLE -> SCAN -> IDLE.
987 
988   Arguments:    arg: Callback argument (specify NULL on invocation)
989 
990   Returns:      None.
991  *---------------------------------------------------------------------------*/
WXCi_ScanProc(void * arg)992 static void WXCi_ScanProc(void *arg)
993 {
994     WXCDriverWork *const driver = work;
995     WMCallback *cb = (WMCallback *)arg;
996 
997     static u16 scan_channelList;
998 
999     if (!arg || WXCi_CheckWmCallbackResult(driver, arg))
1000     {
1001         WMErrCode wmResult = WM_ERRCODE_SUCCESS;
1002 
1003         /* IDLE -> SCAN */
1004         if (!arg)
1005         {
1006             driver->state = WXC_DRIVER_STATE_BUSY;
1007             driver->scan_found_num = 0;
1008             /* Searches for all channels using the broadcast addresses */
1009             driver->scan_param->scanBuf = (WMBssDesc *)driver->scan_buf;
1010             driver->scan_param->scanBufSize = WM_SIZE_SCAN_EX_BUF;
1011 
1012             /* Set to single channel per search */
1013             scan_channelList = WM_GetAllowedChannel();
1014             driver->scan_param->channelList = (u16)MATH_GetLeastSignificantBit(scan_channelList);
1015             scan_channelList = (u16)(scan_channelList ^ MATH_GetLeastSignificantBit(scan_channelList));
1016 
1017             driver->scan_param->maxChannelTime = WXC_SCAN_TIME_MAX;
1018             MI_CpuFill8(driver->scan_param->bssid, 0xFF, sizeof(driver->scan_param->bssid));
1019             driver->scan_param->scanType = WM_SCANTYPE_PASSIVE;
1020             driver->scan_param->ssidLength = 0;
1021             MI_CpuFill8(driver->scan_param->ssid, 0xFF, sizeof(driver->scan_param->ssid));
1022             wmResult = WM_StartScanEx(WXCi_ScanProc, driver->scan_param);
1023             (void)WXCi_CheckWmApiResult(driver, WM_APIID_START_SCAN_EX, wmResult);
1024         }
1025         /* SCAN -> IDLE */
1026         else if (cb->apiid == WM_APIID_START_SCAN_EX)
1027         {
1028             WMStartScanExCallback *cb = (WMStartScanExCallback *)arg;
1029             /* Saves if the beacon is detected */
1030             if (cb->state == WM_STATECODE_PARENT_FOUND)
1031             {
1032                 DC_InvalidateRange(driver->scan_buf, WM_SIZE_SCAN_EX_BUF);
1033                 driver->scan_found_num = cb->bssDescCount;
1034             }
1035             wmResult = WM_EndScan(WXCi_ScanProc);
1036             (void)WXCi_CheckWmApiResult(driver, WM_APIID_END_SCAN, wmResult);
1037         }
1038         /* End */
1039         else if (cb->apiid == WM_APIID_END_SCAN)
1040         {
1041             BOOL    ret = FALSE;
1042             /* Evaluate BssDesc if the target is still CHILD */
1043             if (driver->target == WXC_DRIVER_STATE_CHILD)
1044             {
1045                 int     i;
1046                 const u8 *scan_buf = driver->scan_buf;
1047 
1048                 WXC_DRIVER_LOG("found:%d beacons\n", driver->scan_found_num);
1049                 for (i = 0; i < driver->scan_found_num; ++i)
1050                 {
1051                     const WMBssDesc *p_desc = (const WMBssDesc *)scan_buf;
1052                     const int len = p_desc->length * 2;
1053                     BOOL    is_valid;
1054                     is_valid = WM_IsValidGameBeacon(p_desc);
1055                     WXC_DRIVER_LOG("   GGID=%08X(%2dch:%3dBYTE)\n",
1056                                    is_valid ? p_desc->gameInfo.ggid : 0xFFFFFFFF,
1057                                    p_desc->channel, len);
1058                     if (is_valid)
1059                     {
1060                         /* Callback for each BssDesc */
1061                         ret =
1062                             (BOOL)WXCi_CallDriverEvent(driver, WXC_DRIVER_EVENT_BEACON_RECV,
1063                                                        (void *)p_desc);
1064                         if (ret)
1065                         {
1066                             WXC_DRIVER_LOG("     -> matched!\n");
1067                             MI_CpuCopy8(p_desc, driver->target_bss, sizeof(WMBssDesc));
1068                             break;
1069                         }
1070                     }
1071                     scan_buf += MATH_ROUNDUP(len, 4);
1072                 }
1073                 /* If the target is not yet decided and there are unsearched channels, search them. */
1074                 if((ret == FALSE)&&(MATH_GetLeastSignificantBit(scan_channelList) != 0))
1075                 {
1076                     driver->scan_found_num = 0;
1077                     driver->scan_param->channelList = (u16)MATH_GetLeastSignificantBit(scan_channelList);
1078                     scan_channelList = (u16)(scan_channelList ^ MATH_GetLeastSignificantBit(scan_channelList));
1079                     wmResult = WM_StartScanEx(WXCi_ScanProc, driver->scan_param);
1080                     (void)WXCi_CheckWmApiResult(driver, WM_APIID_START_SCAN_EX, wmResult);
1081                     return;
1082                 }
1083             }
1084             /* If there is a target, start connection */
1085             if (ret)
1086             {
1087                 WXCi_StartChildProc(NULL);
1088             }
1089             /* If there is none, select the next startup mode */
1090             else
1091             {
1092                 if (driver->target == WXC_DRIVER_STATE_CHILD)
1093                 {
1094                     driver->target = WXC_DRIVER_STATE_IDLE;
1095                 }
1096                 WXCi_OnStateChanged(driver, WXC_DRIVER_STATE_IDLE, NULL);
1097             }
1098         }
1099     }
1100 }
1101 
1102 /*---------------------------------------------------------------------------*
1103   Name:         WXC_InitDriver
1104 
1105   Description:  Initializes wireless and starts the transition to IDLE.
1106 
1107   Arguments:    driver: Used as an internal work memory buffer
1108                               Pointer to WXCDriverWork structure
1109                               This must be 32-byte aligned.
1110                 pp: Parent parameters
1111                 func: Event notification callback
1112                 dma: DMA channel assigned to wireless
1113 
1114   Returns:      None.
1115  *---------------------------------------------------------------------------*/
WXC_InitDriver(WXCDriverWork * driver,WMParentParam * pp,WXCDriverEventFunc func,u32 dma)1116 void WXC_InitDriver(WXCDriverWork * driver, WMParentParam *pp, WXCDriverEventFunc func, u32 dma)
1117 {
1118     /* Gets the initial TGID outside of the interrupt handler */
1119     {
1120         OSIntrMode bak_cpsr = OS_EnableInterrupts();
1121         (void)WM_GetNextTgid();
1122         (void)OS_RestoreInterrupts(bak_cpsr);
1123     }
1124     /* Initialize work variables */
1125     work = driver;
1126     MI_CpuClear32(driver, sizeof(WXCDriverWork));
1127     driver->own_aid = 0;
1128     driver->send_busy = TRUE;
1129     driver->callback = func;
1130     driver->wm_dma = (u16)dma;
1131     driver->send_size_max = (u16)sizeof(driver->mp_send_work);
1132     driver->recv_size_max = (u16)sizeof(driver->mp_recv_work);
1133     driver->state = WXC_DRIVER_STATE_END;
1134     driver->parent_param = pp;
1135     driver->parent_param->entryFlag = 1;
1136     driver->parent_param->beaconPeriod = WXC_BEACON_PERIOD;
1137     driver->parent_param->channel = 1;
1138 }
1139 
1140 /*---------------------------------------------------------------------------*
1141   Name:         WXC_SetDriverTarget
1142 
1143   Description:  Starts transition from a specific state to a target state.
1144 
1145   Arguments:    driver: WXCDriverWork structure
1146                 target: State of the transition target
1147 
1148   Returns:      None.
1149  *---------------------------------------------------------------------------*/
WXC_SetDriverTarget(WXCDriverWork * driver,WXCDriverState target)1150 void WXC_SetDriverTarget(WXCDriverWork * driver, WXCDriverState target)
1151 {
1152     driver->target = target;
1153     /* If the system is stable in some other state, start transition here */
1154     if ((driver->state != WXC_DRIVER_STATE_BUSY) && (driver->state != driver->target))
1155     {
1156         WXCi_OnStateChanged(driver, driver->state, NULL);
1157     }
1158 }
1159 
1160 /*---------------------------------------------------------------------------*
1161   Name:         WXC_SetDriverSsid
1162 
1163   Description:  Configures the SSID at connection.
1164 
1165   Arguments:    driver: WXCDriverWork structure
1166                 buffer: SSID data to configure
1167                 length: SSID data length
1168                               Must be no more than WM_SIZE_CHILD_SSID.
1169 
1170   Returns:      None.
1171  *---------------------------------------------------------------------------*/
WXC_SetDriverSsid(WXCDriverWork * driver,const void * buffer,u32 length)1172 void WXC_SetDriverSsid(WXCDriverWork * driver, const void *buffer, u32 length)
1173 {
1174     length = (u32)MATH_MIN(length, WM_SIZE_CHILD_SSID);
1175     MI_CpuCopy8(buffer, driver->ssid, length);
1176     MI_CpuFill8(driver->ssid + length, 0x00, (u32)(WM_SIZE_CHILD_SSID - length));
1177 }
1178 
1179 
1180 /*---------------------------------------------------------------------------*
1181   End of file
1182  *---------------------------------------------------------------------------*/
1183