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