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