/*****************************************************************************
 * Copyright (C) Gemalto M2M GmbH
 *****************************************************************************
 * Project: IMC-RIL-Driver
 *****************************************************************************
 * Author : Wireless Modules RD SW A2
 *****************************************************************************
 * $Workfile: Y:/z_ms-d/src/working/Quinn/RilDrv/exat.cpp $
 * $Revision: /main/ms-d/25 $
 *     $Date: 2013-06-27 16:00:10 $
 *   $Author: pmackent $
 *****************************************************************************
 * Target:
 *  Microsoft Windows Mobile 5.0
 *****************************************************************************
 * Description:
 *  Gemalto IMC RIL
 *****************************************************************************
 *
 * $Log: Y:/z_ms-d/src/working/Quinn/RilDrv/exat.cpp$ 
 * 
 * /main/ms-d/25   2013-06-27 16:00:10   pmackent 
 * Adaptions for EHS5 Rel1->Rel2 update
 *
 * /main/ms-d/24   2013-05-23 13:29:10   pmackent
 * Handle CE6 compiler warning
 *
 * /main/ms-d/23   2013-05-23 11:53:03   pmackent
 * Improved handling of CME ERRORs
 *
 * /main/ms-d/22   2013-04-05 09:53:34   pmackent
 * Finish EHS5 Java midlet update
 * Add RHA-API for bootloader pin on EHS5
 *
 * /main/ms-d/21   2013-03-05 11:33:34   pmackent
 * Handle ^SYSSTART URC for EHS5 Rel2
 *
 * /main/ms-d/20   2012-11-01 17:12:45   pmackent
 * Retries for SCFG configuration
 *
 * /main/ms-d/19   2012-11-01 09:06:20   pmackent
 * Add CME-Error 3
 *
 * /main/ms-d/18   2012-09-26 10:57:20   pmackent
 * Correction for last check in: accidentily checked in unchanged file
 *
 * /main/ms-d/17   2012-09-14 16:36:56   pmackent
 * Handle CME-Error 371 (AT$PRL in GSM mode) (IPIS100082280)
 *
 * /main/ms-d/16   2011-09-26 13:34:28   ctna0256
 * Comply with CE6 warning level 4
 *
 * /main/ms-d/15   2010-11-26 10:19:10   ctna0256
 * Allow faster pre-init AT commands
 *
 * /main/ms-d/14   2010-11-04 15:47:09   ctna0256
 * New configuration of SDPORT, C-IMEI and SVN
 *
 * /main/ms-d/13   2008-08-22 10:23:43   ctna0256
 * Set Cinterion copyright text
 *
 * /main/ms-d/12   2008-08-21 09:34:24   ctna0256
 * Prepared for new disclaimer handling
 *
 * /main/ms-d/11   2008-04-29 13:39:53   mp49210
 * Removed Lint warning
 *
 * /main/ms-d/10   2008-04-16 16:24:09   mp49210
 * Debug zones for RHA traces
 *
 * /main/ms-d/9   2008-04-03 14:41:33   mp49210
 * Introduction of new debug zones
 *
 * /main/ms-d/8   2008-02-08 13:14:09   mp49210
 * Prevent start of module, if IMEI is missing
 *
 * /main/ms-d/7   2007-11-21 13:34:59   mp49210
 * Lint compatibility
 *
 * /main/ms-d/6   2007-08-28 14:28:38   mp49210
 * Avoid recursive call of ExGetATResponse()
 *
 * /main/ms-d/5   2007-08-10 10:32:45   mp49210
 * Apply COM timeout settings for pre-init phase of the RIL
 *
 * /main/ms-d/4   2007-08-08 11:16:42   mp49210
 * Introduction of audio profile download
 *
 * /main/ms-d/3   2007-07-11 15:11:29   mp49210
 * Optional additional waiting period before sending AT commands
 *
 * /main/ms-d/2   2007-02-26 16:16:43   mp49210
 * Reduce number of CEPC default retries
 *
 * /main/ms-d/1   2006-10-25 16:53:29   wm36264
 * First transformation from classic RIL
 *
*/

//////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2008 Gemalto M2M GmbH
// Copyright (C) 2006-2008 Siemens AG
// Copyright (C) 2003 Intrinsyc Europe Ltd.
//
// Version: 1.0
// Date:    dummy
//
//////////////////////////////////////////////////////////////////////////////

/*++
THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF
ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
PARTICULAR PURPOSE.
Copyright (c) 1995-1999  Microsoft Corporation
--*/

#include "precomp.h"

// Retries for PreInit-ATCommands. Usually there are no additional retries necessary,
// but when starting a system with all debug options enabled (e.g. CEPC) the serial
// interface might not be sufficiently stable the first time so additional retries might
// be useful.
#if defined(X86_EMUL)
#define MIN_EX_ATCMD_RETRIES 2
#else
#define MIN_EX_ATCMD_RETRIES 0
#endif

// Bytes sent in one block of binary data.
#define EXSENDBYTES_BLOCK   512

//
//
//
BOOL CRilHandle::ExSendBytes(COM_PORT_TYPE PortType, BYTE *pbBytes, DWORD dwCmdLen, DWORD dwDelay) const {
DWORD dwWritten;
UINT nIdx;

   if (!RHA_IsRadioPwrOn()) {
      RILRetailTraceZone(ZONE_ERROR, (TEXT("RILDrv: ExSendBytes(%s) - Module powered off externally\r\n"), g_ComManager.GetDebugName(PortType)));
      g_PowerManager.ForcePowerOff();
      return FALSE;
   }
   RILRetailTraceZone(ZONE_ATCOMMAND, (TEXT("RILDrv: ExSendBytes(%s, len=%d)\r\n"), g_ComManager.GetDebugName(PortType), dwCmdLen));

   if (dwDelay == 0)
        return DOWNST_WRITEFILE(g_ComManager.GetPortHandle(PortType), pbBytes, dwCmdLen, &dwWritten);

   for (nIdx = 0; nIdx < dwCmdLen; nIdx++)
   {
     Sleep(dwDelay);
     if (!DOWNST_WRITEFILE(g_ComManager.GetPortHandle(PortType), pbBytes+nIdx, 1, &dwWritten))
        return FALSE;
   }
   return TRUE;
}

BOOL CRilHandle::ExSendBytesLarge(COM_PORT_TYPE PortType, BYTE *pbBytes, DWORD dwCmdLen) const {
DWORD dwWritten = 0;
DWORD dwWrittenUSB;
DWORD dwBytes;

   if (!RHA_IsRadioPwrOn()) {
      RILRetailTraceZone(ZONE_ERROR, (TEXT("RILDrv: ExSendBytesLarge(%s) - Module powered off externally\r\n"), g_ComManager.GetDebugName(PortType)));
      g_PowerManager.ForcePowerOff();
      return FALSE;
   }
   RILRetailTraceZone(ZONE_ATCOMMAND, (TEXT("RILDrv: ExSendBytesLarge(%s, len=%d)\r\n"), g_ComManager.GetDebugName(PortType), dwCmdLen));

   while (dwWritten < dwCmdLen) {
      dwBytes = min(EXSENDBYTES_BLOCK, dwCmdLen - dwWritten);
      if (!DOWNST_WRITEFILE(g_ComManager.GetPortHandle(PortType), pbBytes + dwWritten, dwBytes, &dwWrittenUSB))
         return FALSE;
      dwWritten += dwBytes;
   }

   return TRUE;
}

//
//
//
BOOL CRilHandle::ExSendATCommand(COM_PORT_TYPE PortType, const char *pszATCommand) const {
DWORD dwWritten;

  if (!RHA_IsRadioPwrOn()) {
    RILRetailTraceZone(ZONE_ERROR, ((TEXT("RilDrv: ExSendATCommand(%s) - Module powered off externally\r\n")), g_ComManager.GetDebugName(PortType)));
    (void)g_PowerManager.ForcePowerOff();
    return FALSE;
  }
  Sleep(g_dwWaitBeforeATCommand);
  ExtRILRetailTraceZone(ZONE_ATCOMMAND, TEXT("ExSendATCommand(%s): "), g_ComManager.GetDebugName(PortType), pszATCommand);
  return DOWNST_WRITEFILE(g_ComManager.GetPortHandle(PortType), pszATCommand, strlen(pszATCommand), &dwWritten);
}

//
//
//
BOOL CRilHandle::ExGetATResponse(COM_PORT_TYPE PortType, char *pszATResponse, UINT uiBufLen, DWORD dwTimeout, BOOL fIgnoreTimeout, BOOL fAcceptAllResponses) {
const char*   szAcceptedResponsesRestricted[] = { "OK\r\n", "ERROR\r\n", "", "" };   // Double empty string to avoid Arm memory allignment problems (same below)
const char*   szAcceptedResponsesAll[] = { "\r\nOK\r\n", "\r\nERROR\r\n", "0\r", "4\r", "^SFSA: 0", "CONNECT", "", "", "" };
const char**  szAcceptedResponses;
const char*   szCMEError = "+CME ERROR:";
char*         pszResponse;
DWORD         dwBytesRead;
DWORD         dwResponsePos;
DWORD         dwStartTime;
UINT          uiBufferLength;
BOOL          fRestoreTO = FALSE;
BOOL          fRet = FALSE;
COMMTIMEOUTS  ct, ctOld;;

  memset(&ct,0,sizeof(ct)); // zero struct
  memset(&ctOld,0,sizeof(ctOld)); // zero struct
  if (GetCommTimeouts(g_ComManager.GetPortHandle(PortType), &ct))
  {
    fRestoreTO = TRUE;

    memcpy(&ctOld, &ct, sizeof(COMMTIMEOUTS));
    ct.ReadIntervalTimeout          = MAXDWORD;
    ct.ReadTotalTimeoutMultiplier   = MAXDWORD;
    ct.ReadTotalTimeoutConstant     = 100;
    ct.WriteTotalTimeoutMultiplier  = 1;
    ct.WriteTotalTimeoutConstant    = 3000;

    (void)SetCommTimeouts(g_ComManager.GetPortHandle(PortType), &ct);
  }

  szAcceptedResponses = (fAcceptAllResponses ? szAcceptedResponsesAll : szAcceptedResponsesRestricted);
  // Clear the response buffer
  memset(pszATResponse, 0, uiBufLen);
  uiBufLen--;
  uiBufferLength = uiBufLen;
  pszResponse = pszATResponse;

  dwStartTime = GetTickCount();
  for (;;) {

    // Wait for some data
    if (!DOWNST_READFILE(g_ComManager.GetPortHandle(PortType), pszATResponse, uiBufLen, &dwBytesRead)) {
      RILRetailTraceZone(ZONE_ERROR, (TEXT("RilDrv: ExGetATResponse(%s) - ReadFile failed\r\n"), g_ComManager.GetDebugName(PortType)));
      break;
    }
    // Check if we have received some data
    if (dwBytesRead > 0) {
      DWORD dwEnd;

      // Scan for possible responses
      for (dwResponsePos = 0; strlen(szAcceptedResponses[dwResponsePos]); dwResponsePos++) {
        if (FindString(0, pszResponse, (char*)szAcceptedResponses[dwResponsePos], uiBufferLength, 0)) {
          ExtRILRetailTraceZone(ZONE_ATCOMMAND, TEXT("ExGetATResponse(%s): "), g_ComManager.GetDebugName(PortType), pszResponse);
          fRet = TRUE;
          goto End;
        }
      }
      // Extra handling of CME errors
      if (FindString(0, pszResponse, (char*)szCMEError, uiBufferLength, &dwEnd)) {
        // CME error must be terminated with <CR><LF>
        if (FindString(0, pszResponse + dwEnd, (char*)"\r\n", uiBufferLength - dwEnd, NULL)) {
          ExtRILRetailTraceZone(ZONE_ATCOMMAND, TEXT("ExGetATResponse(%s): "), g_ComManager.GetDebugName(PortType), pszResponse);
          fRet = TRUE;
          goto End;
        }
      }
      if (FindString(0, pszResponse, "^SYSSTART", uiBufferLength, &dwEnd)) {
        // Relevant for EHS5 only: ^SYSSTART detected.
        m_fSysStartDected = TRUE;
      }

      pszATResponse += dwBytesRead;
      uiBufLen -= dwBytesRead;
      if (uiBufLen <= 0) {
        RILRetailTraceZone(ZONE_ERROR, (TEXT("RilDrv: ExGetATResponse(%s) - buffer overflow\r\n"), g_ComManager.GetDebugName(PortType)));
        break;
      }
    }

    if ((GetTickCount() -dwStartTime) >= dwTimeout) {
      RILRetailTraceZone(ZONE_ERROR, (TEXT("RilDrv: ExGetATResponse(%s) - timeout\r\n"), g_ComManager.GetDebugName(PortType)));
      if (!fIgnoreTimeout) {
        DWORD dwWritten = 0;
        char szRspAT[32];

        DOWNST_WRITEFILE(g_ComManager.GetPortHandle(PortType), ABORT_COMMAND, ABORT_COMMAND_SIZE, &dwWritten);
        (void)ExGetATResponse(PortType, szRspAT, sizeof(szRspAT), 1000, TRUE, TRUE);
        if (!ExSendATCmdGetRsp(PortType, "AT\r", szRspAT, sizeof(szRspAT), 1000, 0, TRUE)) {
          (void)BroadcastDWORDBlobNotification(RIL_NOTIFY_DEVSPECIFIC_EMERG_SHUTDOWN, EMERG_SHUTDOWN_POWERLOSS);
          (void)g_PowerManager.ForcePowerOff();
        }
      }
      break;
    }

    if (!RHA_IsRadioPwrOn()) {
      RILRetailTraceZone(ZONE_ERROR, (TEXT("RilDrv: ExGetATResponse(%s) - Module powered off externally\r\n"), g_ComManager.GetDebugName(PortType)));
      (void)g_PowerManager.ForcePowerOff();
      break;
    }

    Sleep(10);
  }

End:

  if (fRestoreTO)
    (void)SetCommTimeouts(g_ComManager.GetPortHandle(PortType), &ctOld);

  return fRet;
}

//
//
//
BOOL CRilHandle::ExSendATCmdGetRsp(COM_PORT_TYPE PortType, const char *pszATCommand, char *pszATResponse, UINT uiBufLen, DWORD dwTimeout, DWORD dwRetries, BOOL fIgnoreTimeout, BOOL fAcceptAllResponses) {

  if (dwRetries < MIN_EX_ATCMD_RETRIES)
    dwRetries = MIN_EX_ATCMD_RETRIES;

  for (DWORD i = 0; (i <= dwRetries) && RHA_IsRadioPwrOn(); i++) {
    if (i) {
      RILRetailTraceZone(ZONE_ERROR, (TEXT("RilDrv: ExSendATCmdGetRsp(%s): Retrying command\r\n"), g_ComManager.GetDebugName(PortType)));
      Sleep(100);
    }

    if (!ExSendATCommand(PortType, pszATCommand)) {
      RILRetailTraceZone(ZONE_ERROR, (TEXT("RilDrv: ExSendATCmdGetRsp(%s): Write failed\r\n"), g_ComManager.GetDebugName(PortType)));
      continue;
    }
    if (ExGetATResponse(PortType, pszATResponse, uiBufLen, dwTimeout, fIgnoreTimeout, fAcceptAllResponses)) {
      return TRUE;
    }
  }

  return FALSE;
}

void CRilHandle::WaitForSysstart(COM_PORT_TYPE PortType)
{
    char           szResponse[MAX_ATCMD_LEN];
    DWORD         dwBytesRead;
    DWORD         dwTimeout = 5000;
    DWORD         dwStartTime;
    BOOL          fRestoreTO = FALSE;
    COMMTIMEOUTS  ct, ctOld;

    // The ^SYSSTART URC might have already shown up.
    if (m_fSysStartDected) return;

    (void)GetRegistryDWORD(HKEY_LOCAL_MACHINE, g_tszRegKeyRIL, TEXT("JavaSysstartTimeout"), &dwTimeout);

    memset(&ct,0,sizeof(ct)); // zero struct
    memset(&ctOld,0,sizeof(ctOld)); // zero struct
    if (GetCommTimeouts(g_ComManager.GetPortHandle(PortType), &ct))
    {
        fRestoreTO = TRUE;

        memcpy(&ctOld, &ct, sizeof(COMMTIMEOUTS));
        ct.ReadIntervalTimeout          = MAXDWORD;
        ct.ReadTotalTimeoutMultiplier   = MAXDWORD;
        ct.ReadTotalTimeoutConstant     = 100;
        ct.WriteTotalTimeoutMultiplier  = 1;
        ct.WriteTotalTimeoutConstant    = 3000;

        (void)SetCommTimeouts(g_ComManager.GetPortHandle(PortType), &ct);
    }

    // Clear the response buffer
    memset(szResponse, 0, MAX_ATCMD_LEN);

    dwStartTime = GetTickCount();
    for (;;) {
        // Wait for some data
        if (!DOWNST_READFILE(g_ComManager.GetPortHandle(PortType), szResponse, MAX_ATCMD_LEN, &dwBytesRead)) {
            RILRetailTraceZone(ZONE_ERROR, (TEXT("RilDrv: WaitForSysstart(%s) - ReadFile failed\r\n"), g_ComManager.GetDebugName(PortType)));
            break;
        }
        // Check if we have received some data
        if (dwBytesRead > 0) {
            ExtRILRetailTraceZone(ZONE_ATCOMMAND, TEXT("WaitForSysstart(%s): "), g_ComManager.GetDebugName(PortType), szResponse);
            // Scan for possible responses
            if (strstr(szResponse, "^SYSSTART") != NULL) {
                RILRetailTraceZone(ZONE_INFO, (TEXT("RilDrv: WaitForSysstart(%s): ^SYSSTART detected\r\n"), g_ComManager.GetDebugName(PortType)));
                m_fSysStartDected = TRUE;
                break;
            }
        }

        if ((GetTickCount() -dwStartTime) >= dwTimeout) {
          RILRetailTraceZone(ZONE_ERROR, (TEXT("RilDrv: WaitForSysstart(%s) - timeout\r\n"), g_ComManager.GetDebugName(PortType)));
          break;
        }

        if (!RHA_IsRadioPwrOn()) {
          RILRetailTraceZone(ZONE_ERROR, (TEXT("RilDrv: WaitForSysstart(%s) - Module powered off externally\r\n"), g_ComManager.GetDebugName(PortType)));
          (void)g_PowerManager.ForcePowerOff();
          break;
        }

        Sleep(10);
    }

    if (fRestoreTO)
        (void)SetCommTimeouts(g_ComManager.GetPortHandle(PortType), &ctOld);

    return;
}

