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"));
}