blob: 77682e4278437172b602158102f3cf55891e2daa [file] [log] [blame]
/* Copyright 2018,2020 NXP
* SPDX-License-Identifier: Apache-2.0
*/
#include "sm_types.h"
#include "windows.h"
#include <stdlib.h>
#include <stdio.h>
#include "smComSerial.h"
#include "WinDef.h"
#include "WinBase.h"
#include "string.h"
#include <assert.h>
#define REMOTE_JC_SHELL_HEADER_LEN (4)
#define REMOTE_JC_SHELL_MSG_TYPE_APDU_DATA (0x01)
#include "sm_apdu.h"
#define MAX_BUF_SIZE (MAX_APDU_BUF_LENGTH)
#ifdef FLOW_VERBOSE
#define NX_LOG_ENABLE_SMCOM_DEBUG 1
#endif
#include "nxLog_smCom.h"
#include "nxEnsure.h"
#define MTY_ATR 0x00
#define MTY_CLOSE 0x03
#define NAD 0x00
static U8 Header[2] = {0x01, 0x00};
static U8 sockapdu[MAX_BUF_SIZE];
static U8 response[MAX_BUF_SIZE];
static U8 *pCmd = (U8 *)&sockapdu;
static U8 *pRsp = (U8 *)&response;
static HANDLE gpComHandle = INVALID_HANDLE_VALUE;
static void escapeComPortName(char pOutPortName[20], const char *iPortName)
{
ENSURE_OR_GO_EXIT(iPortName != NULL);
ENSURE_OR_GO_EXIT(strlen(iPortName) < 20);
strncpy(pOutPortName, iPortName, strlen(iPortName));
if (0 == _strnicmp(iPortName, "COM", 3)) {
long number = atol(&iPortName[3]);
if (number > 4) {
_snprintf(pOutPortName, 20, "\\\\.\\%s", iPortName);
}
}
else {
_snprintf(pOutPortName, 20, "%s", iPortName);
}
exit:
return;
}
U32 smComVCom_Open(void** vcom_ctx, const char *pComPortString)
{
U32 status = 0;
COMMTIMEOUTS cto;
char escaped_port_name[20] = {0};
HANDLE pComHandle = INVALID_HANDLE_VALUE;
#ifdef UNICODE
wchar_t wPortName[20] = {0};
#endif
/* Prepare CTO structure */
cto.ReadTotalTimeoutConstant = 500;
cto.ReadTotalTimeoutMultiplier = 0;
cto.ReadIntervalTimeout = 10;
cto.WriteTotalTimeoutConstant = 0;
cto.WriteTotalTimeoutMultiplier = 0;
escapeComPortName(escaped_port_name, pComPortString);
printf("Opening COM Port '%s'\n", escaped_port_name);
if (pComHandle != INVALID_HANDLE_VALUE) {
printf("\n Already COM Port Open \n ");
return SMCOM_COM_ALREADY_OPEN;
}
#ifdef UNICODE
mbstowcs(wPortName, escaped_port_name, sizeof(wPortName) / sizeof(wPortName[0]));
pComHandle = CreateFile(wPortName, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, NULL);
#else
pComHandle = CreateFile(escaped_port_name, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, NULL);
#endif
status = GetLastError();
if (status == ERROR_SUCCESS) {
status = smComVCom_SetState(pComHandle);
if (status == 0) {
if (SetCommTimeouts(pComHandle, &cto) == false) {
status = 1;
}
}
}
else if (ERROR_FILE_NOT_FOUND == status) {
printf("ERROR! Failed opening '%s'. ERROR=ERROR_FILE_NOT_FOUND\n", pComPortString);
}
else if (ERROR_ACCESS_DENIED == status) {
printf("ERROR! Failed opening '%s'. ERROR=ERROR_ACCESS_DENIED\n", pComPortString);
}
else if (pComHandle == INVALID_HANDLE_VALUE) {
if (status == 0)
status = 1; /* Over ride - it's a failure */
printf("ERROR! Failed opening '%s'. ERROR=%X\n", escaped_port_name, status);
}
if (vcom_ctx == NULL) {
gpComHandle = pComHandle;
}
else {
*vcom_ctx = pComHandle;
gpComHandle = pComHandle;
}
return status;
}
U32 smComVCom_SetState(void* conn_ctx)
{
DCB dcb;
memset(&dcb, 0, sizeof(dcb));
HANDLE pComHandle = (conn_ctx == NULL) ? gpComHandle : (HANDLE)conn_ctx;
dcb.DCBlength = sizeof(DCB);
dcb.BaudRate = 115200;
dcb.fBinary = true;
dcb.fParity = false;
dcb.fOutxCtsFlow = false;
dcb.fOutxDsrFlow = false;
dcb.fDtrControl = DTR_CONTROL_DISABLE;
dcb.fDsrSensitivity = false;
dcb.fTXContinueOnXoff = true;
dcb.fOutX = false;
dcb.fInX = false;
dcb.fErrorChar = false;
dcb.fNull = false;
dcb.fRtsControl = RTS_CONTROL_DISABLE;
dcb.fAbortOnError = false;
dcb.XonLim = 0;
dcb.XoffLim = 0;
dcb.ByteSize = 8;
dcb.Parity = NOPARITY;
dcb.StopBits = ONESTOPBIT;
if (SetCommState(pComHandle, &dcb) == false) {
return 1;
}
else {
EscapeCommFunction(pComHandle, SETDTR);
smCom_Init(&smComVCom_Transceive, &smComVCom_TransceiveRaw);
return 0;
}
}
U32 smComVCom_GetATR(void* conn_ctx, U8 *pAtr, U16 *atrLen)
{
int retval;
HANDLE pComHandle = (conn_ctx == NULL) ? gpComHandle : (HANDLE)conn_ctx;
U32 expectedLength = 0;
U32 totalReceived = 0;
U8 lengthReceived = 0;
DWORD WrittenLen = 0;
U8 status;
U32 rc = 1;
// wait 256 ms
U8 ATRCmd[4] = {MTY_ATR, NAD, 0, 0};
ENSURE_OR_GO_EXIT(pAtr != NULL);
ENSURE_OR_GO_EXIT(atrLen != NULL);
LOG_MAU8_D("Get ATR", ATRCmd, sizeof(ATRCmd));
status = WriteFile(pComHandle, ATRCmd, sizeof(ATRCmd), &WrittenLen, NULL);
if ((status == 0) || (WrittenLen != sizeof(ATRCmd))) {
return 1;
}
expectedLength = REMOTE_JC_SHELL_HEADER_LEN; // remote JC shell header length
while (totalReceived < expectedLength) {
U32 maxCommLength;
DWORD numBytesRead = 0;
if (lengthReceived == 0) {
maxCommLength = REMOTE_JC_SHELL_HEADER_LEN - totalReceived;
}
else {
maxCommLength = expectedLength - totalReceived;
}
status = ReadFile(pComHandle, (char *)&pAtr[totalReceived], maxCommLength, &numBytesRead, NULL);
retval = numBytesRead;
if ((retval < 0) || (status == 0)) {
fprintf(stderr, "Client: recv() failed: error %i.\n", retval);
return 1;
}
else {
totalReceived += retval;
}
if ((totalReceived >= REMOTE_JC_SHELL_HEADER_LEN) && (lengthReceived == 0)) {
expectedLength += ((pAtr[2] << 8) | (pAtr[3]));
lengthReceived = 1;
}
}
retval = totalReceived;
LOG_AU8_D(pAtr, retval);
retval -= 4; // Remove the 4 bytes of the Remote JC Terminal protocol
memmove(pAtr, pAtr + 4, retval);
*atrLen = (U16)retval;
rc = 0;
exit:
return rc;
}
U32 smComVCom_Transceive(void *conn_ctx, apdu_t *pApdu)
{
int retval;
#if defined(LOG_SOCK)
int i;
#endif
U32 txLen = 0;
U32 expectedLength = 0;
U32 totalReceived = 0;
U8 lengthReceived = 0;
U8 status;
DWORD WrittenLen = 0;
U32 rv = SMCOM_SND_FAILED;
HANDLE pComHandle = (conn_ctx == NULL) ? gpComHandle : (HANDLE)conn_ctx;
ENSURE_OR_GO_EXIT(pApdu != NULL);
pApdu->rxlen = 0;
memset(sockapdu, 0x00, MAX_BUF_SIZE);
memset(response, 0x00, MAX_BUF_SIZE);
// remote JC Terminal header construction
txLen = pApdu->buflen;
memcpy(pCmd, Header, sizeof(Header));
pCmd[2] = (txLen & 0xFF00) >> 8;
pCmd[3] = txLen & 0xFF;
memcpy(&pCmd[4], pApdu->pBuf, pApdu->buflen);
pApdu->buflen += 4; /* header & length */
#ifdef LOG_SOCK
sm_printf(CONSOLE, " send: ");
for (i = 4; i < (txLen + 4); i++) {
sm_printf(CONSOLE, "%02X", pCmd[i]);
}
sm_printf(CONSOLE, "\n");
#endif
LOG_MAU8_D("H>", pCmd, 4);
LOG_MAU8_D("Tx>", pCmd + 4, pApdu->buflen - 4);
status = WriteFile(pComHandle, pCmd, pApdu->buflen, &WrittenLen, NULL);
if ((status == 0) || (WrittenLen != pApdu->buflen)) {
fprintf(stderr, "Client: send() failed: error %i.\n", WrittenLen);
return SMCOM_SND_FAILED;
}
expectedLength = REMOTE_JC_SHELL_HEADER_LEN; // remote JC shell header length
while (totalReceived < expectedLength) {
DWORD numBytesRead = 0;
status = ReadFile(pComHandle, (char *)&pRsp[totalReceived], MAX_BUF_SIZE, &numBytesRead, NULL);
retval = numBytesRead;
if ((retval < 0) || (status == 0)) {
fprintf(stderr, "Client: recv() failed: error %i.\n", retval);
rv = SMCOM_RCV_FAILED;
goto exit;
}
else {
totalReceived += retval;
}
if ((totalReceived >= REMOTE_JC_SHELL_HEADER_LEN) && (lengthReceived == 0)) {
expectedLength += ((pRsp[2] << 8) | (pRsp[3]));
lengthReceived = 1;
}
}
retval = totalReceived;
retval -= 4; // Remove the 4 bytes of the Remote JC Terminal protocol
memcpy(pApdu->pBuf, &pRsp[4], retval);
LOG_MAU8_D("<H", pRsp, 4);
LOG_MAU8_D("<Rx", pApdu->pBuf, retval);
#ifdef LOG_SOCK
sm_printf(CONSOLE, " recv: ");
for (i = 0; i < retval; i++) {
sm_printf(CONSOLE, "%02X", pApdu->pBuf[i]);
}
sm_printf(CONSOLE, "\n");
#endif
pApdu->rxlen = (U16)retval;
// reset offset for subsequent response parsing
pApdu->offset = 0;
rv = SMCOM_OK;
exit:
return rv;
}
U32 smComVCom_TransceiveRaw(void* conn_ctx, U8 *pTx, U16 txLen, U8 *pRx, U32 *pRxLen)
{
DWORD retval;
U32 answerReceived = 0;
U32 len = 0;
U8 status = 0;
DWORD WrittenLen = 0;
#if defined(LOG_SOCK) || defined(DBG_LOG_SOCK)
int i;
#endif
U32 readOffset = 0;
U8 headerParsed = 0;
U8 correctHeader = 0;
U32 rv = SMCOM_COM_FAILED;
HANDLE pComHandle = (conn_ctx == NULL) ? gpComHandle : (HANDLE)conn_ctx;
ENSURE_OR_GO_EXIT(pTx != NULL);
ENSURE_OR_GO_EXIT(pRx != NULL);
ENSURE_OR_GO_EXIT(pRxLen != NULL);
memset(sockapdu, 0x00, MAX_BUF_SIZE);
memset(response, 0x00, MAX_BUF_SIZE);
memcpy(pCmd, Header, 2);
pCmd[2] = (txLen & 0xFF00) >> 8;
pCmd[3] = (txLen & 0x00FF);
memcpy(&pCmd[4], pTx, txLen);
txLen += 4; /* header + len */
#ifdef DBG_LOG_SOCK
sm_printf(CONSOLE, " full send: ");
for (i = 0; i < txLen; i++) {
sm_printf(CONSOLE, "%02X", pCmd[i]);
}
sm_printf(CONSOLE, "\n");
#endif
LOG_MAU8_D("H>", pCmd, 4);
LOG_MAU8_D("Tx>", pCmd + 4, txLen - 4);
status = WriteFile(pComHandle, pCmd, txLen, &WrittenLen, NULL);
if ((status == false) || (WrittenLen != txLen)) {
fprintf(stderr, "Client: send() failed: error %i.\n", WrittenLen);
return SMCOM_SND_FAILED;
}
else {
#ifdef DBG_LOG_SOCK
sm_printf(CONSOLE, "Client: send() is OK.\n");
#endif
}
#ifdef LOG_SOCK
sm_printf(CONSOLE, " send: ");
for (i = 4; i < txLen; i++) {
sm_printf(CONSOLE, "%02X", pCmd[i]);
}
sm_printf(CONSOLE, "\n");
#endif
retval = REMOTE_JC_SHELL_HEADER_LEN; // receive at least the JCTerminal header
while ((retval > 0) || (answerReceived == 0)) {
status = ReadFile(pComHandle, (char *)pRsp, MAX_BUF_SIZE, &retval, NULL);
if ((retval < 0) || (status == 0)) {
return SMCOM_RCV_FAILED;
}
else // data received
{
if (retval > 4) {
LOG_MAU8_D("<H", pRsp, 4);
LOG_MAU8_D("<Rx", pRsp + 4, retval - 4);
}
while (retval > 0) // parse all bytes
{
if (headerParsed == 1) // header already parsed; get data
{
if (retval >= (S32)len) {
if (correctHeader == 1) {
memcpy(&pRx[0], &pRsp[readOffset], len);
answerReceived = 1;
}
else {
// reset header parsed
readOffset += len;
headerParsed = 0;
}
retval -= len;
if (retval == 0) // no data left, reset readOffset
{
readOffset = 0;
}
}
else {
// data too small according header => Error
fprintf(stderr, "Failed reading data %x %x\n", retval, len);
return SMCOM_RCV_FAILED;
}
}
else // parse header
{
len = ((pRsp[readOffset + 2] << 8) | (pRsp[readOffset + 3]));
if (pRsp[readOffset] == REMOTE_JC_SHELL_MSG_TYPE_APDU_DATA) {
// type correct => copy the data
retval -= REMOTE_JC_SHELL_HEADER_LEN;
if (retval > 0) // data left to read
{
readOffset += REMOTE_JC_SHELL_HEADER_LEN;
}
correctHeader = 1;
}
else {
// type incorrect => skip the data as well and try again if data are left
readOffset += REMOTE_JC_SHELL_HEADER_LEN;
retval -= REMOTE_JC_SHELL_HEADER_LEN;
correctHeader = 0;
}
headerParsed = 1;
}
}
}
}
#ifdef LOG_SOCK
sm_printf(CONSOLE, " recv: ");
for (i = 0; i < len; i++) {
sm_printf(CONSOLE, "%02X", pRx[i]);
}
sm_printf(CONSOLE, "\n");
#endif
*pRxLen = len;
rv = SMCOM_OK;
exit:
return rv;
}
U32 smComVCom_Close(void* conn_ctx)
{
int retval;
U16 status;
U32 u32status;
U8 Cmd[4] = {MTY_CLOSE, NAD, 0, 0};
DWORD WrittenLen = 0;
U32 totalReceived = 0;
U8 lengthReceived = 0;
U32 expectedLength = 0;
HANDLE pComHandle = (conn_ctx == NULL) ? gpComHandle : (HANDLE)conn_ctx;
status = WriteFile(pComHandle, Cmd, sizeof(Cmd), &WrittenLen, NULL);
if ((status == 0) || (WrittenLen != sizeof(Cmd))) {
return 1;
}
expectedLength = REMOTE_JC_SHELL_HEADER_LEN; // remote JC shell header length
while (totalReceived < expectedLength) {
U32 maxCommLength;
DWORD numBytesRead = 0;
if (lengthReceived == 0) {
maxCommLength = REMOTE_JC_SHELL_HEADER_LEN - totalReceived;
}
else {
maxCommLength = expectedLength - totalReceived;
}
status = ReadFile(pComHandle, (char *)&pRsp[totalReceived], maxCommLength, &numBytesRead, NULL);
retval = numBytesRead;
if ((retval < 0) || (status == 0)) {
fprintf(stderr, "Client: recv() failed: error %i.\n", retval);
return 1;
}
else {
totalReceived += retval;
}
if ((totalReceived >= REMOTE_JC_SHELL_HEADER_LEN) && (lengthReceived == 0)) {
expectedLength += ((pRsp[2] << 8) | (pRsp[3]));
lengthReceived = 1;
}
}
retval = totalReceived;
status = CloseHandle(pComHandle);
pComHandle = INVALID_HANDLE_VALUE;
u32status = GetLastError();
if (u32status == ERROR_SUCCESS)
return SMCOM_OK;
else {
status = (U16)u32status;
return status;
}
}