debuggercdi/com.nokia.carbide.trk.support/Native/GetTRKVersion/GetTRKVersion.cpp
author timkelly
Tue, 04 May 2010 09:39:10 -0500
branchRCL_2_4
changeset 1325 98aebbb322f3
parent 1115 f2b4c39f3e7b
permissions -rw-r--r--
Fix bug 11210. NPEs when editing SBSv2 config tab data on old SBSv2 build configs. Now allows saving SBSv2 variant data for old SBSv2 configs (i.e. before we display the build alias in the config name)

/*
* Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies).
* All rights reserved.
* This component and the accompanying materials are made available
* under the terms of the License "Eclipse Public License v1.0"
* which accompanies this distribution, and is available
* at the URL "http://www.eclipse.org/legal/epl-v10.html".
*
* Initial Contributors:
* Nokia Corporation - initial contribution.
*
* Contributors:
*
* Description: 
*
*/
// GetTRKVersion.cpp : Defines the entry point for the DLL application.
//

#include "stdafx.h"
#include "GetTRKVersion.h"
#include "com_nokia_carbide_trk_support_service_TRKConnectedService.h"


BOOL APIENTRY DllMain( HANDLE hModule, 
                       DWORD  ul_reason_for_call, 
                       LPVOID lpReserved
					 )
{
    switch (ul_reason_for_call)
	{
		case DLL_PROCESS_ATTACH:
		case DLL_THREAD_ATTACH:
		case DLL_THREAD_DETACH:
		case DLL_PROCESS_DETACH:
			break;
    }
    return TRUE;
}

#define MIN(x, y) (((x) < (y)) ? (x) : (y))

static const DWORD kNoPingError = -1;
static const DWORD kNoVersionError = -2;

const DWORD kBaudRates[] = { 300, 1200, 2400, 4800, 9600, 19200, 38400, 57600, 115200, 230400 };
enum { FlowHardware = 1, FlowSoftware = 2 }; 

static void SetDCB(DCB* ioDCB, int baudIndex, int dataBits, int parity, int stopBits, int flowControl)
{
	ioDCB->DCBlength = sizeof( DCB );

	ioDCB->BaudRate = kBaudRates[baudIndex];

	ioDCB->ByteSize 		= dataBits+4;
	ioDCB->Parity 			= parity;
	ioDCB->StopBits 		= stopBits;

	// setup hardware flow control
	if (flowControl == FlowHardware)
		ioDCB->fRtsControl	= RTS_CONTROL_HANDSHAKE;
	else
		ioDCB->fRtsControl	= RTS_CONTROL_DISABLE;

	ioDCB->fOutxCtsFlow		= (flowControl == FlowHardware);
	ioDCB->fDtrControl		= DTR_CONTROL_ENABLE;	
	ioDCB->fDsrSensitivity	= false;


	// setup software flow control

	if (flowControl == FlowSoftware)
	{
		ioDCB->fInX 		= ioDCB->fOutX = 1;
		ioDCB->XonChar		= '\021';	// Ctrl-Q;
		ioDCB->XoffChar		= '\023';	// Ctrl-S;
		ioDCB->XonLim		= 100;
		ioDCB->XoffLim		= 100;
	}
	else
	{
		ioDCB->fInX 		= ioDCB->fOutX = 0;
	}

	// other various settings

	ioDCB->fBinary			= TRUE;
	ioDCB->fParity			= TRUE;
	ioDCB->fNull 			= FALSE;
	ioDCB->fAbortOnError	= FALSE;
	
}

static DWORD OpenSerialPort(const char* inPortName, int baudIndex, int dataBits, int parity, int stopBits, int flowControl, HANDLE& serialPortHandle)
{
	serialPortHandle = CreateFile(inPortName,
								GENERIC_READ|GENERIC_WRITE,	
								0,							// lock the port so no one else can get it
								NULL,						// no attributes
								OPEN_EXISTING,
								0, 
								NULL );


	if (serialPortHandle == (HANDLE)-1)
	{
		return GetLastError();
	}

	if (!SetCommMask(serialPortHandle, EV_RXCHAR))	// WaitCommEvent notified by RX Events
	{
		CloseHandle(serialPortHandle); 
		return GetLastError();
	}
	
	if (!SetupComm(serialPortHandle, 4096, 4096))	// 4K Tx and Rx Buffers
	{
		CloseHandle(serialPortHandle); 
		return GetLastError();
	}
	
	// Get rid of any junk that might be sitting there.
	PurgeComm(serialPortHandle, PURGE_TXABORT | PURGE_RXABORT |
		                  PURGE_TXCLEAR | PURGE_RXCLEAR );

	// Using these settings, the ReadFile command will return immediately
	// rather than waiting for a timeout.

	COMMTIMEOUTS  lclCommTimeOuts;
	lclCommTimeOuts.ReadIntervalTimeout			= 0xFFFFFFFF;
	lclCommTimeOuts.ReadTotalTimeoutMultiplier	= 0;
	lclCommTimeOuts.ReadTotalTimeoutConstant	= 0;
	lclCommTimeOuts.WriteTotalTimeoutMultiplier	= 0;
	lclCommTimeOuts.WriteTotalTimeoutConstant	= 2001UL;
	
	if (!SetCommTimeouts(serialPortHandle, &lclCommTimeOuts))
	{
		CloseHandle(serialPortHandle); 
		return GetLastError();
	}

	if (baudIndex >= 0)
	{
		DCB dcb;
		
		if (!GetCommState(serialPortHandle, &dcb))
		{
			CloseHandle(serialPortHandle); 
			return GetLastError();
		}
		
		SetDCB(&dcb, baudIndex, dataBits, parity, stopBits, flowControl);

		if (!SetCommState(serialPortHandle, &dcb))
		{
			CloseHandle(serialPortHandle); 
			return GetLastError();
		}
	}
	
	return ERROR_SUCCESS;
}

static void Disconnect(HANDLE serialPortHandle)
{
	// disable event notification 
	SetCommMask(serialPortHandle, 0);
	
	// drop DTR
	EscapeCommFunction(serialPortHandle, CLRDTR);
	
	// purge any outstanding reads/writes and close device handle
	PurgeComm(serialPortHandle, PURGE_TXABORT | PURGE_RXABORT | PURGE_TXCLEAR | PURGE_RXCLEAR);
							
	CloseHandle(serialPortHandle);
}

// inData: Pointer to data buffer to send
// inSize: Size of data to send
static DWORD SendData(HANDLE serialPortHandle, const void* inData, unsigned long inSize)
{
	DWORD lclNumBytes;

	if (WriteFile(serialPortHandle, inData, inSize, &lclNumBytes, NULL))
		return ERROR_SUCCESS;

	return GetLastError();
}

//	inSize: Size of data to read in bytes
//	outData: Pointer to data buffer to read data into
//	outSize: Size actually read in bytes
static DWORD ReadData(HANDLE serialPortHandle, unsigned long inSize, void* outData, unsigned long &outSize)
{
	// Init our number of bytes read to zero
	outSize = 0;
	
	COMSTAT    lclComStat;
	DWORD      lclErrorFlags;
	DWORD      lclLength;
	
	// clear out any errors in the channel and get the length of the buffer
	ClearCommError(serialPortHandle, &lclErrorFlags, &lclComStat);
	lclLength = MIN(inSize, lclComStat.cbInQue );

	if (lclLength > 0)
	{
		// Read lclLength number of bytes into outData.
		if (!ReadFile(serialPortHandle, outData, lclLength, &outSize, NULL))
			return GetLastError();
	}

	return ERROR_SUCCESS;
}


static DWORD ReceiveData(HANDLE serialPortHandle, unsigned long timeout, unsigned long inSize, void* outData, unsigned long &outSize)
{
	const unsigned long kSleepMillis = 10;

	DWORD error = 0;

	int maxIters = timeout/kSleepMillis;
	int i = 0;
	for (; i < maxIters; i++)
	{
		error = ReadData(serialPortHandle, inSize, outData, outSize);
		if (error != ERROR_SUCCESS)
			return error;
		
		if (outSize > 0)
			break;
		else
			Sleep(kSleepMillis);
	}
	if (i == maxIters)
		return kNoPingError;
	
	return ERROR_SUCCESS;
}

static bool IsVersions3EnabledTRK(long major, long minor, long micro)
{
	return major > 3 || (major == 3 && minor > 2) || (major == 3 && minor == 2 && micro >= 4);
}


__declspec(dllexport)
DWORD GetTRKVersion(const char* inPortName, int baudIndex, int dataBits, int parity, int stopBits, int flowControl, long version[4])
{
	// open the serial port
	HANDLE serialPortHandle = NULL;
	DWORD error = OpenSerialPort(inPortName, baudIndex, dataBits, parity, stopBits, flowControl, serialPortHandle);
	if (error != ERROR_SUCCESS)
		return error;

	// send a ping command
	unsigned char pingTxBuf[] = { 0x7e, 0x00, 0x00, 0xff, 0x7e };
	error = SendData(serialPortHandle, &pingTxBuf, 5);
	if (error != ERROR_SUCCESS) {
		Disconnect(serialPortHandle);
		return error;
	}

	// receive response
	unsigned char pingRxBuf[16];
	unsigned long pingRxSize = 0;
	error = ReceiveData(serialPortHandle, 2001UL, 16, pingRxBuf, pingRxSize);
	if (error != ERROR_SUCCESS) {
		Disconnect(serialPortHandle);
		return error;
	}
	
	// send get version command
	unsigned char versTxBuf[] = { 0x7e, 0x08, 0x01, 0xf6, 0x7e };
	error = SendData(serialPortHandle, &versTxBuf, 5);
	if (error != ERROR_SUCCESS) {
		Disconnect(serialPortHandle);
		return error;
	}

	// receive response
	unsigned char versRxBuf[16];
	unsigned long versRxSize = 0;
	error = ReceiveData(serialPortHandle, 2001UL, 16, versRxBuf, versRxSize);
	if (error != ERROR_SUCCESS) {
		Disconnect(serialPortHandle);
		if (error == kNoPingError)
			return kNoVersionError; // ping ok, but no version
		return error;
	}
	
	if (versRxSize >= 9) {
		version[0] = versRxBuf[4];
		version[1] = versRxBuf[5];
		version[2] = versRxBuf[8];
		version[3] = 0; // unknown TRK

		if (IsVersions3EnabledTRK(version[0], version[1], version[2])) {
			// send versions3 command
			unsigned char vers3TxBuf[] = {0x7e, 0x51, 0x02, 0xac, 0x7e};
			error = SendData(serialPortHandle, &vers3TxBuf, 5);
			if (error != ERROR_SUCCESS) {
				Disconnect(serialPortHandle);
				return error;
			}

			// receive response
			unsigned char vers3RxBuf[64];
			unsigned long vers3RxSize = 0;
			error = ReceiveData(serialPortHandle, 2001UL, 64, vers3RxBuf, vers3RxSize);
			if (error != ERROR_SUCCESS) {
				Disconnect(serialPortHandle);
				return error;
			}

			version[3] = 1; // AppTRK
			unsigned char SYS_TRK_NAME[] = {0x0a, 0x53, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x20, 0x54, 0x52, 0x4b};
			unsigned long SYS_TRK_NAME_LEN = sizeof(SYS_TRK_NAME);
			const int SYS_TRK_NAME_OFFSET = 10;
			if (vers3RxSize >= (SYS_TRK_NAME_LEN + SYS_TRK_NAME_OFFSET)) {
				unsigned char nameBuf[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
				memcpy(nameBuf, vers3RxBuf + SYS_TRK_NAME_OFFSET, SYS_TRK_NAME_LEN);
				if (memcmp(nameBuf, SYS_TRK_NAME, SYS_TRK_NAME_LEN) == 0)
					version[3] = 2; // SysTRK
			}
		}
	}


	Disconnect(serialPortHandle);
	return ERROR_SUCCESS;
}

static const char* GetErrorText(DWORD error)
{
	if (error == kNoPingError)
		return "TRK did not respond";

	else if (error == kNoVersionError)
		return "TRK responded to PING, but not to GETVERSION command";

	else if (error == ERROR_FILE_NOT_FOUND)
		return "Could not open the serial port";

	static char msg[256];
	FormatMessage(
		FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
		NULL,
		error,
		MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language
		(LPTSTR) &msg,
		sizeof(msg) - 1,
		NULL);

	return msg;
}


/*
 * Class:     com_nokia_carbide_trk_support_service_TRKConnectedService
 * Method:    getTRKVersionFromSerial
 * Signature: (Ljava/lang/String;IIIII[I)V
 */
JNIEXPORT void JNICALL Java_com_nokia_carbide_trk_support_service_TRKConnectedService_getTRKVersionFromSerial
  (JNIEnv* env, jclass, jstring jPortName, jint jBaud, jint jDataBits, jint jParity, jint jStopBits, jint jFlowControl, jintArray jVersionInts)
{
	const char* portName = env->GetStringUTFChars(jPortName, NULL);

	jint versionInts[4];
	DWORD error = GetTRKVersion(portName, jBaud, jDataBits, jParity, jStopBits, jFlowControl, versionInts);
	env->SetIntArrayRegion(jVersionInts, 0, 4, versionInts);
	
	env->ReleaseStringUTFChars(jPortName, portName);

	if (error > ERROR_SUCCESS)
	{

		jclass clazz = env->FindClass("Ljava/lang/Exception;");
		env->ThrowNew(clazz, GetErrorText(error));
	}
}