/*---------------------------------------------------------------------------* Project: NitroSDK - WXC - libraries - File: wxc_api.c Copyright 2003-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 /*****************************************************************************/ /* Declaration */ /* All work variables of WXC library */ typedef struct WXCWork { u32 wm_dma; WXCCallback system_callback; WXCScheduler scheduler; WXCUserInfo user_info[WM_NUM_MAX_CHILD + 1]; BOOL stopping; u8 beacon_count; /* Number of beacon sends */ u8 padding[3]; WMParentParam parent_param ATTRIBUTE_ALIGN(32); WXCDriverWork driver ATTRIBUTE_ALIGN(32); WXCProtocolContext protocol[1] ATTRIBUTE_ALIGN(32); } WXCWork; SDK_STATIC_ASSERT(sizeof(WXCWork) <= WXC_WORK_SIZE); /*****************************************************************************/ /* Variables */ static WXCStateCode state = WXC_STATE_END; static WXCWork *work = NULL; /*****************************************************************************/ /* Functions */ /*---------------------------------------------------------------------------* Name: WXCi_ChangeState Description: Generates a callback at the same time it changes the internal status. Arguments: stat: Transitioning status arg: Callback function (changes depending on the status) Returns: None. *---------------------------------------------------------------------------*/ static inline void WXCi_ChangeState(WXCStateCode stat, void *arg) { OSIntrMode bak_cpsr = OS_DisableInterrupts(); { state = stat; if (work->system_callback) { (*work->system_callback) (state, arg); } } (void)OS_RestoreInterrupts(bak_cpsr); } /*---------------------------------------------------------------------------* Name: WXCi_SeekNextBlock Description: Searches for the next block that should be run. Arguments: None. Returns: None. *---------------------------------------------------------------------------*/ static void WXCi_SeekNextBlock(void) { /* Selects the next block */ WXCProtocolRegistry *target = WXC_FindNextBlock(work->protocol, work->protocol->current_block, 0, FALSE); if (!target) { /* If there is no data registered, it has no choice but to select an empty block */ target = WXC_FindNextBlock(work->protocol, NULL, 0, TRUE); } WXC_SetCurrentBlock(work->protocol, target); work->parent_param.ggid = target->ggid; } /*---------------------------------------------------------------------------* Name: WXCi_DriverEventCallback Description: Event callback from the wireless driver. Arguments: event: Notified event arg: Function assigned to each event Returns: u32 event result value assigned to each event. *---------------------------------------------------------------------------*/ static u32 WXCi_DriverEventCallback(WXCDriverEvent event, void *arg) { u32 result = 0; switch (event) { case WXC_DRIVER_EVENT_BEACON_SEND: /* * Beacon update timing (argument is WMParentParam pointer) * Update the beacon state while running. */ if (!work->stopping && (WXC_GetStateCode() == WXC_STATE_ACTIVE)) { WXC_BeaconSendHook(work->protocol, &work->parent_param); /* Restarts if there is no connection for a period of time */ if (work->driver.peer_bitmap == 0) { if (++work->beacon_count > WXC_PARENT_BEACON_SEND_COUNT_OUT) { work->beacon_count = 0; /* Updates the scheduler and if it's still in parent mode, makes a connection */ if (WXCi_UpdateScheduler(&work->scheduler)) { } /* If it is in child mode, the status is changed */ else { WXCi_StartChild(&work->driver); } } } } break; case WXC_DRIVER_EVENT_BEACON_RECV: /* * Beacon detection timing (argument is WMBssDesc pointer) * If it is seen as connection target, return TRUE. Otherwise, return FALSE. */ { WXCBeaconFoundCallback cb; /* Beacon disconnect first happens in protocol */ cb.bssdesc = (const WMBssDesc *)arg; cb.matched = WXC_BeaconRecvHook(work->protocol, cb.bssdesc); /* Based on that result, the user application can also be disconnected */ (*work->system_callback) (WXC_STATE_BEACON_RECV, &cb); result = (u32)cb.matched; /* If a connection should be made based on this result, TRUE is returned */ if (result) { work->parent_param.ggid = WXC_GetCurrentBlock(work->protocol)->ggid; } } break; case WXC_DRIVER_EVENT_STATE_END: /* * Shut down wireless (the argument is work memory allocated to the WXC library) */ work->stopping = FALSE; WXCi_ChangeState(WXC_STATE_END, work); break; case WXC_DRIVER_EVENT_STATE_STOP: /* * Transition to the STOP state complete (the argument is always NULL) */ work->stopping = FALSE; WXCi_ChangeState(WXC_STATE_READY, NULL); break; case WXC_DRIVER_EVENT_STATE_IDLE: /* * Transition to the IDLE state complete (the argument is always NULL) */ /* If it is preparing to finish, wireless ends here */ if (WXC_GetStateCode() != WXC_STATE_ACTIVE) { WXCi_End(&work->driver); } /* If it is preparing to stop, wireless stops here */ else if (work->stopping) { WXCi_Stop(&work->driver); } /* Otherwise, it switches between parent and child */ else if (WXCi_UpdateScheduler(&work->scheduler)) { /* If there is multiple data for exchange registered, GGID switches here */ WXCi_SeekNextBlock(); /* Initialization of parent settings compatible with GGID happens in protocol */ WXC_BeaconSendHook(work->protocol, &work->parent_param); /* Parent start */ WXCi_StartParent(&work->driver); } else { WXCi_StartChild(&work->driver); } break; case WXC_DRIVER_EVENT_STATE_PARENT: case WXC_DRIVER_EVENT_STATE_CHILD: /* * Transition to the MP state complete (the argument is always NULL) */ /* If it is preparing to finish, wireless ends here */ if (WXC_GetStateCode() != WXC_STATE_ACTIVE) { WXCi_End(&work->driver); } /* If it is preparing to stop, wireless stops here */ else if (work->stopping) { WXCi_Stop(&work->driver); } work->beacon_count = 0; /* Configures its own user settings */ { WXCUserInfo *p_user = &work->user_info[work->driver.own_aid]; p_user->aid = work->driver.own_aid; OS_GetMacAddress(p_user->macAddress); } break; case WXC_DRIVER_EVENT_CONNECTING: /* * Pre-connection notification (the argument is a WMBssDesc pointer) */ { WMBssDesc * bss = (WMBssDesc *)arg; WXC_CallPreConnectHook(work->protocol, bss, &bss->ssid[8]); } break; case WXC_DRIVER_EVENT_CONNECTED: /* * Connection notification (the argument is a WMStartParentCallback or WMStartConnectCallback) */ { const WXCProtocolRegistry *block = WXC_GetCurrentBlock(work->protocol); WMStartParentCallback *cb = (WMStartParentCallback *)arg; const BOOL is_parent = WXC_IsParentMode(); const u16 parent_size = (u16)(is_parent ? work->parent_param. parentMaxSize : WXCi_GetParentBssDesc(&work->driver)->gameInfo.parentMaxSize); const u16 child_size = (u16)(is_parent ? work->parent_param. childMaxSize : WXCi_GetParentBssDesc(&work->driver)->gameInfo.childMaxSize); const u16 aid = (u16)(is_parent ? cb->aid : 0); WXCUserInfo *p_user = &work->user_info[aid]; const BOOL is_valid_block = (!work->stopping && (WXC_GetStateCode() == WXC_STATE_ACTIVE) && block); /* First, copy the user information of the connection partner */ p_user->aid = aid; if (is_parent) { WM_CopyBssid16(cb->macAddress, p_user->macAddress); } else { const WMBssDesc *p_bss = WXCi_GetParentBssDesc(&work->driver); WM_CopyBssid16(p_bss->bssid, p_user->macAddress); } /* Next, initialize impl in the current MP settings */ WXC_ResetSequence(work->protocol, parent_size, child_size); /* * If the process is not shutting down and RegisterData is currently valid, automatically register data here * */ if (is_valid_block) { /* Data is only registered when there is data to be automatically registered */ if ((block->send.buffer != NULL) && (block->recv.buffer != NULL)) { WXC_AddBlockSequence(work->protocol, block->send.buffer, block->send.length, block->recv.buffer, block->recv.length); } } /* Connection notification to protocol */ WXC_ConnectHook(work->protocol, (u16)(1 << aid)); /* If necessary, it calls back to the user application */ if (is_valid_block) { (*work->system_callback) (WXC_STATE_CONNECTED, p_user); } /* * If no one has used the AddData function at this point, impl runs so that the !IsExecuting function will be true * */ } break; case WXC_DRIVER_EVENT_DISCONNECTED: /* * Disconnect notification * This function is called when WM_Disconnect completes, as indicated by WM_StartParent, and in PortRecvCallback, so there may be some overlap. * * * */ WXC_DisconnectHook(work->protocol, (u16)(u32)arg); /* * Reset when disconnecting if all data is unregistered and the WXC_End function is run in the completion callback * * */ if (!WXC_GetUserBitmap()) { if ((WXC_GetStateCode() == WXC_STATE_ACTIVE) || (WXC_GetStateCode() == WXC_STATE_ENDING) || !WXC_GetCurrentBlock(work->protocol)) { work->beacon_count = 0; WXCi_Reset(&work->driver); } } break; case WXC_DRIVER_EVENT_DATA_SEND: /* MP packet send hook (argument is the WXCPacketInfo pointer) */ WXC_PacketSendHook(work->protocol, (WXCPacketInfo *) arg); break; case WXC_DRIVER_EVENT_DATA_RECV: /* MP packet receive hook (argument is the const WXCPacketInfo pointer) */ (void)WXC_PacketRecvHook(work->protocol, (const WXCPacketInfo *)arg); /* When it is already disconnected as protocol, it resets */ if (!work->protocol->current_block->impl->IsExecuting(work->protocol)) { WXCi_Reset(&work->driver); } break; default: OS_TPanic("unchecked event implementations!"); break; } return result; } /*---------------------------------------------------------------------------* External Functions *---------------------------------------------------------------------------*/ /*---------------------------------------------------------------------------* Name: WXC_Init Description: Initializes chance encounter communications. Initializes the internal status Arguments: work_mem: Work memory used in the work library. The memory must be at least WXC_WORK_SIZE bytes and 32-byte aligned. callback: Sends notifications for the various library system states Pointer to the callback dma: DMA channel used in the library Returns: None. *---------------------------------------------------------------------------*/ void WXC_Init(void *work_mem, WXCCallback callback, u32 dma) { OSIntrMode bak_cpsr = OS_DisableInterrupts(); // Install various protocols on the first invocation static BOOL initialized = FALSE; if (!initialized) { WXC_InstallProtocolImpl(WXCi_GetProtocolImplCommon()); WXC_InstallProtocolImpl(WXCi_GetProtocolImplWPB()); WXC_InstallProtocolImpl(WXCi_GetProtocolImplWXC()); initialized = TRUE; } { if (WXC_GetStateCode() == WXC_STATE_END) { if (((u32)work_mem & 31) != 0) { OS_TPanic("WXC work buffer must be 32-byte aligned!"); } work = (WXCWork *) work_mem; MI_CpuClear32(work, sizeof(WXCWork)); work->wm_dma = dma; work->system_callback = callback; WXCi_InitScheduler(&work->scheduler); WXC_InitProtocol(work->protocol); /* Initializes parent settings */ work->parent_param.maxEntry = 1; /* Currently only supports 1 to 1 connections */ work->parent_param.parentMaxSize = WXC_PACKET_SIZE_MAX; work->parent_param.childMaxSize = WXC_PACKET_SIZE_MAX; work->parent_param.CS_Flag = 1; /* Continuous distribution */ WXC_InitDriver(&work->driver, &work->parent_param, WXCi_DriverEventCallback, work->wm_dma); WXCi_ChangeState(WXC_STATE_READY, NULL); } } (void)OS_RestoreInterrupts(bak_cpsr); } /*---------------------------------------------------------------------------* Name: WXC_SetParentParameter Description: Specifies wireless connection settings The configuration contents are only used when in parent mode. When connected in child mode, the device follows the parent's settings. This function is optional. Call it before the WXC_Start function only when necessary. Arguments: sendSize: Parent send size. It must be at least WXC_PACKET_SIZE_MIN and no more than WXC_PACKET_SIZE_MAX. recvSize: Parent receive size. It must be at least WXC_PACKET_SIZE_MIN and no more than WXC_PACKET_SIZE_MAX. maxEntry: Maximum number of connected players. The current implementation supports only a specified value of 1. Returns: If the settings are valid, they are applied internally, and TRUE is returned. Otherwise, a warning message is sent to debug output, and FALSE is returned. *---------------------------------------------------------------------------*/ BOOL WXC_SetParentParameter(u16 sendSize, u16 recvSize, u16 maxEntry) { BOOL ret = FALSE; /* The current implementation only supports communication in pairs */ if (maxEntry > 1) { OS_TWarning("unsupported maxEntry. (must be 1)\n"); } /* Determines packet size */ else if ((sendSize < WXC_PACKET_SIZE_MIN) || (sendSize > WXC_PACKET_SIZE_MAX) || (recvSize < WXC_PACKET_SIZE_MIN) || (recvSize > WXC_PACKET_SIZE_MAX)) { OS_TWarning("packet size is out of range.\n"); } /* Determines time required for MP communications */ else { /* Evaluation for the time taken to perform MP communications once */ int usec = 330 + 4 * (sendSize + 38) + maxEntry * (112 + 4 * (recvSize + 32)); const int max_time = 5600; if (usec >= max_time) { OS_TWarning("specified MP setting takes %d[usec]. (should be lower than %d[usec])\n", usec, max_time); } else { work->parent_param.parentMaxSize = sendSize; work->parent_param.childMaxSize = recvSize; ret = TRUE; } } return ret; } /*---------------------------------------------------------------------------* Name: WXC_Start Description: Starts chance encounter communications. Starts the internal wireless driver. Arguments: None. Returns: None. *---------------------------------------------------------------------------*/ void WXC_Start(void) { OSIntrMode bak_cpsr = OS_DisableInterrupts(); { SDK_ASSERT(WXC_GetStateCode() != WXC_STATE_END); if (WXC_GetStateCode() == WXC_STATE_READY) { WXCi_Reset(&work->driver); WXCi_ChangeState(WXC_STATE_ACTIVE, NULL); } } (void)OS_RestoreInterrupts(bak_cpsr); } /*---------------------------------------------------------------------------* Name: WXC_Stop Description: Stops chance encounter communications. Stops the internal wireless driver. Arguments: None. Returns: None. *---------------------------------------------------------------------------*/ void WXC_Stop(void) { OSIntrMode bak_cpsr = OS_DisableInterrupts(); { SDK_ASSERT(WXC_GetStateCode() != WXC_STATE_END); switch (WXC_GetStateCode()) { case WXC_STATE_ACTIVE: if (!work->stopping) { work->stopping = TRUE; /* If currently in a connection, it waits to end while specifying a stop request */ if (WXC_GetUserBitmap() == 0) { WXCi_Stop(&work->driver); } } break; case WXC_STATE_READY: case WXC_STATE_ENDING: case WXC_STATE_END: /* If the end operation is complete or running, it does not do anything */ break; } } (void)OS_RestoreInterrupts(bak_cpsr); } /*---------------------------------------------------------------------------* Name: WXC_End Description: Ends chance encounter communications. Ends the internal wireless driver. Arguments: None. Returns: None. *---------------------------------------------------------------------------*/ void WXC_End(void) { OSIntrMode bak_cpsr = OS_DisableInterrupts(); { switch (WXC_GetStateCode()) { case WXC_STATE_READY: case WXC_STATE_ACTIVE: WXCi_ChangeState(WXC_STATE_ENDING, NULL); /* If currently in a connection, it waits to end while specifying an end request */ if (WXC_GetUserBitmap() == 0) { WXCi_End(&work->driver); } break; case WXC_STATE_ENDING: case WXC_STATE_END: /* If the end operation is complete or running, it does not do anything */ break; } } (void)OS_RestoreInterrupts(bak_cpsr); } /*---------------------------------------------------------------------------* Name: WXC_GetStateCode Description: Gets the current status of the library. Arguments: None. Returns: WXCStateCode type that shows the current library status. *---------------------------------------------------------------------------*/ WXCStateCode WXC_GetStateCode(void) { return state; } /*---------------------------------------------------------------------------* Name: WXC_IsParentMode Description: Determines whether the current wireless state is parent. Only valid in the WXC_STATE_ACTIVE state. Arguments: None. Returns: If the wireless status is parent mode, TRUE. If it is child mode, FALSE. *---------------------------------------------------------------------------*/ BOOL WXC_IsParentMode(void) { return WXCi_IsParentMode(&work->driver); } /*---------------------------------------------------------------------------* Name: WXC_GetParentParam Description: Refers to the WMParentParam structure for the current wireless state. Arguments: None. Returns: WMParentParam structure. *---------------------------------------------------------------------------*/ const WMParentParam *WXC_GetParentParam(void) { return &work->parent_param; } /*---------------------------------------------------------------------------* Name: WXC_GetParentBssDesc Description: Refers to the WMBssDesc structure for the current connection target. Arguments: None. Returns: WMBssDesc structure. *---------------------------------------------------------------------------*/ const WMBssDesc *WXC_GetParentBssDesc(void) { return WXCi_GetParentBssDesc(&work->driver); } /*---------------------------------------------------------------------------* Name: WXC_GetUserBitmap Description: Gets the currently connected user's status with a bitmap. Arguments: None. Returns: WMBssDesc structure. *---------------------------------------------------------------------------*/ u16 WXC_GetUserBitmap(void) { u16 bitmap = work->driver.peer_bitmap; if (bitmap != 0) { bitmap = (u16)(bitmap | (1 << work->driver.own_aid)); } return bitmap; } /*---------------------------------------------------------------------------* Name: WXC_GetCurrentGgid Description: Gets the selected GGID in the current connection. If communications are not established, it returns 0. The current communications status can be determined by the WXC_GetUserBitmap function's return value. Arguments: None. Returns: The selected GGID in the current connection. *---------------------------------------------------------------------------*/ u32 WXC_GetCurrentGgid(void) { u32 ggid = 0; { OSIntrMode bak_cpsr = OS_DisableInterrupts(); WXCProtocolRegistry *current = WXC_GetCurrentBlock(work->protocol); if (current) { ggid = current->ggid; /* Modeled on the detected partner ID if ANY */ if (ggid == WXC_GGID_COMMON_ANY) { ggid = work->protocol->target_ggid; } } (void)OS_RestoreInterrupts(bak_cpsr); } return ggid; } /*---------------------------------------------------------------------------* Name: WXC_GetOwnAid Description: Gets its own AID in the current connection. If communications are not established, it returns an inconstant value. The current communications status can be determined by the WXC_GetUserBitmap function's return value. Arguments: None. Returns: Its own AID in the current connection. *---------------------------------------------------------------------------*/ u16 WXC_GetOwnAid(void) { return work->driver.own_aid; } /*---------------------------------------------------------------------------* Name: WXC_GetUserInfo Description: Gets the user information of the user currently connected. Do not change the target of the pointer that is returned. Arguments: aid: User AID that gets information Returns: If the specified AID is valid, user information. Otherwise, NULL. *---------------------------------------------------------------------------*/ const WXCUserInfo *WXC_GetUserInfo(u16 aid) { const WXCUserInfo *ret = NULL; { OSIntrMode bak_cpsr = OS_DisableInterrupts(); if ((aid <= WM_NUM_MAX_CHILD) && ((WXC_GetUserBitmap() & (1 << aid)) != 0)) { ret = &work->user_info[aid]; } (void)OS_RestoreInterrupts(bak_cpsr); } return ret; } /*---------------------------------------------------------------------------* Name: WXC_SetChildMode Description: Wireless is set so that it can only be run on the child side. This function must be called before the WXC_Start function is called. Arguments: enable: If it can only be run on the child side, TRUE Returns: None. *---------------------------------------------------------------------------*/ void WXC_SetChildMode(BOOL enable) { SDK_ASSERT(WXC_GetStateCode() != WXC_STATE_ACTIVE); WXCi_SetChildMode(&work->scheduler, enable); } /*---------------------------------------------------------------------------* Name: WXC_AddData Description: Data is configured by adding it to a completed block data exchange. Arguments: send_buf: Send buffer send_size: Send buffer size recv_buf: Receive buffer recv_max: Receive buffer size Returns: None. *---------------------------------------------------------------------------*/ void WXC_AddData(const void *send_buf, u32 send_size, void *recv_buf, u32 recv_max) { WXC_AddBlockSequence(work->protocol, send_buf, send_size, recv_buf, recv_max); } /*---------------------------------------------------------------------------* Name: WXC_RegisterDataEx Description: Registers data for exchange Arguments: ggid: GGID of the registered data callback: Callback function to the user (invoked when data exchange is complete) send_ptr: Pointer to registered data send_size: Size of registered data recv_ptr: Pointer to the receive buffer recv_size: Receive buffer size type: Chance encounter communication type (PIType) Returns: None. *---------------------------------------------------------------------------*/ void WXC_RegisterDataEx(u32 ggid, WXCCallback callback, u8 *send_ptr, u32 send_size, u8 *recv_ptr, u32 recv_size, const char *type) { OSIntrMode bak_cpsr = OS_DisableInterrupts(); /* * Do not allow the same GGID to be registered more than once * (because the child device cannot determine which one to use when connecting) */ WXCProtocolRegistry *same_ggid = WXC_FindNextBlock(work->protocol, NULL, ggid, TRUE); if (same_ggid != NULL) { OS_TWarning("already registered same GGID!"); } /* If not, use an empty block */ else { WXCProtocolRegistry *p_data = WXC_FindNextBlock(work->protocol, NULL, 0, TRUE); /* If there is no empty block, fail */ if (!p_data) { OS_TPanic("no more memory to register data!"); } else { /* Change the registered communication protocol based on the selected protocol */ WXCProtocolImpl *impl = WXC_FindProtocolImpl(type); if (!impl) { OS_TPanic("unknown protocol \"%s\"!", type); } WXC_InitProtocolRegistry(p_data, ggid, callback, impl); WXC_SetInitialExchangeBuffers(p_data, send_ptr, send_size, recv_ptr, recv_size); } } (void)OS_RestoreInterrupts(bak_cpsr); } /*---------------------------------------------------------------------------* Name: WXC_SetInitialData Description: The data block used for every exchange is set by the first exchange. Arguments: ggid: GGID of the registered data send_ptr: Pointer to registered data send_size: Size of registered data recv_ptr: Pointer to the receive buffer recv_size: Receive buffer size Returns: None. *---------------------------------------------------------------------------*/ void WXC_SetInitialData(u32 ggid, u8 *send_ptr, u32 send_size, u8 *recv_ptr, u32 recv_size) { OSIntrMode bak_cpsr = OS_DisableInterrupts(); /* Searches for the specified block */ WXCProtocolRegistry *target = WXC_FindNextBlock(work->protocol, NULL, ggid, TRUE); if (target) { WXC_SetInitialExchangeBuffers(target, send_ptr, send_size, recv_ptr, recv_size); } (void)OS_RestoreInterrupts(bak_cpsr); } /*---------------------------------------------------------------------------* Name: WXC_UnregisterData Description: Deletes data for exchange from registration Arguments: ggid: GGID related to the deleted block Returns: None. *---------------------------------------------------------------------------*/ void WXC_UnregisterData(u32 ggid) { OSIntrMode bak_cpsr = OS_DisableInterrupts(); /* Searches for the specified block */ WXCProtocolRegistry *target = WXC_FindNextBlock(work->protocol, NULL, ggid, TRUE); /* Searches once more, including when this is a shared chance encounter communication specification */ if (!target) { target = WXC_FindNextBlock(work->protocol, NULL, (u32)(ggid | WXC_GGID_COMMON_BIT), TRUE); } if (target) { /* If it is being used in the current connection, fail */ if ((WXC_GetUserBitmap() != 0) && (target == WXC_GetCurrentBlock(work->protocol))) { OS_TWarning("specified data is now using."); } else { /* Releases data */ target->ggid = 0; target->callback = NULL; target->send.buffer = NULL; target->send.length = 0; target->recv.buffer = NULL; target->recv.length = 0; } } (void)OS_RestoreInterrupts(bak_cpsr); } /*---------------------------------------------------------------------------* Name: WXCi_SetSsid Description: Configures the SSID as a child when connecting. Internal function. Arguments: buffer: SSID data to configure length: SSID data length Must be less than WM_SIZE_CHILD_SSID Returns: None. *---------------------------------------------------------------------------*/ void WXCi_SetSsid(const void *buffer, u32 length) { OSIntrMode bak_cpsr = OS_DisableInterrupts(); WXC_SetDriverSsid(&work->driver, buffer, length); (void)OS_RestoreInterrupts(bak_cpsr); }