/*
* 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));
}
}