/*---------------------------------------------------------------------------*
  Project:  NitroSDK - MB - demos - fake_child
  File:     main.c

  Copyright 2003,2004 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.

  $Log: main.c,v $
  Revision 1.6  2004/11/26 00:23:04  takano_makoto
  Revised sequence, added comments

  Revision 1.4  2004/11/25 07:39:13  takano_makoto
  Revised manipulation of OwnerInfo used for IPL in old version

  Revision 1.2  2004/11/24 13:09:10  takano_makoto
  Added error handling, cancel processing 

  Revision 1.1  2004/11/22 13:01:22  takano_makoto
  Initial update.

  $NoKeywords: $
 *---------------------------------------------------------------------------*/

#include <nitro.h>

#include "common.h"
#include "disp.h"
#include "gmain.h"
#include "wh.h"


/*
 * In this sample, add child entries to demos/mb/multiboot-Model,
 * and participate in communication with other DS download play children. 
 * The target application is set by the GGID argument in MB_FakeScanParent. 
 */
 

enum
{
    MODE_TITLE,
    MODE_START_SCAN,
    MODE_SCANNING,
    MODE_ENTRY,
    MODE_RECONNECT,
    MODE_GMAIN,
    MODE_ERROR,
    MODE_CANCEL
};

typedef struct 
{
    MBUserInfo  info;
    u16         valid;
    u8          maxPlayerNum;   // Max number of players
    u8          nowPlayerNum;   // Current number of players
    u8          macAddr[6];     // MAC address
} ParentInfo;

#define WH_GGID (0x003FFF00 | (0x21))

static void PrintError( MBFakeScanErrorCallback* arg );

static void ModeTitle( void );
static void ModeStartScan( void );
static void ModeScanning( void );
static void ModeEntry( void );
static void ModeError( void );
static void ModeReconnect( void );
static void ModeGMain( void );
static void ModeCancel( void );

static void NotifyScanParent( u16 type, void *arg );
static void MBStateCallback( u32 status, void *arg );


// Parent info list
static ParentInfo  parentInfo[ MB_GAME_INFO_RECV_LIST_NUM ];
static ParentInfo  parentInfoBuf[ MB_GAME_INFO_RECV_LIST_NUM ];

static WMBssDesc   sParentBssDesc;  // BssDesc of entered parent 

static u8  *mbfBuf = NULL;
static u8  *wmBuf  = NULL;


static s32 gFrame;                 // Frame counter

static u16 sMode;

enum 
{
    WSTATE_STOP,                // Stop state
    WSTATE_INITIALIZE_BUSY,     // Initializing wireless 
    WSTATE_IDLE,                // Idling
    WSTATE_ERROR,               // Error before starting/after stopping fake child
    WSTATE_FAKE_SCAN,           // Scan
    WSTATE_FAKE_END_SCAN,       // End scan
    WSTATE_FAKE_ENTRY,          // Entry to parent
    WSTATE_FAKE_BOOT_READY,     // Entry to DS download play is complete 
    WSTATE_FAKE_BOOT_END_BUSY,  // DS download processing is now finishing 
    WSTATE_FAKE_BOOT_END,
    WSTATE_FAKE_CANCEL_BUSY,    // DS download cancelling 
    WSTATE_FAKE_ERROR           // Error
};
static u16 sWirelessState;

static u16 sConnectIndex;           // Index of the parent connecting 

/******************************************************************************/
/* function */

/* V blank interrupt processing */
static void VBlankIntr(void)
{
    //---- Interrupt check flag
    OS_SetIrqCheckFlag( OS_IE_V_BLANK );
}


/* Main */
void NitroMain()
{
    // Initialize system 
    CommonInit();
    // Initialize screen
    DispInit();
    // Initialize heap
    InitAllocateSystem();
    
    // Enable interrupts
    (void)OS_EnableIrq();
    (void)OS_EnableInterrupts();
    
    // Clear buffer for data sharing
    GInitDataShare();
    
    // To title screen
    sMode = MODE_TITLE;
    
    // Start LCD display
    GX_DispOn();
    GXS_DispOn();
    
    // Output debug text string
    OS_TPrintf("MB fake child: Simple DataSharing demo started.\n");
    
    // Dummy call to get key input data (To handle press of A Button by IPL)
    ReadKey();
    
    /* Main loop */
    for ( gFrame = 0 ; TRUE ; gFrame++ )
    {
        // Get key input info 
        ReadKey();
        
        // Clear screen 
        BgClear();
        
        switch ( sMode )
        {
        case MODE_TITLE :
            // Display title screen 
            ModeTitle();
            break;
        case MODE_START_SCAN :
            // Start scan
            ModeStartScan();
            break;
        case MODE_SCANNING :
            // Scanning MB parent
            ModeScanning();
            break;
        case MODE_ENTRY :
            // Entry to MB parent 
            ModeEntry();
            break;
        case MODE_RECONNECT :
            // Reconnect to MB parent
            ModeReconnect();
            break;
        case MODE_GMAIN :
            // Game main
            ModeGMain();
            break;
        case MODE_ERROR :
            // Error processing
            ModeError();
            break;
        case MODE_CANCEL :
            // Cancelling
            ModeCancel();
            break;
        }
        
        SVC_WaitVBlankIntr();
    }
}



/* ----------------------------------------------------------------------
  Name:        ModeTitle
  
  Description: Title screen
  
  Arguments:   None.
                
  Returns:     None.
  ---------------------------------------------------------------------- */
static void ModeTitle( void )
{
    BgSetMessage( PLTT_WHITE, " Press Button A to start   ");
    
    sWirelessState = WSTATE_STOP;
    
    // When A Button is pressed, set wireless to ON and enter MB parent search mode. 
    if ( IS_PAD_TRIGGER( PAD_BUTTON_A ) )
    {
        sMode     = MODE_START_SCAN;
    }
}

/* ----------------------------------------------------------------------
  Name:        ModeStartScan
  
  Description: Starts parent scan 
  
  Arguments:   None.
               
  Returns:     None.
  ---------------------------------------------------------------------- */
static void ModeStartScan( void )
{
    BgSetMessage( PLTT_WHITE, "Now working....         ");
    
    switch ( sWirelessState )
    {
    //--------------------------
    // Start wireless communication 
    case WSTATE_STOP:
        if ( ! WH_Initialize() )
        {
            sWirelessState = WSTATE_ERROR;
            sMode = MODE_ERROR;
            return;
        }
        sWirelessState = WSTATE_INITIALIZE_BUSY;
        break;
    
    //--------------------------
    // Initializing wireless 
    case WSTATE_INITIALIZE_BUSY:
        if ( WH_GetSystemState() == WH_SYSSTATE_BUSY )
        {
            BgSetMessage( PLTT_WHITE, "Now working....         ");
            return;
        }
        if ( WH_GetSystemState() != WH_SYSSTATE_IDLE )
        {
            sWirelessState = WSTATE_ERROR;
            sMode = MODE_ERROR;
            return;
        }
        sWirelessState = WSTATE_IDLE;
        // don't break;
        
    //--------------------------
    // Start parent scan 
    case WSTATE_IDLE:
        {
            // Set user info 
            MBUserInfo userInfo;
            
#if ( SDK_NVRAM_FORMAT >= 100 )
            OSOwnerInfo info;
            
            OS_GetOwnerInfo( &info );
            userInfo.favoriteColor = info.favoriteColor;
            userInfo.nameLength = (u8)info.nickNameLength;
            MI_CpuCopy8( info.nickName, userInfo.name, userInfo.nameLength * 2 );
#else // For old IPL
            NVRAMConfig* info = (NVRAMConfig*)OS_GetSystemWork()->nvramUserInfo;
            
            #define DEFAULT_FAVORIT_COLOR   1
            userInfo.favoriteColor = DEFAULT_FAVORIT_COLOR;
            userInfo.nameLength = (u8)info->ncd.owner.nickname.length;
            MI_CpuCopy8( info->ncd.owner.nickname.name, userInfo.name, NVRAM_CONFIG_NICKNAME_LENGTH * 2 );
#endif
            userInfo.playerNo = 0;
            
            // Allocate work buffer for multiboot entries 
            mbfBuf = (u8*)OS_Alloc( MB_FakeGetWorkSize() );
            MB_FakeInit( mbfBuf, &userInfo );
            // Set the state transition notification callback function 
            MB_FakeSetCStateCallback( MBStateCallback );
            
            // Clear parent list 
            MI_CpuClear8( parentInfo, sizeof( parentInfo ) );
            
            // Scanning wireless state 
            sWirelessState = WSTATE_FAKE_SCAN;
            
            // Set GGID of the parent to scan and the notification callback function; start scanning
            MB_FakeStartScanParent( NotifyScanParent, WH_GGID );
            
            // Enter scan mode 
            sMode = MODE_SCANNING;
        }
        break;
    default:
        OS_TPanic("illegal state %d", sWirelessState);
        break;
    }
}


/* ----------------------------------------------------------------------
  Name:        ModeScanning
  
  Description: Parent scanning 
  
  Arguments:   None.
               
  Returns:     None.
  ---------------------------------------------------------------------- */
static void ModeScanning( void )
{
    enum { STR_X = 4, STR_Y = 4, NUM_STR_X = 24 };
    static u16 cursol_idx = 0;
    
    u16     i;
    
    // If error occurrs, terminate 
    if ( sWirelessState == WSTATE_FAKE_ERROR )
    {
        sMode = MODE_ERROR;
        return;
    }
    
    // To prevent modification by an interrupt during rendering,
    // copy the parent info to a temporary buffer before use. 
    {
        OSIntrMode enabled = OS_DisableInterrupts();
        MI_CpuCopy8( parentInfo, parentInfoBuf, sizeof( parentInfo ) );
        (void)OS_RestoreInterrupts( enabled );
    }
    
    // Move cursor 
    if ( IS_PAD_TRIGGER( PAD_KEY_UP ) )
    {
        cursol_idx = (u16)( (cursol_idx - 1) & 0xF );
    }
    else if ( IS_PAD_TRIGGER( PAD_KEY_DOWN ) )
    {
        cursol_idx = (u16)( (cursol_idx + 1) & 0xF );
    }
    
    // Display parent info 
    BgPutString( 10, 1, PLTT_RED, "Select Parent" );
    
    BgPutString( STR_X - 2, STR_Y + cursol_idx, PLTT_WHITE, ">" );
    for ( i = 0; i < MB_GAME_INFO_RECV_LIST_NUM; i++ )
    {
        if ( parentInfoBuf[ i ].valid )
        {
            BgPrintStr( STR_X, STR_Y + i, PLTT_GREEN, "%02x:%02x:%02x:%02x:%02x:%02x",
                        parentInfoBuf[ i ].macAddr[ 0 ], parentInfoBuf[ i ].macAddr[ 1 ],
                        parentInfoBuf[ i ].macAddr[ 2 ], parentInfoBuf[ i ].macAddr[ 3 ],
                        parentInfoBuf[ i ].macAddr[ 4 ], parentInfoBuf[ i ].macAddr[ 5 ] );
            BgPrintStr( NUM_STR_X, STR_Y + i, PLTT_GREEN, "%d/%d", 
                        parentInfoBuf[ i ].nowPlayerNum, parentInfoBuf[ i ].maxPlayerNum );
        }
        else
        {
            BgPutString( STR_X, STR_Y + i, PLTT_WHITE, "Searching..." );
        }
    }
    
    // Cancel communication
    if ( IS_PAD_TRIGGER( PAD_BUTTON_B ) )
    {
        sWirelessState = WSTATE_FAKE_CANCEL_BUSY;
        sMode = MODE_CANCEL;
        MB_FakeEnd();
        return;
    }
    
    // Select the parent to connect to
    if ( IS_PAD_TRIGGER( PAD_BUTTON_A ) )
    {
        if ( parentInfoBuf[ cursol_idx ].valid )
        {
            // Get info from the parent to connect to
            sConnectIndex = cursol_idx;
            if ( ! MB_FakeReadParentBssDesc( sConnectIndex, &sParentBssDesc, WH_PARENT_MAX_SIZE, WH_CHILD_MAX_SIZE, FALSE, FALSE ) )
            {
                // If the parent info is not valid, continue scan 
                return;
            }
            sMode = MODE_ENTRY;
        }
    }
}


/* ----------------------------------------------------------------------
  Name:        ModeEntry
  
  Description: Entry to parent machine
  
  Arguments:   None.
                
  Returns:     None.
  ---------------------------------------------------------------------- */
static void ModeEntry( void )
{
    BgSetMessage( PLTT_WHITE, "Now Entrying...        ");
    
    switch ( sWirelessState )
    {
    //--------------------------
    // If scanning
    case WSTATE_FAKE_SCAN:
        {
            // Terminate scan
            MB_FakeEndScan();
            sWirelessState = WSTATE_FAKE_END_SCAN;
        }
        break;

    //--------------------------
    // Terminating scan 
    case WSTATE_FAKE_END_SCAN:
        break;

    //--------------------------
    // Start wireless communication 
    case WSTATE_IDLE:
        {
            if ( ! MB_FakeEntryToParent( sConnectIndex ) )
            {
                // If specified index is not valid, error
                sWirelessState = WSTATE_FAKE_ERROR;
                sMode = MODE_ERROR;
                return;
            }
            sWirelessState = WSTATE_FAKE_ENTRY;
        }
        break;

    //--------------------------
    // Adding entry
    case WSTATE_FAKE_ENTRY:
        // If entry has not been completed, cancel with B Button 
        if ( IS_PAD_TRIGGER( PAD_BUTTON_B ) )
        {
            sWirelessState = WSTATE_FAKE_CANCEL_BUSY;
            sMode = MODE_CANCEL;
            MB_FakeEnd();
            return;
        }
        break;
        
    //--------------------------
    // Complete entry processing 
    case WSTATE_FAKE_BOOT_READY:
        {
            // Final processing for entry processing 
            sWirelessState = WSTATE_FAKE_BOOT_END_BUSY;
            MB_FakeEnd();
        }
        break;
    
    //--------------------------
    // Wait for finish
    case WSTATE_FAKE_BOOT_END_BUSY:
        break;
        
    //--------------------------
    // Final processing for fake child 
    case WSTATE_FAKE_BOOT_END:
        {
            // Increment tgid to align with parent 
            sParentBssDesc.gameInfo.tgid++;
            // Start reconnection
            sWirelessState = WSTATE_IDLE;
            sMode = MODE_RECONNECT;
        }
        break;
        
    //--------------------------
    // Error termination
    case WSTATE_FAKE_ERROR:
        sMode = MODE_ERROR;
        break;
    
    default:
        OS_TPanic("illegal state %d\n", sWirelessState );
    }
}


/* ----------------------------------------------------------------------
  Name:        ModeReconnect
  
  Description: Title screen
  
  Arguments:   None.
                
  Returns:     None.
  ---------------------------------------------------------------------- */
static void ModeReconnect( void )
{
    BgSetMessage( PLTT_WHITE, "Connecting to Parent...        ");
    
    switch ( WH_GetSystemState() )
    {
    case WH_SYSSTATE_IDLE:
    case WH_SYSSTATE_CONNECT_FAIL:
        {
            (void)WH_ChildConnect( WH_CONNECTMODE_DS_CHILD, &sParentBssDesc );
        }
        break;
    case WH_SYSSTATE_ERROR:
        sWirelessState = WSTATE_ERROR;
        sMode = MODE_ERROR;
        break;
    case WH_SYSSTATE_BUSY:
        break;
    case WH_SYSSTATE_CONNECTED:
    case WH_SYSSTATE_KEYSHARING:
    case WH_SYSSTATE_DATASHARING:
        sMode = MODE_GMAIN;
        break;
    }
}


/* ----------------------------------------------------------------------
  Name:        ModeGMain
  
  Description: Title screen
  
  Arguments:   None.
                
  Returns:     None.
  ---------------------------------------------------------------------- */
static void ModeGMain( void )
{
    if ( WH_GetSystemState() == WH_SYSSTATE_ERROR )
    {
        sWirelessState = WSTATE_ERROR;
        sMode = MODE_ERROR;
        return;
    }
    GStepDataShare( gFrame );
    GMain();
}


/* ----------------------------------------------------------------------
  Name:        ModeCancel
  
  Description: Cancelling screen
  
  Arguments:   None.
                
  Returns:     None.
  ---------------------------------------------------------------------- */
static void ModeCancel( void )
{
    switch ( sWirelessState  )
    {
    case WSTATE_IDLE:
        if ( WH_GetSystemState() == WH_SYSSTATE_IDLE )
        {
            (void)WH_End();
            return;
        }
        
        if ( WH_GetSystemState() != WH_SYSSTATE_STOP )
        {
            return;
        }
        sWirelessState = WSTATE_STOP;
        sMode = MODE_TITLE;
        break;
    //--------------------------
    // Cancelling
    case WSTATE_FAKE_CANCEL_BUSY:
        break;
        
    //--------------------------
    // Error occurred
    case WSTATE_ERROR:
        sWirelessState = WSTATE_IDLE;
        (void)WH_Finalize();
        break;

    //--------------------------
    // Termination processing
    default:
        sWirelessState = WSTATE_IDLE;
        (void)WH_Finalize();
        break;
    }
}


/* ----------------------------------------------------------------------
  Name:        ModeError
  
  Description: Error screen
  
  Arguments:   None.
                
  Returns:     None.
  ---------------------------------------------------------------------- */
static void ModeError( void )
{
    BgPutString( 4, 10, PLTT_RED, "========== ERROR ==========");
    
    if ( IS_PAD_TRIGGER( PAD_BUTTON_B ) || IS_PAD_TRIGGER( PAD_BUTTON_A ) )
    {
        if ( sWirelessState == WSTATE_FAKE_ERROR )
        {
            sWirelessState = WSTATE_FAKE_CANCEL_BUSY;
            MB_FakeEnd();
        }
        sMode = MODE_CANCEL;
    }
}


/* ----------------------------------------------------------------------
  Name:        NotifyScanParent
  
  Description: Parent scan state notification callback 
  
  Arguments:   state    Scan state.
                        One of four messages is sent:
                        MB_FAKESCAN_PARENT_FOUND, MB_FAKESCAN_PARENT_LOST,
                        MB_FAKESCAN_API_ERROR, MB_FAKESCAN_END_SCAN
                
               arg      If state is MB_FAKESCAN_PARENT_FOUND or MB_FAKESCAN_PARENT_LOST,
                        info of target parent can be obtained. 
                
  Returns:     None.
  ---------------------------------------------------------------------- */
static void NotifyScanParent( u16 type, void *arg )
{
    static const char *MB_FAKESCAN_CALLBACK_TYPE_NAME[] =
    {
        "MB_FAKESCAN_PARENT_FOUND",
        "MB_FAKESCAN_PARENT_LOST",
        "MB_FAKESCAN_API_ERROR",
        "MB_FAKESCAN_END_SCAN",
        "MB_FAKESCAN_CONNECTED",
        "MB_FAKESCAN_ENTRY",
        "MB_FAKESCAN_DISCONNECTED",
        "MB_FAKESCAN_SUCCESS"
    };
    
    OS_TPrintf("> %s\n", MB_FAKESCAN_CALLBACK_TYPE_NAME[type] );
    
    switch ( type )
    {
    case MB_FAKESCAN_PARENT_FOUND:
        {
            MBFakeScanCallback *cb = (MBFakeScanCallback*)arg;
            WMBssDesc *bssdesc = cb->bssDesc;
            MBGameInfo *gameInfo = cb->gameInfo;
            
            OS_TPrintf(" find parent %04x%08x %d\n", *(u16*)(&bssdesc->bssid[4]), *(u32*)(&bssdesc->bssid[0]), cb->index );
            OS_TPrintf("      parentMaxSize = %d, childMaxSize = %d\n", bssdesc->gameInfo.parentMaxSize, bssdesc->gameInfo.childMaxSize);
            
            parentInfo[ cb->index ].valid = 1;
            parentInfo[ cb->index ].maxPlayerNum = gameInfo->fixed.maxPlayerNum;
            parentInfo[ cb->index ].nowPlayerNum = gameInfo->volat.nowPlayerNum;
            MI_CpuCopy8( &gameInfo->fixed.parent, &parentInfo[ cb->index ].info, sizeof( MBUserInfo ) );
            WM_CopyBssid( bssdesc->bssid, parentInfo[ cb->index ].macAddr );
        }
        break;
    case MB_FAKESCAN_PARENT_LOST:
        {
            MBFakeScanCallback *cb = (MBFakeScanCallback*)arg;
            
            OS_TPrintf(" lost parent %d\n", cb->index );
            
            parentInfo[ cb->index ].valid = 0;
        }
        break;
    case MB_FAKESCAN_API_ERROR:
        {
            PrintError( (MBFakeScanErrorCallback*)arg );
            sWirelessState = WSTATE_FAKE_ERROR;
        }
        break;
    case MB_FAKESCAN_END_SCAN:
        {
            sWirelessState = WSTATE_IDLE;
        }
        break;
    default:
        break;
    }
}


/* ----------------------------------------------------------------------
  Name:        MBStateCallback
  
  Description: Fake child state-transition notification callback.
               After MB_FakeEntryToParent() is called, the sequence 
               will execute automatically until the child boot completion 
               message is done. No special processing is needed.  When all 
               multiboot processing is complete, the MB_COMM_CSTATE_BOOT_READY
               callback will return. 
  
  Arguments:   status   Current state of the MB child 
               arg      Callback argument 
                
  Returns:     None.
  ---------------------------------------------------------------------- */
static void MBStateCallback( u32 status, void *arg )
{
#pragma unused( arg )
    switch ( status )
    {
    case MB_COMM_CSTATE_INIT_COMPLETE:      // Connection to parent complete 
    case MB_COMM_CSTATE_CONNECT:            // Connection to parent complete 
    case MB_COMM_CSTATE_REQ_ENABLE:         // Established MP with parent 
    case MB_COMM_CSTATE_DLINFO_ACCEPTED:    // Entry to parent complete
    case MB_COMM_CSTATE_RECV_PROCEED:       // Data transmission from parent started 
    case MB_COMM_CSTATE_RECV_COMPLETE:      // Parent data transmission completed 
    case MB_COMM_CSTATE_BOOTREQ_ACCEPTED:   // Boot request from parent received 
        // No special processing needed
        break;
    
    case MB_COMM_CSTATE_BOOT_READY:
        // Notify that disconnect from parent is complete and MB processing is done 
        {
            sWirelessState = WSTATE_FAKE_BOOT_READY;
        }
        break;
        
    case MB_COMM_CSTATE_CANCELLED:
        // Acknowledge that entry to parent was cancelled partway thru 
        break;
    
    case MB_COMM_CSTATE_CONNECT_FAILED:         // Connection to parent failed
    case MB_COMM_CSTATE_DISCONNECTED_BY_PARENT: // Disconnect by parent 
    case MB_COMM_CSTATE_REQ_REFUSED:            // Request refused by parent 
    case MB_COMM_CSTATE_MEMBER_FULL:            // Number of child entries in parent is too high 
    case MB_COMM_CSTATE_ERROR:                  // Processing when communication state error occurs  
        {
            sWirelessState = WSTATE_FAKE_ERROR;
        }
        break;
        
    case MB_COMM_CSTATE_FAKE_END:
        // Release the work passed to MB
        OS_Free( mbfBuf );
        mbfBuf = NULL;
        
        if ( sWirelessState == WSTATE_FAKE_BOOT_END_BUSY )
        {
            sWirelessState = WSTATE_FAKE_BOOT_END;
        }
        else
        {
            sWirelessState = WSTATE_IDLE;
        }
    }
}



/* ----------------------------------------------------------------------
  Name:        PrintError
  
  Description: Prints the WM API error details...
  
  Arguments:   arg Error callback for MB_FakeStartScanParent
  
  Returns:     None.
  ---------------------------------------------------------------------- */
static void PrintError( MBFakeScanErrorCallback* arg )
{
#pragma unused( arg )
    static const char *APIID_NAME[] =
    {
        "WM_APIID_INITIALIZE",
        "WM_APIID_RESET",
        "WM_APIID_END",
        "WM_APIID_ENABLE",
        "WM_APIID_DISABLE",
        "WM_APIID_POWER_ON",
        "WM_APIID_POWER_OFF",
        "WM_APIID_SET_P_PARAM",
        "WM_APIID_START_PARENT",
        "WM_APIID_END_PARENT",
        "WM_APIID_START_SCAN",
        "WM_APIID_END_SCAN",
        "WM_APIID_START_CONNECT",
        "WM_APIID_DISCONNECT",
        "WM_APIID_START_MP",
        "WM_APIID_SET_MP_DATA",
        "WM_APIID_END_MP",
        "WM_APIID_START_DCF",
        "WM_APIID_SET_DCF_DATA",
        "WM_APIID_END_DCF",
        "WM_APIID_SET_WEPKEY",
        "WM_APIID_START_KS",
        "WM_APIID_END_KS",
        "WM_APIID_GET_KEYSET",
        "WM_APIID_SET_GAMEINFO",
        "WM_APIID_SET_BEACON_IND",
        "WM_APIID_START_TESTMODE",
        "WM_APIID_STOP_TESTMODE",
        "WM_APIID_VALARM_MP",
        "WM_APIID_SET_LIFETIME",
        "WM_APIID_MEASURE_CHANNEL",
        "WM_APIID_INIT_W_COUNTER",
        "WM_APIID_GET_W_COUNTER",
        "WM_APIID_SET_ENTRY",
        "WM_APIID_AUTO_DEAUTH",
        "WM_APIID_SET_MP_FREQ",
        "WM_APIID_SET_BEACON_PERIOD",
        "WM_APIID_AUTO_DISCONNECT",
        "WM_APIID_START_SCAN_EX",
        "WM_APIID_KICK_MP_PARENT",
        "WM_APIID_KICK_MP_CHILD",
        "WM_APIID_KICK_MP_RESUME",
        "WM_APIID_ASYNC_KIND_MAX",
        "WM_APIID_INDICATION",
        "WM_APIID_PORT_SEND",
        "WM_APIID_PORT_RECV",
        "WM_APIID_READ_STATUS",
        "WM_APIID_UNKNOWN",
    };
    
    static const char *ERRCODE_NAME[] =
    {
        "WM_ERRCODE_SUCCESS",
        "WM_ERRCODE_FAILED",
        "WM_ERRCODE_OPERATING",
        "WM_ERRCODE_ILLEGAL_STATE",
        "WM_ERRCODE_WM_DISABLE",
        "WM_ERRCODE_NO_DATASET",
        "WM_ERRCODE_INVALID_PARAM",
        "WM_ERRCODE_NO_CHILD",
        "WM_ERRCODE_FIFO_ERROR",
        "WM_ERRCODE_TIMEOUT",
        "WM_ERRCODE_SEND_QUEUE_FULL",
        "WM_ERRCODE_NO_ENTRY",
        "WM_ERRCODE_OVER_MAX_ENTRY",
        "WM_ERRCODE_INVALID_POLLBITMAP",
        "WM_ERRCODE_NO_DATA",
        "WM_ERRCODE_SEND_FAILED",
        "WM_ERRCODE_DCF_TEST",
        "WM_ERRCODE_WL_INVALID_PARAM",
        "WM_ERRCODE_WL_LENGTH_ERR",
        "WM_ERRCODE_FLASH_ERROR"
    };
    
    OS_TPrintf("ERR: %s %s\n", APIID_NAME[arg->apiid], ERRCODE_NAME[arg->errcode] );
}

