www.pudn.com > WINCEOS.zip > parser.cpp


// 
// Copyright (c) Microsoft Corporation.  All rights reserved. 
// 
// 
// This source code is licensed under Microsoft Shared Source License 
// Version 1.0 for Windows CE. 
// For a copy of the license visit http://go.microsoft.com/fwlink/?LinkId=3223. 
// 
 
#include "btagpriv.h" 
 
typedef void (CAGEngine::*PFNCMDPROC)(LPSTR pszParams, int cchParam); 
 
typedef struct _AT_CMD_TBL { 
    LPSTR pszCommand; 
    UINT uiCmdLen; 
    PFNCMDPROC pfnHandler; 
} AT_CMD_TBL, *PAT_CMD_TBL; 
 
AT_CMD_TBL ATCmdTable[] = { 
    "AT+CKPD=200", 11, &CAGEngine::OnHeadsetButton, 
    "AT+VGM=", 7, &CAGEngine::OnMicVol, 
    "AT+VGS=", 7, &CAGEngine::OnSpeakerVol, 
    "AT+BRSF=", 8, &CAGEngine::OnSupportedFeatures, 
    "AT+CIND?", 8, &CAGEngine::OnReadIndicators, 
    "AT+CIND=?", 9, &CAGEngine::OnTestIndicators, 
    "AT+CMER=", 8, &CAGEngine::OnRegisterIndicatorUpdates, 
    "ATA", 3, &CAGEngine::OnAnswerCall, 
    "AT+CHUP", 7, &CAGEngine::OnHangupCall, 
    "ATD>", 4, &CAGEngine::OnDialMemory, 
    "ATD", 3, &CAGEngine::OnDial, 
    "AT+BLDN", 7, &CAGEngine::OnDialLast, 
    "AT+CCWA=", 8, &CAGEngine::OnEnableCallWaiting, 
    "AT+CLIP=", 8, &CAGEngine::OnEnableCLI, 
    "AT+VTS=", 7, &CAGEngine::OnDTMF, 
    "AT+CHLD=", 8, &CAGEngine::OnCallHold, 
    "AT+BVRA=", 8, &CAGEngine::OnVoiceRecognition, 
    "ATH", 3, &CAGEngine::OnHangupCall, 
    "", 0, NULL 
}; 
 
#ifdef DEBUG 
 
void DbgPrintATCmd(DWORD dwZone, LPSTR szCommand, int cbCommand) 
{ 
    CHAR szDebug[MAX_DEBUG_BUF]; 
 
    for (int i = 0, j = 0; i < cbCommand; i++, j++) { 
        if (szCommand[i] == '\r') { 
            szDebug[j] = '<'; j++; 
            szDebug[j] = 'c'; j++; 
            szDebug[j] = 'r'; j++; 
            szDebug[j] = '>'; 
        } 
        else if (szCommand[i] == '\n') { 
            szDebug[j] = '<'; j++; 
            szDebug[j] = 'l'; j++; 
            szDebug[j] = 'f'; j++; 
            szDebug[j] = '>'; 
        } 
        else { 
            szDebug[j] = szCommand[i]; 
        } 
    } 
 
    szDebug[j] = '\0'; 
 
    DEBUGMSG(dwZone, (L"%hs", szDebug)); 
} 
 
#endif // DEBUG 
 
 
// This function wraps winsock recv 
DWORD MyRecv(SOCKET s, LPSTR szBuf, DWORD cbBuf) 
{  
    DEBUGMSG(ZONE_PARSER, (L"BTAGSVC: My Recv on sock:%d szBuf:0x%X cbBuf:%d\n", s, szBuf, cbBuf)); 
     
    DWORD cbTotalRead = recv(s, szBuf, cbBuf, 0); 
 
    if (0 == cbTotalRead) { 
        // Socket was gracefully disconnected 
        DEBUGMSG(ZONE_PARSER, (L"BTAGSVC: The client socket was gracefully disconnected.\n")); 
    } 
    else if (SOCKET_ERROR == cbTotalRead) { 
        // Socket was forcefully closed 
        DEBUGMSG(ZONE_PARSER, (L"BTAGSVC: The client socket was forcefully disconnected.\n")); 
        cbTotalRead = 0; 
    } 
 
    return cbTotalRead; 
} 
 
 
CATParser::CATParser(void) 
{ 
    m_sockClient = INVALID_SOCKET; 
    m_pHandler = NULL; 
    m_hThread = NULL; 
    m_fShutdown = FALSE; 
    m_cbUnRead = 0; 
 
    m_hBtExtHandler = NULL; 
    m_pfnBthAGExtATHandler = NULL; 
    m_pfnBthAGExtATSetCallback = NULL; 
    m_pfnBthAGOnVoiceTag = NULL; 
} 
 
DWORD WINAPI CATParser::ATParserThread(LPVOID pv) 
{ 
    CATParser* pInst = (CATParser*)pv; 
    pInst->ATParserThread_Int(); 
 
    return 0; 
} 
 
 
// This method reads one command from the peer device. 
DWORD CATParser::ReadCommand(SOCKET s, CBuffer& buffCommand) 
{ 
    CHAR* pszBuf = (LPSTR) m_buffRecv.GetBuffer(0); 
    DWORD cbTotalRead = m_cbUnRead;  
    DWORD cbCommand = 0; 
 
    ASSERT(IsLocked()); 
 
    while (1) { 
        if (cbCommand == cbTotalRead) { 
            // Need to recv more data 
            int cbBuff = cbCommand*2; 
 
            pszBuf = (LPSTR) m_buffRecv.GetBuffer(cbBuff, TRUE); 
            if (! pszBuf) { 
                DEBUGMSG(ZONE_ERROR, (L"BTAGSVC: Parser - Out of resources.\n")); 
                cbTotalRead = 0;                 
                break; 
            } 
 
            cbBuff = m_buffRecv.GetSize(); 
 
            pszBuf += cbTotalRead; 
 
            Unlock(); 
            DWORD cbRead = MyRecv(s, pszBuf, cbBuff-cbTotalRead); 
            Lock(); 
 
            // Reset pointer to start of buffer 
            pszBuf = (LPSTR) m_buffRecv.GetBuffer(0); 
             
            if (0 == cbRead) { 
                cbTotalRead = 0; 
                break; 
            } 
 
            cbTotalRead += cbRead; 
        } 
         
        if ((cbCommand > 0) && (pszBuf[cbCommand] == '\r')) { 
            // We have read the trailing .  Check for  and return the command. 
            if (pszBuf[cbCommand+1] == '\n') { 
                cbCommand++; 
            } 
 
            cbCommand++; 
             
            LPSTR pszCommand = (LPSTR) buffCommand.GetBuffer(cbCommand + 1); 
            if (! pszCommand) { 
                DEBUGMSG(ZONE_ERROR, (L"BTAGSVC: Out of resources!\n")); 
                cbTotalRead = 0; 
                break; 
            } 
             
            memcpy(pszCommand, pszBuf, cbCommand); 
            break; 
        } 
 
        cbCommand++; 
    } 
 
    if (cbTotalRead) { 
        // We have successfully read a command.  See if we have valid data left in our 
        // recv buffer and keep track of how much for next call to ReadCommand. 
 
        ASSERT(cbCommand <= cbTotalRead); 
 
        if (cbCommand < cbTotalRead) { 
            m_cbUnRead = cbTotalRead - cbCommand; 
            memmove(pszBuf, (pszBuf + cbCommand), m_cbUnRead); 
        } 
        else { 
            m_cbUnRead = 0; 
        } 
    } 
    else { 
        m_cbUnRead = 0; 
        cbCommand = 0; 
    } 
     
    return cbCommand; 
} 
 
 
// This method reads and parses a command from the peer device. 
void CATParser::ATParserThread_Int(void) 
{ 
    DEBUGMSG(ZONE_PARSER, (L"BTAGSVC: ATParserThread started.\n")); 
     
    Lock(); 
     
    while (1) { 
        CBuffer buffCommand; 
        CBuffer buffParam; 
        LPSTR pszBuf; 
        LPSTR pszParam; 
        DWORD cbCommand; 
        SOCKET s = m_sockClient; 
 
        DEBUGMSG(ZONE_PARSER, (L"BTAGSVC: Parser thread is calling recv again.\n")); 
 
        cbCommand = ReadCommand(s, buffCommand); 
 
        if (m_fShutdown || (0 == cbCommand)) { 
            DEBUGMSG(ZONE_PARSER, (L"BTAGSVC: Signalled to break from ATParserThread_Int.\n")); 
             
            Unlock(); 
            m_pHandler->CloseAGConnection(TRUE); 
            Lock(); 
             
            break; 
        } 
 
        pszBuf = (LPSTR) buffCommand.GetBuffer(0); 
        if (! pszBuf) { 
            DEBUGMSG(ZONE_ERROR, (L"BTAGSVC: Out of resources!\n")); 
            break; 
        } 
 
        pszBuf[cbCommand] = '\0'; 
 
#ifdef DEBUG 
        DEBUGMSG(ZONE_PARSER, (L"BTAGSVC: ATParserThread - Data was received: ")); 
        DbgPrintATCmd(ZONE_PARSER, pszBuf, cbCommand); 
        DEBUGMSG(ZONE_PARSER, (L"\n")); 
#endif // DEBUG 
 
        // 
        // See if external parser wants to handle this 
        // 
        if (m_pfnBthAGExtATHandler && m_pfnBthAGExtATHandler(pszBuf, cbCommand)) { 
            // Command handled 
            continue; 
        } 
 
        // 
        // Loop through table of AT commands trying to find a match. 
        // 
         
        int i = 0; 
        BOOL fHandled = FALSE; 
        while ((! fHandled) && (! m_fShutdown)) { 
            if (ATCmdTable[i].pszCommand[0] == 0) { 
                if (! _stricmp(pszBuf, "\r\nOK\r\n")) { 
                    m_pHandler->OnOK(); 
                } 
                else if (! _stricmp(pszBuf, "\r\nERROR\r\n")) { 
                    m_pHandler->OnError();                 
                } 
                else { 
                    DEBUGMSG(ZONE_PARSER, (L"BTAGSVC: AT Command is not handled.\n")); 
                    m_pHandler->OnUnknownCommand(); 
                } 
 
                fHandled = TRUE; 
            } 
            else if (! _strnicmp(pszBuf, ATCmdTable[i].pszCommand, ATCmdTable[i].uiCmdLen)) { 
                // Handle AT command 
                // 
                DEBUGMSG(ZONE_PARSER, (L"BTAGSVC: AT Command is being handled.\n")); 
 
                int cchParam = (cbCommand - ATCmdTable[i].uiCmdLen) - 1; 
 
                pszParam = (LPSTR) buffParam.GetBuffer(cchParam + 1); 
                if (! pszParam) { 
                    DEBUGMSG(ZONE_ERROR, (L"BTAGSVC: Out of resources!\n")); 
                    break; 
                } 
                 
                if (0 == cchParam) { 
                    pszParam[0] = '\0'; 
                } 
                else { 
                    strncpy(pszParam, (pszBuf + ATCmdTable[i].uiCmdLen), cchParam); 
                    pszParam[cchParam] = '\0'; // Null terminate removing trailing  
                } 
 
                Unlock(); 
                (m_pHandler->*(ATCmdTable[i].pfnHandler))(pszParam, cchParam); 
                Lock(); 
                fHandled = TRUE; 
            } 
             
            i++; 
        } 
    } 
 
    Unlock(); 
 
    if (! m_fShutdown) { 
        Stop(); 
    } 
 
    DEBUGMSG(ZONE_PARSER, (L"BTAGSVC: ATParserThread exiting.\n")); 
} 
 
 
DWORD SendATCommandCallback(LPSTR szCommand, DWORD cbCommand) 
{ 
    DEBUGMSG(ZONE_WARN, (L"BTAGSVC: SendATCommandCallback - external handler is sending a command.\n")); 
 
    if (! g_pAGEngine) { 
        return ERROR_NOT_READY; 
    } 
     
    return g_pAGEngine->ExternalSendATCommand(szCommand, cbCommand); 
} 
 
void SetCallback(PFN_BthAGATSetCallback pfn) 
{ 
    __try { 
        pfn(SendATCommandCallback); 
    } __except (1) { 
        DEBUGMSG(ZONE_ERROR, (L"BTAGSVC: SetCallback - exception in call to BthAGExtATSetCallback.\n", BTATHANDLER_HANDLER_API)); 
    } 
} 
 
 
// This method initializes the parser 
DWORD CATParser::Init(void) 
{ 
    DWORD cbRead; 
    WCHAR wszExtHandlerMod[MAX_PATH]; 
    BOOL fUseDefaultMod = TRUE; 
    HKEY hk; 
 
    DWORD dwErr = RegOpenKeyEx(HKEY_LOCAL_MACHINE, RK_AUDIO_GATEWAY, 0, 0, &hk); 
    if (dwErr == ERROR_SUCCESS) { 
        cbRead = sizeof(wszExtHandlerMod);     
        dwErr = RegQueryValueEx(hk, _T("BTAGExtModule"), 0, NULL, (PBYTE)wszExtHandlerMod, &cbRead); 
        if (dwErr == ERROR_SUCCESS) { 
            fUseDefaultMod = FALSE; 
        } 
 
        RegCloseKey(hk); 
    } 
 
    if (fUseDefaultMod) { 
        m_hBtExtHandler = LoadLibrary(DEFAULT_BTEXTHANDLER_MODULE); 
    } 
    else { 
        m_hBtExtHandler = LoadLibrary(wszExtHandlerMod); 
    } 
 
    if (! m_hBtExtHandler) { 
        DEBUGMSG(ZONE_WARN, (L"BTAGSVC: Warning - did not load AT extension Handler.\n")); 
        goto exit; 
    } 
 
    m_pfnBthAGOnVoiceTag = (PFN_BthAGOnVoiceTag) GetProcAddress(m_hBtExtHandler, BTAGEXT_ON_VOICETAG); 
    if (! m_pfnBthAGOnVoiceTag) { 
        DEBUGMSG(ZONE_WARN, (L"BTAGSVC: Warning - could not GetProcAddress for %s.\n", BTAGEXT_ON_VOICETAG)); 
        //not a fatal error, continue 
    } 
 
    m_pfnBthAGExtATHandler = (PFN_BthAGATHandler) GetProcAddress(m_hBtExtHandler, BTATHANDLER_HANDLER_API); 
    if (! m_pfnBthAGExtATHandler) { 
        DEBUGMSG(ZONE_ERROR, (L"BTAGSVC: Error - could not GetProcAddress for %s.\n", BTATHANDLER_HANDLER_API)); 
        goto exit; 
    } 
 
    m_pfnBthAGExtATSetCallback = (PFN_BthAGATSetCallback) GetProcAddress(m_hBtExtHandler, BTATHANDLER_SETCALLBACK_API); 
    if (! m_pfnBthAGExtATHandler) { 
        DEBUGMSG(ZONE_ERROR, (L"BTAGSVC: Error - could not GetProcAddress for %s.\n", BTATHANDLER_SETCALLBACK_API)); 
        goto exit; 
    } 
 
    SetCallback(m_pfnBthAGExtATSetCallback);         
 
exit: 
    return ERROR_SUCCESS; 
} 
 
 
// This method deinitializes the parser 
void CATParser::Deinit(void) 
{ 
    if (m_hBtExtHandler) { 
       FreeLibrary(m_hBtExtHandler); 
       m_hBtExtHandler = NULL; 
    } 
 
    m_pfnBthAGExtATHandler = NULL; 
    m_pfnBthAGExtATHandler = NULL; 
    m_pfnBthAGOnVoiceTag = NULL; 
} 
 
 
// This method is called to start the parser module 
DWORD CATParser::Start(CAGEngine* pHandler, SOCKET sockClient) 
{ 
    DWORD dwRetVal = ERROR_SUCCESS; 
 
    DEBUGMSG(ZONE_PARSER, (L"BTAGSVC: AT Command Parser is being started.\n")); 
 
    Lock(); 
 
    if (m_hThread) { 
        DEBUGMSG(ZONE_WARN, (L"BTAGSVC: Already started AT Parser.\n")); 
        dwRetVal = ERROR_ALREADY_INITIALIZED; 
        goto exit; 
    } 
 
    m_pHandler      = pHandler; 
    m_sockClient    = sockClient; 
    m_fShutdown     = FALSE; 
    m_cbUnRead      = 0; 
     
    m_hThread = CreateThread(NULL, 0, ATParserThread, this, 0, NULL); 
    if (! m_hThread) { 
        dwRetVal = GetLastError(); 
        goto exit; 
    } 
 
exit: 
    Unlock(); 
    return dwRetVal; 
} 
 
 
// This method is called to stop the parser module 
void CATParser::Stop(void) 
{ 
    DEBUGMSG(ZONE_PARSER, (L"BTAGSVC: Stopping AT Command Parser.\n")); 
     
    Lock(); 
 
    HANDLE h = m_hThread; 
    m_fShutdown = TRUE; 
    m_hThread = NULL; 
 
    if (m_sockClient != INVALID_SOCKET) { 
        DEBUGMSG(ZONE_PARSER, (L"BTAGSVC: Closing client socket %d.\n", m_sockClient)); 
        closesocket(m_sockClient); 
        m_sockClient = INVALID_SOCKET; 
        BthAGPhoneExtEvent(AG_PHONE_EVENT_BT_CTRL, 0, NULL); 
    } 
     
    Unlock(); 
 
    if (h) { 
        if (h != (HANDLE) GetCurrentThreadId()) { 
            DEBUGMSG(ZONE_PARSER, (L"BTAGSVC: Waiting for ATParserThread (id=%d) to exit.\n", h)); 
            WaitForSingleObject(h, INFINITE); 
        } 
 
        CloseHandle(h); 
    } 
 
    DEBUGMSG(ZONE_PARSER, (L"BTAGSVC: AT Command Parser is stopped.\n")); 
}