/*---------------------------------------------------------------------------* Project: TwlSDK - WXC - libraries - File: wxc_driver.c Copyright 2005-2009 Nintendo. All rights reserved. These coded instructions, statements, and computer programs contain proprietary information of Nintendo of America Inc. and/or Nintendo Company Ltd., and are protected by Federal copyright law. They may not be disclosed to third parties or copied or duplicated in any form, in whole or in part, without the prior written consent of Nintendo. $Date:: 2008-12-16#$ $Rev: 9661 $ $Author: okubata_ryoma $ *---------------------------------------------------------------------------*/ #include #include #include /*****************************************************************************/ /* Declaration */ /* Error check function group */ static void WXCi_RecoverWmApiFailure(WXCDriverWork *, WMApiid id, WMErrCode err); static BOOL WXCi_CheckWmApiResult(WXCDriverWork * driver, WMApiid id, WMErrCode err); static BOOL WXCi_CheckWmCallbackResult(WXCDriverWork *, void *arg); static void WXCi_ErrorQuit(WXCDriverWork *); /* * Continuous transition procedure between wireless states. * It is first called with an argument of NULL and then makes repeated callbacks to the same function. * An event notification callback occurs when the intended state is reached. * * Note: * 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-. * * */ static void WXCi_InitProc(void *arg); /* (end) -> STOP */ static void WXCi_StartProc(void *arg); /* STOP -> IDLE */ static void WXCi_StopProc(void *arg); /* IDLE -> STOP */ static void WXCi_EndProc(void *arg); /* STOP -> (end) */ static void WXCi_ResetProc(void *arg); /* (any) -> IDLE */ static void WXCi_StartParentProc(void *arg); /* IDLE -> MP_PARENT */ static void WXCi_StartChildProc(void *arg); /* IDLE -> MP_CHILD */ static void WXCi_ScanProc(void *arg); /* IDLE -> SCAN -> IDLE */ static void WXCi_MeasureProc(void *arg); /* IDLE -> (measure) -> IDLE */ /* Status transition control */ static void WXCi_OnStateChanged(WXCDriverWork *, WXCDriverState state, void *arg); /* Other callbacks or indicators */ static void WXCi_IndicateCallback(void *arg); static void WXCi_PortCallback(void *arg); static void WXCi_MPSendCallback(void *arg); static void WXCi_ParentIndicate(void *arg); static void WXCi_ChildIndicate(void *arg); /*****************************************************************************/ /* Constants */ /* After SDK 3.0 RC, it was changed so that an indicator is generated even when disconnecting itself */ #define VERSION_TO_INT(major, minor, relstep) \ (((major) << 24) | ((minor) << 16) | ((relstep) << 0)) #if VERSION_TO_INT(SDK_VERSION_MAJOR, SDK_VERSION_MINOR, SDK_VERSION_RELSTEP) < VERSION_TO_INT(3, 0, 20100) #define WM_STATECODE_DISCONNECTED_FROM_MYSELF ((WMStateCode)26) #endif /*****************************************************************************/ /* Variables */ /* * Work memory for the wireless driver * - This variable has been provided to use as "this" only in WM callbacks. * (Normally, WXCDriverWork is taken as an argument to specify the invocation target whenever possible.) * - 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. * */ static WXCDriverWork *work; /*****************************************************************************/ /* Functions */ /*---------------------------------------------------------------------------* Name: WXCi_ErrorQuit Description: Reset operation when an error is detected. Arguments: driver: WXCDriverWork structure Returns: None. *---------------------------------------------------------------------------*/ static void WXCi_ErrorQuit(WXCDriverWork * driver) { /* It usually becomes an unusual finish operation, so BUSY is ignored */ if (driver->state == WXC_DRIVER_STATE_BUSY) { driver->state = driver->target; } WXCi_End(driver); } /*---------------------------------------------------------------------------* Name: WXCi_RecoverWmApiFailure Description: Attempts to recover from the WM function's error. Arguments: driver: WXCDriverWork structure id: WM function types err: Error code returned from the function Returns: None. *---------------------------------------------------------------------------*/ static void WXCi_RecoverWmApiFailure(WXCDriverWork * driver, WMApiid id, WMErrCode err) { /* Just in case, success is also determined here */ if (err == WM_ERRCODE_SUCCESS) { return; } switch (id) { default: /* Functions which should not be used in WXC */ OS_TWarning("WXC library error (unknown WM API : %d)\n", id); WXCi_ErrorQuit(driver); break; case WM_APIID_ENABLE: case WM_APIID_POWER_ON: case WM_APIID_POWER_OFF: case WM_APIID_DISABLE: case WM_APIID_RESET: /* Ends if it failed to even initialize */ WXCi_ErrorQuit(driver); break; case WM_APIID_MEASURE_CHANNEL: case WM_APIID_SET_P_PARAM: case WM_APIID_START_PARENT: case WM_APIID_START_MP: case WM_APIID_START_SCAN_EX: case WM_APIID_END_SCAN: case WM_APIID_DISCONNECT: /* Ends when an unexpected failure occurs with major status transition functions */ WXCi_ErrorQuit(driver); break; case WM_APIID_START_CONNECT: /* Connection failure when StartConnect ends is possible, so it goes to IDLE again */ if ((err == WM_ERRCODE_FAILED) || (err == WM_ERRCODE_NO_ENTRY) || (err == WM_ERRCODE_OVER_MAX_ENTRY)) { /* Only for this moment, BUSY is released */ driver->state = WXC_DRIVER_STATE_CHILD; WXCi_Reset(driver); } /* Everything else is determined as a fatal error, and it ends */ else { WXCi_ErrorQuit(driver); } break; case WM_APIID_INDICATION: /* Ends when a fatal notification such as FIFO_ERROR or FLASH_ERROR is received */ WXCi_ErrorQuit(driver); break; case WM_APIID_SET_MP_DATA: case WM_APIID_PORT_SEND: /* * The following are cases in which SetMPData fails. * ILLEGAL_STATE,INVALID_PARAM,FIFO_ERROR: Always * NO_CHILD: When the function is called * SEND_QUEUE_FULL,SEND_FAILED: During a callback * This problem is resolved by a retry and is ignored. */ break; } } /*---------------------------------------------------------------------------* Name: WXCi_CheckWmApiResult Description: Determines the call return value of WM functions. Arguments: driver: WXCDriverWork Structure id: WM function types err: Error code returned from the function Returns: If WM_ERRCODE_SUCCESS: TRUE. Otherwise, output error: FALSE. *---------------------------------------------------------------------------*/ static BOOL WXCi_CheckWmApiResult(WXCDriverWork * driver, WMApiid id, WMErrCode err) { BOOL ret = WXC_CheckWmApiResult(id, err); /* Return operation for each API when there is an error */ if (!ret) { WXCi_RecoverWmApiFailure(driver, id, err); } return ret; } /*---------------------------------------------------------------------------* Name: WXCi_CheckWmCallbackResult Description: Determines the return value of WM callbacks. Arguments: driver: WXCDriverWork structure arg: Argument returned from the function Returns: If WM_ERRCODE_SUCCESS: TRUE. Otherwise, output error: FALSE. *---------------------------------------------------------------------------*/ static BOOL WXCi_CheckWmCallbackResult(WXCDriverWork * driver, void *arg) { BOOL ret = WXC_CheckWmCallbackResult(arg); /* Return operation for each API when there is an error */ if (!ret) { const WMCallback *cb = (const WMCallback *)arg; WXCi_RecoverWmApiFailure(driver, (WMApiid)cb->apiid, (WMErrCode)cb->errcode); } return ret; } /*---------------------------------------------------------------------------* Name: WXCi_CallDriverEvent Description: Notifies of a wireless driver event. Arguments: driver: WXCDriverWork Structure event: Notified event arg: Function assigned to each event Returns: u32 event result value assigned to each event. *---------------------------------------------------------------------------*/ static inline u32 WXCi_CallDriverEvent(WXCDriverWork * driver, WXCDriverEvent event, void *arg) { u32 result = 0; if (driver->callback) { result = (*driver->callback) (event, arg); } return result; } /*---------------------------------------------------------------------------* Name: WXCi_CallSendEvent Description: Callback function for the data send callback event. Arguments: driver: WXCDriverWork structure Returns: None. *---------------------------------------------------------------------------*/ static void WXCi_CallSendEvent(WXCDriverWork * driver) { if (!driver->send_busy) { /* * Set information for the send packet buffer * TODO: * Would this be more efficient if it saved once when MP communications are established? * The same process actually exists in wxc_api.c. */ const u16 max_length = (u16)((driver->own_aid == 0) ? driver->parent_param->parentMaxSize : driver->target_bss->gameInfo.childMaxSize); WXCPacketInfo packet; packet.bitmap = driver->peer_bitmap; packet.length = max_length; packet.buffer = driver->current_send_buf; /* Uses the user settings on send data */ (void)WXCi_CallDriverEvent(driver, WXC_DRIVER_EVENT_DATA_SEND, &packet); /* Send data */ if (packet.length <= max_length) { WMErrCode ret; ret = WM_SetMPDataToPort(WXCi_MPSendCallback, (u16 *)packet.buffer, packet.length, packet.bitmap, WXC_DEFAULT_PORT, WXC_DEFAULT_PORT_PRIO); driver->send_busy = WXCi_CheckWmApiResult(driver, WM_APIID_SET_MP_DATA, ret); } } } /*---------------------------------------------------------------------------* Name: WXCi_OnStateChanged Description: Operation when it stabilizes in the specified state. Arguments: driver: WXCDriverWork structure state: Transition is the defined state arg: Argument for each event Returns: u32 event result value assigned to each event. *---------------------------------------------------------------------------*/ static void WXCi_OnStateChanged(WXCDriverWork * driver, WXCDriverState state, void *arg) { driver->state = state; /* Notify the user if the current state is the transition target */ if (driver->target == state) { switch (state) { case WXC_DRIVER_STATE_END: (void)WXCi_CallDriverEvent(driver, WXC_DRIVER_EVENT_STATE_END, NULL); break; case WXC_DRIVER_STATE_STOP: (void)WXCi_CallDriverEvent(driver, WXC_DRIVER_EVENT_STATE_STOP, NULL); break; case WXC_DRIVER_STATE_IDLE: (void)WXCi_CallDriverEvent(driver, WXC_DRIVER_EVENT_STATE_IDLE, NULL); break; case WXC_DRIVER_STATE_PARENT: driver->send_busy = FALSE; (void)WXCi_CallDriverEvent(driver, WXC_DRIVER_EVENT_STATE_PARENT, NULL); break; case WXC_DRIVER_STATE_CHILD: driver->send_busy = FALSE; (void)WXCi_CallDriverEvent(driver, WXC_DRIVER_EVENT_STATE_CHILD, NULL); /* Here, the connection with the parent is notified */ driver->peer_bitmap |= (u16)(1 << 0); (void)WXCi_CallDriverEvent(driver, WXC_DRIVER_EVENT_CONNECTED, arg); WXCi_CallSendEvent(driver); break; } } /* * Otherwise, continue transitioning * +-----+ +------+ +------+ +--------+ * | > Init | > Start > > Measure/StartParent > PARENT | * | | | | | < Reset < | * | | | | | | +--------+ * | END | | STOP | | IDLE > Scan/StartChild > CHILD | * | | | | | < Reset < | * | | | < Stop < | +--------+ * | < End < < End < | * +-----+ +------+ +------+ */ else { switch (state) { case WXC_DRIVER_STATE_END: WXCi_InitProc(NULL); break; case WXC_DRIVER_STATE_STOP: switch (driver->target) { case WXC_DRIVER_STATE_END: WXCi_EndProc(NULL); break; case WXC_DRIVER_STATE_IDLE: case WXC_DRIVER_STATE_PARENT: case WXC_DRIVER_STATE_CHILD: WXCi_StartProc(NULL); break; } break; case WXC_DRIVER_STATE_IDLE: switch (driver->target) { case WXC_DRIVER_STATE_END: case WXC_DRIVER_STATE_STOP: WXCi_StopProc(NULL); break; case WXC_DRIVER_STATE_PARENT: driver->need_measure_channel = TRUE; if (driver->need_measure_channel) { WXCi_MeasureProc(NULL); } break; case WXC_DRIVER_STATE_CHILD: WXCi_ScanProc(NULL); break; } break; case WXC_DRIVER_STATE_PARENT: case WXC_DRIVER_STATE_CHILD: WXCi_ResetProc(NULL); break; } } } /*---------------------------------------------------------------------------* Name: WXCi_MPSendCallback Description: The callback function to WM_SetMPData. Arguments: arg: Pointer to the callback structure Returns: None. *---------------------------------------------------------------------------*/ static void WXCi_MPSendCallback(void *arg) { WXCDriverWork *const driver = work; (void)WXCi_CheckWmCallbackResult(driver, arg); driver->send_busy = FALSE; if (driver->peer_bitmap != 0) { WXCi_CallSendEvent(driver); } } /*---------------------------------------------------------------------------* Name: WXCi_IndicateCallback Description: The callback function called when Indicate occurs. Arguments: arg: Pointer to the callback structure Returns: None. *---------------------------------------------------------------------------*/ static void WXCi_IndicateCallback(void *arg) { /* TODO: Can't this be integrated with CheckError? */ WXCDriverWork *const driver = work; WMIndCallback *cb = (WMIndCallback *)arg; if (cb->errcode == WM_ERRCODE_FIFO_ERROR) { WXC_DRIVER_LOG("WM_ERRCODE_FIFO_ERROR Indication!\n"); /* Unrecoverable error */ driver->target = WXC_DRIVER_STATE_ERROR; driver->state = WXC_DRIVER_STATE_ERROR; } } /*---------------------------------------------------------------------------* Name: WXCi_PortCallback Description: The reception notification to the port. Arguments: arg: Pointer to the callback structure Returns: None. *---------------------------------------------------------------------------*/ static void WXCi_PortCallback(void *arg) { WXCDriverWork *const driver = work; if (WXCi_CheckWmCallbackResult(driver, arg)) { WMPortRecvCallback *cb = (WMPortRecvCallback *)arg; switch (cb->state) { case WM_STATECODE_PORT_RECV: { WXCPacketInfo packet; packet.bitmap = (u16)(1 << cb->aid); packet.length = cb->length; packet.buffer = (u8 *)cb->data; (void)WXCi_CallDriverEvent(driver, WXC_DRIVER_EVENT_DATA_RECV, &packet); } break; case WM_STATECODE_CONNECTED: break; case WM_STATECODE_DISCONNECTED_FROM_MYSELF: case WM_STATECODE_DISCONNECTED: WXC_DRIVER_LOG("disconnected(%02X-=%02X)\n", driver->peer_bitmap, (1 << cb->aid)); driver->peer_bitmap &= (u16)~(1 << cb->aid); (void)WXCi_CallDriverEvent(driver, WXC_DRIVER_EVENT_DISCONNECTED, (void *)(1 << cb->aid)); break; } } } /*---------------------------------------------------------------------------* Name: WXCi_InitProc Description: READY -> Continues transition to STOP. Arguments: arg: Callback argument (specify NULL on invocation) Returns: None. *---------------------------------------------------------------------------*/ static void WXCi_InitProc(void *arg) { WXCDriverWork *const driver = work; WMCallback *cb = (WMCallback *)arg; if (!arg || WXCi_CheckWmCallbackResult(driver, arg)) { WMErrCode wmResult = WM_ERRCODE_SUCCESS; /* (outof) -> STOP */ if (!arg) { /* Currently, MeasureChannel only runs once at startup */ driver->need_measure_channel = TRUE; driver->state = WXC_DRIVER_STATE_BUSY; wmResult = WM_Init(driver->wm_work, driver->wm_dma); (void)WXCi_CheckWmApiResult(driver, WM_APIID_INITIALIZE, wmResult); wmResult = WM_Enable(WXCi_InitProc); (void)WXCi_CheckWmApiResult(driver, WM_APIID_ENABLE, wmResult); } /* End */ else if (cb->apiid == WM_APIID_ENABLE) { /* Receive each type of notification */ wmResult = WM_SetIndCallback(WXCi_IndicateCallback); if (WXCi_CheckWmApiResult(driver, WM_APIID_INDICATION, wmResult)) { /* Sets the port callback */ wmResult = WM_SetPortCallback(WXC_DEFAULT_PORT, WXCi_PortCallback, NULL); if (WXCi_CheckWmApiResult(driver, WM_APIID_PORT_SEND, wmResult)) { WXCi_OnStateChanged(driver, WXC_DRIVER_STATE_STOP, NULL); } } } } } /*---------------------------------------------------------------------------* Name: WXCi_StartProc Description: STOP -> Continues transition to IDLE. Arguments: arg: Callback argument (specify NULL on invocation) Returns: None. *---------------------------------------------------------------------------*/ static void WXCi_StartProc(void *arg) { WXCDriverWork *const driver = work; WMCallback *cb = (WMCallback *)arg; if (!arg || WXCi_CheckWmCallbackResult(driver, arg)) { WMErrCode wmResult = WM_ERRCODE_SUCCESS; /* STOP -> IDLE */ if (!arg) { driver->state = WXC_DRIVER_STATE_BUSY; wmResult = WM_PowerOn(WXCi_StartProc); (void)WXCi_CheckWmApiResult(driver, WM_APIID_POWER_ON, wmResult); } /* End */ else if (cb->apiid == WM_APIID_POWER_ON) { WXCi_OnStateChanged(driver, WXC_DRIVER_STATE_IDLE, NULL); } } } /*---------------------------------------------------------------------------* Name: WXCi_StopProc Description: IDLE -> Continues transition to STOP. Arguments: arg: Callback argument (specify NULL on invocation) Returns: None. *---------------------------------------------------------------------------*/ static void WXCi_StopProc(void *arg) { WXCDriverWork *const driver = work; WMCallback *cb = (WMCallback *)arg; if (!arg || WXCi_CheckWmCallbackResult(driver, arg)) { WMErrCode wmResult = WM_ERRCODE_SUCCESS; /* IDLE -> STOP */ if (!arg) { driver->state = WXC_DRIVER_STATE_BUSY; wmResult = WM_PowerOff(WXCi_StopProc); (void)WXCi_CheckWmApiResult(driver, WM_APIID_POWER_OFF, wmResult); } /* End */ else if (cb->apiid == WM_APIID_POWER_OFF) { WXCi_OnStateChanged(driver, WXC_DRIVER_STATE_STOP, NULL); } } } /*---------------------------------------------------------------------------* Name: WXCi_EndProc Description: STOP -> Continues transition to READY. Arguments: arg: Callback argument (specify NULL on invocation) Returns: None. *---------------------------------------------------------------------------*/ static void WXCi_EndProc(void *arg) { WXCDriverWork *const driver = work; WMCallback *cb = (WMCallback *)arg; if (!arg || WXCi_CheckWmCallbackResult(driver, arg)) { WMErrCode wmResult = WM_ERRCODE_SUCCESS; /* STOP -> READY */ if (!arg) { driver->state = WXC_DRIVER_STATE_BUSY; wmResult = WM_Disable(WXCi_EndProc); (void)WXCi_CheckWmApiResult(driver, WM_APIID_DISABLE, wmResult); } /* End */ else if (cb->apiid == WM_APIID_DISABLE) { /* Notification of a complete release of wireless */ wmResult = WM_Finish(); if (WXCi_CheckWmApiResult(driver, WM_APIID_END, wmResult)) { work = NULL; WXCi_OnStateChanged(driver, WXC_DRIVER_STATE_END, NULL); } } } } /*---------------------------------------------------------------------------* Name: WXCi_ResetProc Description: (any) -> Continues transition to IDLE. Arguments: arg: Callback argument (specify NULL on invocation) Returns: None. *---------------------------------------------------------------------------*/ static void WXCi_ResetProc(void *arg) { WXCDriverWork *const driver = work; WMCallback *cb = (WMCallback *)arg; if (!arg || WXCi_CheckWmCallbackResult(driver, arg)) { WMErrCode wmResult = WM_ERRCODE_SUCCESS; /* (any) -> IDLE */ if (!arg) { driver->state = WXC_DRIVER_STATE_BUSY; wmResult = WM_Reset(WXCi_ResetProc); (void)WXCi_CheckWmApiResult(driver, WM_APIID_RESET, wmResult); } /* End */ else if (cb->apiid == WM_APIID_RESET) { driver->own_aid = 0; WXCi_OnStateChanged(driver, WXC_DRIVER_STATE_IDLE, NULL); } } } /*---------------------------------------------------------------------------* Name: WXCi_ParentIndicate Description: Parent device's StartParent indicator. Arguments: arg: Callback argument Returns: None. *---------------------------------------------------------------------------*/ static void WXCi_ParentIndicate(void *arg) { WXCDriverWork *const driver = work; WMStartParentCallback *cb = (WMStartParentCallback *)arg; /* When making single status transitions, ignore this and continue the sequence */ if (cb->state == WM_STATECODE_PARENT_START) { WXCi_StartParentProc(arg); } /* Otherwise, they're indicators, so hook to wxc_api.c */ else if (cb->errcode == WM_ERRCODE_SUCCESS) { switch (cb->state) { case WM_STATECODE_PARENT_START: break; case WM_STATECODE_CONNECTED: { BOOL mp_start = (driver->peer_bitmap == 0); WXC_DRIVER_LOG("connected(%02X+=%02X)\n", driver->peer_bitmap, (1 << cb->aid)); driver->peer_bitmap |= (u16)(1 << cb->aid); (void)WXCi_CallDriverEvent(driver, WXC_DRIVER_EVENT_CONNECTED, cb); /* If it's the first child device, notify the startup timing */ if (mp_start) { WXCi_CallSendEvent(driver); } } break; case WM_STATECODE_DISCONNECTED_FROM_MYSELF: case WM_STATECODE_DISCONNECTED: /* Disconnect notification operations were unified in PortCallback */ break; case WM_STATECODE_BEACON_SENT: (void)WXCi_CallDriverEvent(driver, WXC_DRIVER_EVENT_BEACON_SEND, driver->parent_param); break; } } } /*---------------------------------------------------------------------------* Name: WXCi_StartParentProc Description: IDLE -> PARENT -> Continues transition to MP_PARENT. Arguments: arg: Callback argument (specify NULL on invocation) Returns: None. *---------------------------------------------------------------------------*/ static void WXCi_StartParentProc(void *arg) { WXCDriverWork *const driver = work; WMCallback *cb = (WMCallback *)arg; if (!arg || WXCi_CheckWmCallbackResult(driver, arg)) { WMErrCode wmResult = WM_ERRCODE_SUCCESS; /* IDLE -> IDLE (WM_SetParentParameter) */ if (!arg) { driver->state = WXC_DRIVER_STATE_BUSY; /* Updates channels and startup information */ driver->parent_param->channel = driver->current_channel; driver->parent_param->tgid = WM_GetNextTgid(); WXC_DRIVER_LOG("start parent. (%2dch, TGID=%02X, GGID=%04X)\n", driver->current_channel, driver->parent_param->tgid, driver->parent_param->ggid); wmResult = WM_SetParentParameter(WXCi_StartParentProc, driver->parent_param); (void)WXCi_CheckWmApiResult(driver, WM_APIID_SET_P_PARAM, wmResult); } /* IDLE -> PARENT */ else if (cb->apiid == WM_APIID_SET_P_PARAM) { /* * Because of the indicator, redirect the callback to WXCi_ParentIndicate() * */ wmResult = WM_StartParent(WXCi_ParentIndicate); (void)WXCi_CheckWmApiResult(driver, WM_APIID_START_PARENT, wmResult); } /* PARENT -> MP_PARENT */ else if (cb->apiid == WM_APIID_START_PARENT) { /* * WM_STATECODE_PARENT_START is always the only notification sent from WXCi_ParentIndicate() here * */ driver->own_aid = 0; driver->peer_bitmap = 0; wmResult = WM_StartMP(WXCi_StartParentProc, (u16 *)driver->mp_recv_work, driver->recv_size_max, (u16 *)driver->mp_send_work, driver->send_size_max, (u16)(driver->parent_param->CS_Flag ? 0 : 1)); (void)WXCi_CheckWmApiResult(driver, WM_APIID_START_MP, wmResult); } /* End */ else if (cb->apiid == WM_APIID_START_MP) { WMStartMPCallback *cb = (WMStartMPCallback *)arg; switch (cb->state) { case WM_STATECODE_MP_START: WXCi_OnStateChanged(driver, WXC_DRIVER_STATE_PARENT, NULL); break; } } } } /*---------------------------------------------------------------------------* Name: WXCi_ChildIndicate Description: Child device's StartConnect indicator. Arguments: arg: Callback argument Returns: None. *---------------------------------------------------------------------------*/ static void WXCi_ChildIndicate(void *arg) { WXCDriverWork *const driver = work; if (WXCi_CheckWmCallbackResult(driver, arg)) { WMStartConnectCallback *cb = (WMStartConnectCallback *)arg; switch (cb->state) { case WM_STATECODE_CONNECT_START: case WM_STATECODE_BEACON_LOST: break; case WM_STATECODE_CONNECTED: /* When making status transitions, ignore this and continue the sequence */ if (driver->state != WXC_DRIVER_STATE_CHILD) { WXCi_StartChildProc(arg); } break; case WM_STATECODE_DISCONNECTED_FROM_MYSELF: case WM_STATECODE_DISCONNECTED: /* If not in a transition, it resets here */ if (driver->state != WXC_DRIVER_STATE_BUSY) { driver->target = WXC_DRIVER_STATE_PARENT; WXCi_ResetProc(NULL); } else { driver->target = WXC_DRIVER_STATE_IDLE; } break; default: WXCi_ErrorQuit(driver); break; } } } /*---------------------------------------------------------------------------* Name: WXCi_StartChildProc Description: IDLE -> CHILD -> Continues transition to MP_CHILD. Arguments: arg: Callback argument (specify NULL on invocation) Returns: None. *---------------------------------------------------------------------------*/ static void WXCi_StartChildProc(void *arg) { WXCDriverWork *const driver = work; WMCallback *cb = (WMCallback *)arg; if (!arg || WXCi_CheckWmCallbackResult(driver, arg)) { WMErrCode wmResult = WM_ERRCODE_SUCCESS; /* IDLE -> CHILD */ if (!arg) { /* * Hook invocation to set the SSID and other information before connecting */ u8 ssid_bak[WM_SIZE_BSSID]; MI_CpuCopy8(driver->target_bss->ssid, ssid_bak, sizeof(ssid_bak)); (void)WXCi_CallDriverEvent(driver, WXC_DRIVER_EVENT_CONNECTING, driver->target_bss); MI_CpuCopy8(driver->target_bss->ssid + 8, driver->ssid, WM_SIZE_CHILD_SSID); MI_CpuCopy8(ssid_bak, driver->target_bss->ssid, sizeof(ssid_bak)); /* * Because of the indicator, redirect the callback to WXCi_ChildIndicate() * */ driver->state = WXC_DRIVER_STATE_BUSY; wmResult = WM_StartConnect(WXCi_ChildIndicate, driver->target_bss, driver->ssid); (void)WXCi_CheckWmApiResult(driver, WM_APIID_START_CONNECT, wmResult); } /* CHILD -> MP_CHILD */ else if (cb->apiid == WM_APIID_START_CONNECT) { WMStartConnectCallback *cb = (WMStartConnectCallback *)arg; /* * WM_STATECODE_CONNECTED is always the only notification sent from WXCi_ChildIndicate() here * */ driver->own_aid = cb->aid; wmResult = WM_StartMP(WXCi_StartChildProc, (u16 *)driver->mp_recv_work, driver->recv_size_max, (u16 *)driver->mp_send_work, driver->send_size_max, (u16)(driver->parent_param->CS_Flag ? 0 : 1)); (void)WXCi_CheckWmApiResult(driver, WM_APIID_START_MP, wmResult); } /* End */ else if (cb->apiid == WM_APIID_START_MP) { WMStartMPCallback *cb = (WMStartMPCallback *)arg; switch (cb->state) { case WM_STATECODE_MP_START: WXCi_OnStateChanged(driver, WXC_DRIVER_STATE_CHILD, cb); break; } } } } /*---------------------------------------------------------------------------* Name: WXCi_MeasureProc Description: IDLE -> (measure) -> Continues transition to IDLE. Arguments: arg: Callback argument (specify NULL on invocation) Returns: None. *---------------------------------------------------------------------------*/ static void WXCi_MeasureProc(void *arg) { WXCDriverWork *const driver = work; WMMeasureChannelCallback *cb = (WMMeasureChannelCallback *)arg; u16 channel = 0; /* Initializes the measurement value */ if (!arg) { driver->state = WXC_DRIVER_STATE_BUSY; driver->current_channel = 0; driver->measure_ratio_min = WXC_MAX_RATIO + 1; } else if (WXCi_CheckWmCallbackResult(driver, cb)) /* Measurement complete callback */ { channel = cb->channel; /* Get a channel with a lower usage rate (initial value is 101%, so be sure to select the top) */ if (driver->measure_ratio_min > cb->ccaBusyRatio) { driver->measure_ratio_min = cb->ccaBusyRatio; driver->current_channel = channel; } /* All channels measurement completed */ if (channel == (32 - MATH_CountLeadingZeros(WM_GetAllowedChannel()))) { driver->need_measure_channel = FALSE; /* If PARENT is currently the target, it transitions to connection */ //WXCi_OnStateChanged(driver, WXC_DRIVER_STATE_IDLE, NULL); WXCi_StartParentProc(NULL); return; } } /* Measurement failure (to error end) */ else { driver->need_measure_channel = FALSE; } /* Measures all channels in order from the last */ if (driver->need_measure_channel) { /* The time interval in ms for picking up the signal for making one communication for one frame */ const u16 WH_MEASURE_TIME = 30; /* The logical OR of the carrier sense and the ED value */ const u16 WH_MEASURE_CS_OR_ED = 3; /* The recommended ED threshold value that has been empirically shown to be effective */ const u16 WH_MEASURE_ED_THRESHOLD = 17; WMErrCode ret; channel = WXC_GetNextAllowedChannel(channel); ret = WM_MeasureChannel(WXCi_MeasureProc, WH_MEASURE_CS_OR_ED, WH_MEASURE_ED_THRESHOLD, channel, WH_MEASURE_TIME); (void)WXCi_CheckWmApiResult(driver, WM_APIID_MEASURE_CHANNEL, ret); } } /*---------------------------------------------------------------------------* Name: WXCi_ScanProc Description: IDLE -> SCAN -> Continues transition to IDLE. Arguments: arg: Callback argument (specify NULL on invocation) Returns: None. *---------------------------------------------------------------------------*/ static void WXCi_ScanProc(void *arg) { WXCDriverWork *const driver = work; WMCallback *cb = (WMCallback *)arg; static u16 scan_channelList; if (!arg || WXCi_CheckWmCallbackResult(driver, arg)) { WMErrCode wmResult = WM_ERRCODE_SUCCESS; /* IDLE -> SCAN */ if (!arg) { driver->state = WXC_DRIVER_STATE_BUSY; driver->scan_found_num = 0; /* Searches for all channels using the broadcast addresses */ driver->scan_param->scanBuf = (WMBssDesc *)driver->scan_buf; driver->scan_param->scanBufSize = WM_SIZE_SCAN_EX_BUF; /* Set to single channel per search */ scan_channelList = WM_GetAllowedChannel(); driver->scan_param->channelList = (u16)MATH_GetLeastSignificantBit(scan_channelList); scan_channelList = (u16)(scan_channelList ^ MATH_GetLeastSignificantBit(scan_channelList)); driver->scan_param->maxChannelTime = WXC_SCAN_TIME_MAX; MI_CpuFill8(driver->scan_param->bssid, 0xFF, sizeof(driver->scan_param->bssid)); driver->scan_param->scanType = WM_SCANTYPE_PASSIVE; driver->scan_param->ssidLength = 0; MI_CpuFill8(driver->scan_param->ssid, 0xFF, sizeof(driver->scan_param->ssid)); wmResult = WM_StartScanEx(WXCi_ScanProc, driver->scan_param); (void)WXCi_CheckWmApiResult(driver, WM_APIID_START_SCAN_EX, wmResult); } /* SCAN -> IDLE */ else if (cb->apiid == WM_APIID_START_SCAN_EX) { WMStartScanExCallback *cb = (WMStartScanExCallback *)arg; /* Saves if the beacon is detected */ if (cb->state == WM_STATECODE_PARENT_FOUND) { DC_InvalidateRange(driver->scan_buf, WM_SIZE_SCAN_EX_BUF); driver->scan_found_num = cb->bssDescCount; } wmResult = WM_EndScan(WXCi_ScanProc); (void)WXCi_CheckWmApiResult(driver, WM_APIID_END_SCAN, wmResult); } /* End */ else if (cb->apiid == WM_APIID_END_SCAN) { BOOL ret = FALSE; /* Determines BssDesc if the target is still CHILD */ if (driver->target == WXC_DRIVER_STATE_CHILD) { int i; const u8 *scan_buf = driver->scan_buf; WXC_DRIVER_LOG("found:%d beacons\n", driver->scan_found_num); for (i = 0; i < driver->scan_found_num; ++i) { const WMBssDesc *p_desc = (const WMBssDesc *)scan_buf; const int len = p_desc->length * 2; BOOL is_valid; is_valid = WM_IsValidGameBeacon(p_desc); WXC_DRIVER_LOG(" GGID=%08X(%2dch:%3dBYTE)\n", is_valid ? p_desc->gameInfo.ggid : 0xFFFFFFFF, p_desc->channel, len); if (is_valid) { /* Callback for each BssDesc */ ret = (BOOL)WXCi_CallDriverEvent(driver, WXC_DRIVER_EVENT_BEACON_RECV, (void *)p_desc); if (ret) { WXC_DRIVER_LOG(" -> matched!\n"); MI_CpuCopy8(p_desc, driver->target_bss, sizeof(WMBssDesc)); break; } } scan_buf += MATH_ROUNDUP(len, 4); } /* The target is not yet determined. Search if there is still an unsearched channel */ if((ret == FALSE)&&(MATH_GetLeastSignificantBit(scan_channelList) != 0)) { driver->scan_found_num = 0; driver->scan_param->channelList = (u16)MATH_GetLeastSignificantBit(scan_channelList); scan_channelList = (u16)(scan_channelList ^ MATH_GetLeastSignificantBit(scan_channelList)); wmResult = WM_StartScanEx(WXCi_ScanProc, driver->scan_param); (void)WXCi_CheckWmApiResult(driver, WM_APIID_START_SCAN_EX, wmResult); return; } } /* If there is a target, a connection is started */ if (ret) { WXCi_StartChildProc(NULL); } /* If there is no target, it selects the next mode */ else { if (driver->target == WXC_DRIVER_STATE_CHILD) { driver->target = WXC_DRIVER_STATE_IDLE; } WXCi_OnStateChanged(driver, WXC_DRIVER_STATE_IDLE, NULL); } } } } /*---------------------------------------------------------------------------* Name: WXC_InitDriver Description: Initializes wireless and starts the transition to IDLE. Arguments: driver: Used as an internal work memory buffer Pointer to WXCDriverWork structure This must be 32-byte aligned. pp: Parent parameters func: Event notification callback dma: DMA channel assigned to wireless Returns: None. *---------------------------------------------------------------------------*/ void WXC_InitDriver(WXCDriverWork * driver, WMParentParam *pp, WXCDriverEventFunc func, u32 dma) { /* Gets the first TGID outside of the interrupt handler */ { OSIntrMode bak_cpsr = OS_EnableInterrupts(); (void)WM_GetNextTgid(); (void)OS_RestoreInterrupts(bak_cpsr); } /* Work variable initialization */ work = driver; MI_CpuClear32(driver, sizeof(WXCDriverWork)); driver->own_aid = 0; driver->send_busy = TRUE; driver->callback = func; driver->wm_dma = (u16)dma; driver->send_size_max = (u16)sizeof(driver->mp_send_work); driver->recv_size_max = (u16)sizeof(driver->mp_recv_work); driver->state = WXC_DRIVER_STATE_END; driver->parent_param = pp; driver->parent_param->entryFlag = 1; driver->parent_param->beaconPeriod = WXC_BEACON_PERIOD; driver->parent_param->channel = 1; } /*---------------------------------------------------------------------------* Name: WXC_SetDriverTarget Description: Starts transition of a special status to the target. Arguments: driver: WXCDriverWork Structure target: State of the transition target Returns: None. *---------------------------------------------------------------------------*/ void WXC_SetDriverTarget(WXCDriverWork * driver, WXCDriverState target) { driver->target = target; /* Transition starts here if it is stable in another state */ if ((driver->state != WXC_DRIVER_STATE_BUSY) && (driver->state != driver->target)) { WXCi_OnStateChanged(driver, driver->state, NULL); } } /*---------------------------------------------------------------------------* Name: WXC_SetDriverSsid Description: Configures the SSID at connection. Arguments: driver: WXCDriverWork structure buffer: SSID data to configure length: SSID data length. Must be less than WM_SIZE_CHILD_SSID. Returns: None. *---------------------------------------------------------------------------*/ void WXC_SetDriverSsid(WXCDriverWork * driver, const void *buffer, u32 length) { length = (u32)MATH_MIN(length, WM_SIZE_CHILD_SSID); MI_CpuCopy8(buffer, driver->ssid, length); MI_CpuFill8(driver->ssid + length, 0x00, (u32)(WM_SIZE_CHILD_SSID - length)); } /*---------------------------------------------------------------------------* End of file *---------------------------------------------------------------------------*/