597 lines
16 KiB
C++
597 lines
16 KiB
C++
/**
|
|
@file ConnectionCOM.cpp
|
|
@author Lime Microsystems (www.limemicro.com)
|
|
@brief Implementation of communications through COM port
|
|
*/
|
|
|
|
#include "ConnectionCOM.h"
|
|
|
|
#include "string.h"
|
|
#ifdef __unix__
|
|
#include <fstream>
|
|
#include <errno.h>
|
|
#include <unistd.h>
|
|
#include <termios.h>
|
|
#include <sys/types.h>
|
|
#include <sys/stat.h>
|
|
#include <fcntl.h>
|
|
#include <stdlib.h>
|
|
#include <iostream>
|
|
#include <stdio.h>
|
|
#endif // LINUX
|
|
|
|
const int COM_RETRY_INTERVAL = 20; //ms
|
|
const int COM_TOTAL_TIMEOUT = 300; //ms
|
|
|
|
/** @brief Initializes com port connection
|
|
*/
|
|
ConnectionCOM::ConnectionCOM()
|
|
{
|
|
currentDeviceIndex = -1;
|
|
connected = false;
|
|
comPortList.clear();
|
|
m_deviceNames.clear();
|
|
m_connectionType = COM_PORT;
|
|
#ifndef __unix__
|
|
hComm = INVALID_HANDLE_VALUE;
|
|
#else
|
|
hComm = -1;
|
|
#endif
|
|
comBaudrate = 9600;
|
|
}
|
|
|
|
/** @brief When object is destroyed it closes it's opened COM port
|
|
*/
|
|
ConnectionCOM::~ConnectionCOM()
|
|
{
|
|
Close();
|
|
}
|
|
|
|
/** @brief Opens connection to first found chip
|
|
@return 0-success
|
|
*/
|
|
IConnection::DeviceStatus ConnectionCOM::Open()
|
|
{
|
|
comPortList.clear();
|
|
if(comPortList.size() == 0)
|
|
FindAllComPorts();
|
|
|
|
m_deviceNames.clear();
|
|
if(m_deviceNames.size() == 0)
|
|
RefreshDeviceList();
|
|
|
|
for(unsigned int i=0; i<m_deviceNames.size(); i++)
|
|
{
|
|
if( Open(i) == SUCCESS)
|
|
{
|
|
currentDeviceIndex = i;
|
|
return SUCCESS;
|
|
}
|
|
}
|
|
return FAILURE;
|
|
}
|
|
|
|
/** @brief Opens connection to selected chip
|
|
@param index chip index in device list
|
|
@return 0-success
|
|
*/
|
|
IConnection::DeviceStatus ConnectionCOM::Open(unsigned index)
|
|
{
|
|
unsigned int toOpen = index;
|
|
Close();
|
|
if(toOpen < m_deviceNames.size() && m_deviceNames.size() > 0 )
|
|
{
|
|
comPortName = m_deviceNames[toOpen];
|
|
IConnection::DeviceStatus status = Open(comPortName.c_str(), comBaudrate);
|
|
if( status == SUCCESS )
|
|
currentDeviceIndex = toOpen;
|
|
return status;
|
|
}
|
|
return FAILURE;
|
|
}
|
|
|
|
/** @brief Closes connection to chip
|
|
*/
|
|
void ConnectionCOM::Close()
|
|
{
|
|
connected = false;
|
|
currentDeviceIndex = -1;
|
|
#ifndef __unix__
|
|
if (hComm != INVALID_HANDLE_VALUE)
|
|
{
|
|
SetCommTimeouts(hComm, &m_ctmoOld);
|
|
CloseHandle(hComm);
|
|
}
|
|
hComm = INVALID_HANDLE_VALUE;
|
|
#else
|
|
if( hComm >= 0)
|
|
{
|
|
close(hComm);
|
|
}
|
|
hComm = -1;
|
|
#endif
|
|
}
|
|
|
|
/** @brief Returns whether chip is connected
|
|
@return chip is connected
|
|
*/
|
|
bool ConnectionCOM::IsOpen()
|
|
{
|
|
#ifndef __unix__
|
|
if (hComm != INVALID_HANDLE_VALUE && TestConnectivity() )
|
|
return true;
|
|
#else
|
|
if( hComm != -1 && TestConnectivity() )
|
|
return true;
|
|
#endif
|
|
return false;
|
|
}
|
|
|
|
int ConnectionCOM::GetOpenedIndex()
|
|
{
|
|
return currentDeviceIndex;
|
|
}
|
|
|
|
/** @brief Sends data through COM port
|
|
@param buffer data buffer to send
|
|
@param length size of data buffer
|
|
@param timeout_ms timeout limit for operation in milliseconds
|
|
@return Number of bytes sent
|
|
*/
|
|
int ConnectionCOM::Write(const unsigned char *buffer, int length, int timeout_ms)
|
|
{
|
|
if(timeout_ms == 0)
|
|
{
|
|
timeout_ms = COM_TOTAL_TIMEOUT;
|
|
}
|
|
int retryCount = 0;
|
|
const int maxRetries = (timeout_ms/COM_RETRY_INTERVAL) > 1 ? (timeout_ms/COM_RETRY_INTERVAL) : 1;
|
|
bool status = false;
|
|
#ifndef __unix__
|
|
unsigned long bytesWriten = 0;
|
|
m_osWOverlap.InternalHigh = 0;
|
|
|
|
for(int i = 0; i<maxRetries && status == false; ++i)
|
|
{
|
|
if (!WriteFile(hComm, buffer, length , &bytesWriten, NULL))
|
|
{
|
|
status = false;
|
|
}
|
|
else
|
|
status = true;
|
|
++retryCount;
|
|
}
|
|
#else
|
|
long bytesWriten = 0;
|
|
for(int i = 0; i<maxRetries && bytesWriten == 0; ++i)
|
|
{
|
|
bytesWriten = write(hComm, buffer, length);
|
|
if(bytesWriten <= 0)
|
|
{
|
|
// if(bytesWriten < 0)
|
|
// MessageLog::getInstance()->write("COM PORT: error writing data\n", LOG_ERROR);
|
|
// if(bytesWriten == 0)
|
|
// MessageLog::getInstance()->write("COM PORT: data bytes sent 0\n", LOG_WARNING);
|
|
status = false;
|
|
}
|
|
else
|
|
status = true;
|
|
++retryCount;
|
|
}
|
|
#endif
|
|
if(bytesWriten == length)
|
|
status = true;
|
|
|
|
return bytesWriten;
|
|
}
|
|
|
|
/** @brief Reads data from COM port
|
|
@param buffer pointer to data buffer for receiving
|
|
@param length number of bytes to read
|
|
@param timeout_ms timeout limit for operation in milliseconds
|
|
@return Number of bytes received
|
|
*/
|
|
int ConnectionCOM::Read(unsigned char *buffer, int length, int timeout_ms)
|
|
{
|
|
if(timeout_ms == 0)
|
|
{
|
|
timeout_ms = COM_TOTAL_TIMEOUT;
|
|
}
|
|
int retryCount = 0;
|
|
const int maxRetries = (timeout_ms/COM_RETRY_INTERVAL) > 1 ? (timeout_ms/COM_RETRY_INTERVAL) : 1;
|
|
bool status = false;
|
|
|
|
memset(buffer, 0, length);
|
|
long bytesReaded = 0;
|
|
unsigned long totalBytesReaded = 0;
|
|
char cRawData[COM_BUFFER_LENGTH];
|
|
unsigned long bytesToRead = length;
|
|
|
|
memset(cRawData, '\0', sizeof(cRawData[0])*COM_BUFFER_LENGTH);
|
|
|
|
for(int i=0; i<maxRetries && status == false; ++i)
|
|
{
|
|
memset(cRawData, '\0', sizeof(cRawData[0])*COM_BUFFER_LENGTH);
|
|
#ifndef __unix__
|
|
|
|
DWORD bytesReceived = 0;
|
|
if ( !ReadFile(hComm, cRawData, bytesToRead, &bytesReceived, NULL) )
|
|
{
|
|
status = false;
|
|
}
|
|
|
|
bytesReaded = bytesReceived;
|
|
#else
|
|
bytesReaded = read(hComm, cRawData, bytesToRead);
|
|
if(bytesReaded <= 0)
|
|
{
|
|
// if(bytesReaded < 0)
|
|
// MessageLog::getInstance()->write("COM PORT: error reading data\n", LOG_ERROR);
|
|
// if(bytesReaded == 0)
|
|
// MessageLog::getInstance()->write("COM PORT: reading 0 bytes\n", LOG_WARNING);
|
|
status = false;
|
|
}
|
|
#endif
|
|
retryCount++;
|
|
|
|
for(int j=0; j<bytesReaded; ++j)
|
|
{
|
|
buffer[totalBytesReaded+j] = cRawData[j];
|
|
}
|
|
|
|
totalBytesReaded += bytesReaded;
|
|
if(totalBytesReaded == bytesToRead)
|
|
status = true;
|
|
}
|
|
|
|
// ss << " read(" << totalBytesReaded << "): ";
|
|
// for(unsigned int i=0; i<64; ++i)
|
|
// ss << int2hex(buffer[i], 1) << " ";
|
|
// ss << " - retries: " << retryCount-1 << endl;
|
|
//MessageLog::getInstance()->write(ss.str(), LOG_DATA);
|
|
|
|
// if(retryCount == maxRetries)
|
|
// MessageLog::getInstance()->write("COM PORT: read data timeout\n", LOG_WARNING);
|
|
//
|
|
// if(totalBytesReaded > length)
|
|
// MessageLog::getInstance()->write("COM PORT: read data corrupted, received length > requested length\n", LOG_ERROR);
|
|
return totalBytesReaded;
|
|
}
|
|
|
|
/** @brief Searches for available COM ports and adds them to list
|
|
*/
|
|
void ConnectionCOM::FindAllComPorts()
|
|
{
|
|
Close();
|
|
comPortList.clear();
|
|
#ifndef __unix__
|
|
HKEY hSERIALCOMM;
|
|
if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, TEXT("HARDWARE\\DEVICEMAP\\SERIALCOMM"), 0, KEY_QUERY_VALUE, &hSERIALCOMM) == ERROR_SUCCESS)
|
|
{
|
|
// Get the max value name and max value lengths
|
|
DWORD dwMaxValueNameLen;
|
|
DWORD dwMaxValueLen;
|
|
DWORD dwQueryInfo = RegQueryInfoKey(hSERIALCOMM, NULL, NULL, NULL, NULL, NULL, NULL, NULL, &dwMaxValueNameLen, &dwMaxValueLen, NULL, NULL);
|
|
if (dwQueryInfo == ERROR_SUCCESS)
|
|
{
|
|
DWORD dwMaxValueNameSizeInChars = dwMaxValueNameLen + 1;
|
|
// Include space for the NULL terminator
|
|
DWORD dwMaxValueNameSizeInBytes = dwMaxValueNameSizeInChars*sizeof(TCHAR);
|
|
DWORD dwMaxValueDataSizeInChars = dwMaxValueLen / sizeof(TCHAR) + 1;
|
|
// Include space for the NULL terminator
|
|
DWORD dwMaxValueDataSizeInBytes = dwMaxValueDataSizeInChars*sizeof(TCHAR);
|
|
|
|
// Allocate some space for the value name and value data
|
|
TCHAR* szValueName = new TCHAR[dwMaxValueNameSizeInChars];
|
|
TCHAR* byValue = new TCHAR[dwMaxValueDataSizeInBytes];
|
|
if (szValueName && byValue)
|
|
{
|
|
// Enumerate all the values underneath HKEY_LOCAL_MACHINE\HARDWARE\DEVICEMAP\SERIALCOMM
|
|
DWORD dwIndex = 0;
|
|
DWORD dwType;
|
|
DWORD dwValueNameSize = dwMaxValueNameSizeInChars;
|
|
DWORD dwDataSize = dwMaxValueDataSizeInBytes;
|
|
memset(szValueName, 0, dwMaxValueNameSizeInBytes);
|
|
memset(byValue, 0, dwMaxValueDataSizeInBytes);
|
|
LONG nEnum = RegEnumValue(hSERIALCOMM, dwIndex, szValueName, &dwValueNameSize, NULL, &dwType, (LPBYTE)byValue, &dwDataSize);
|
|
while (nEnum == ERROR_SUCCESS)
|
|
{
|
|
// If the value is of the correct type, then add it to the array
|
|
if (dwType == REG_SZ)
|
|
{
|
|
char portname[512];
|
|
TCHAR* szPort = byValue;
|
|
int nUserNameLenUnicode = lstrlen( szPort ); // Convert all UNICODE characters
|
|
int nUserNameLen = WideCharToMultiByte( CP_ACP, // ANSI Code Page
|
|
0, // No special handling of unmapped chars
|
|
(LPCWSTR)szPort, // wide-character string to be converted
|
|
nUserNameLenUnicode,
|
|
NULL, 0, // No output buffer since we are calculating length
|
|
NULL, NULL ); // Unrepresented char replacement - Use Default
|
|
TCHAR* pszUserName = new TCHAR[ nUserNameLen ]; // nUserNameLen includes the NULL character
|
|
WideCharToMultiByte( CP_ACP, // ANSI Code Page
|
|
0, // No special handling of unmapped chars
|
|
(LPCWSTR)szPort, // wide-character string to be converted
|
|
nUserNameLenUnicode,
|
|
portname,
|
|
nUserNameLen,
|
|
NULL, NULL ); // Unrepresented char replacement - Use Default
|
|
portname[nUserNameLen] = 0;
|
|
#ifdef UNICODE
|
|
comPortList.push_back(portname);
|
|
#else
|
|
comPortList.push_back(szPort);
|
|
#endif
|
|
}
|
|
// Prepare for the next time around
|
|
dwValueNameSize = dwMaxValueNameSizeInChars;
|
|
dwDataSize = dwMaxValueDataSizeInBytes;
|
|
memset(szValueName, 0, dwMaxValueNameSizeInBytes);
|
|
memset(byValue, 0, dwMaxValueDataSizeInBytes);
|
|
++dwIndex;
|
|
nEnum = RegEnumValue(hSERIALCOMM, dwIndex, szValueName, &dwValueNameSize, NULL, &dwType, (LPBYTE)byValue, &dwDataSize);
|
|
}
|
|
}
|
|
delete szValueName;
|
|
delete byValue;
|
|
}
|
|
// Close the registry key now that we are finished with it
|
|
RegCloseKey(hSERIALCOMM);
|
|
|
|
if (dwQueryInfo != ERROR_SUCCESS)
|
|
SetLastError(dwQueryInfo);
|
|
}
|
|
#else
|
|
char tempBuffer[256];
|
|
string result = "";
|
|
#warning Currently searching only for ACM connections
|
|
system( "ls /dev | grep ttyACM > /tmp/foundSerialPorts.txt");
|
|
|
|
fstream fin;
|
|
fin.open("/tmp/foundSerialPorts.txt", ios::in);
|
|
while(!fin.eof())
|
|
{
|
|
fin.getline(tempBuffer, 256);
|
|
result = "/dev/";
|
|
result.append(tempBuffer);
|
|
if( strlen(tempBuffer) > 3 ) //longer than tty
|
|
comPortList.push_back(result);
|
|
}
|
|
fin.close();
|
|
#endif
|
|
}
|
|
|
|
/** @brief Opens COM port
|
|
@param comName COM port name
|
|
@param baudrate COM port baudrate
|
|
@return 0 on success
|
|
*/
|
|
IConnection::DeviceStatus ConnectionCOM::Open(const char *comName, int baudrate)
|
|
{
|
|
Close();
|
|
if (strlen(comName) == 0)
|
|
return FAILURE;
|
|
|
|
DeviceStatus errorCode = SUCCESS;
|
|
|
|
#ifndef __unix__
|
|
// Initialize Overlap structures
|
|
m_osROverlap.Internal = 0;
|
|
m_osROverlap.InternalHigh = 0;
|
|
m_osROverlap.Offset = 0;
|
|
m_osROverlap.OffsetHigh = 0;
|
|
m_osROverlap.hEvent = CreateEvent(NULL, false, false, NULL);
|
|
|
|
m_osWOverlap.Internal = 0;
|
|
m_osWOverlap.InternalHigh = 0;
|
|
m_osWOverlap.Offset = 0;
|
|
m_osWOverlap.OffsetHigh = 0;
|
|
m_osWOverlap.hEvent = CreateEvent(NULL, false, false, NULL);
|
|
|
|
// Initialize DSB structure
|
|
memset(&m_dcbCommPort, 0, sizeof(m_dcbCommPort));
|
|
|
|
m_dcbCommPort.BaudRate = comBaudrate;
|
|
m_dcbCommPort.fBinary = 1;
|
|
m_dcbCommPort.fParity = 0;
|
|
m_dcbCommPort.fOutxCtsFlow = 0;
|
|
m_dcbCommPort.fOutxDsrFlow = 0;
|
|
m_dcbCommPort.fDtrControl = 0;
|
|
m_dcbCommPort.fDsrSensitivity = 0;
|
|
m_dcbCommPort.fTXContinueOnXoff = 0;
|
|
m_dcbCommPort.fOutX = 0;
|
|
m_dcbCommPort.fInX = 0;
|
|
m_dcbCommPort.fErrorChar = 0;
|
|
m_dcbCommPort.fNull = 0;
|
|
m_dcbCommPort.fRtsControl = 0;
|
|
m_dcbCommPort.fAbortOnError = 0;
|
|
m_dcbCommPort.fDummy2 = 0;
|
|
// m_dcbCommPort.wReserved = 0;
|
|
m_dcbCommPort.XonLim = 512;
|
|
m_dcbCommPort.XoffLim = 512;
|
|
m_dcbCommPort.ByteSize = 8;
|
|
m_dcbCommPort.Parity = 0;
|
|
m_dcbCommPort.StopBits = 0;
|
|
//m_dcbCommPort.StopBits = 1;
|
|
m_dcbCommPort.XonChar = 17;
|
|
m_dcbCommPort.XoffChar = 19;
|
|
m_dcbCommPort.ErrorChar = 0;
|
|
m_dcbCommPort.EofChar = 26;
|
|
m_dcbCommPort.EvtChar = 0;
|
|
m_dcbCommPort.wReserved1 = 0;
|
|
m_dcbCommPort.DCBlength = sizeof(DCB);
|
|
|
|
// Initialize Timeout's
|
|
m_ctmoNew.ReadIntervalTimeout = 50;
|
|
m_ctmoNew.ReadTotalTimeoutMultiplier = 0;
|
|
m_ctmoNew.ReadTotalTimeoutConstant = 100; // 1;
|
|
m_ctmoNew.WriteTotalTimeoutMultiplier = 0;
|
|
m_ctmoNew.WriteTotalTimeoutConstant = 100;
|
|
|
|
// Open COM port
|
|
string stmp;
|
|
stmp = "\\\\.\\";
|
|
stmp.append(comName);
|
|
hComm = CreateFileA(stmp.c_str(), GENERIC_READ | GENERIC_WRITE, 0, 0, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
|
|
|
|
if (hComm != INVALID_HANDLE_VALUE)
|
|
{
|
|
// Set Events
|
|
if (!SetCommMask(hComm, 0))
|
|
errorCode = FAILURE;
|
|
|
|
// Set Timeouts
|
|
GetCommTimeouts(hComm, &m_ctmoOld);
|
|
if (!SetCommTimeouts(hComm, &m_ctmoNew))
|
|
errorCode = FAILURE;
|
|
|
|
// Set DCB
|
|
if (!SetCommState(hComm, &m_dcbCommPort))
|
|
errorCode = FAILURE;
|
|
}
|
|
else
|
|
{
|
|
errorCode = FAILURE;
|
|
};
|
|
|
|
// Check the results
|
|
if (errorCode != 0)
|
|
{
|
|
//unsigned long err = GetLastError();
|
|
CloseHandle(hComm);
|
|
hComm = INVALID_HANDLE_VALUE;
|
|
return errorCode;
|
|
}
|
|
else
|
|
{
|
|
PurgeComm(hComm, PURGE_TXCLEAR | PURGE_RXCLEAR);
|
|
return SUCCESS;
|
|
}
|
|
#else
|
|
hComm = open(comName, O_RDWR | O_NOCTTY | O_SYNC);
|
|
if(hComm < 0)
|
|
{
|
|
// printf("%s",strerror(errno));
|
|
// MessageLog::getInstance()->write("Connection manager: failed opening COM port\n", LOG_ERROR);
|
|
return FAILURE;
|
|
}
|
|
|
|
struct termios tty;
|
|
memset(&tty, 0, sizeof(tty));
|
|
if( tcgetattr(hComm, &tty) != 0)
|
|
{
|
|
// MessageLog::getInstance()->write("Connection Manager: error from tcgetattr\n", LOG_ERROR);
|
|
return FAILURE;
|
|
}
|
|
int speed = B9600;
|
|
cfsetospeed(&tty, speed);
|
|
cfsetispeed(&tty, speed);
|
|
|
|
tty.c_cflag = (tty.c_cflag & ~CSIZE) | CS8;
|
|
tty.c_iflag &= ~IGNBRK;
|
|
tty.c_lflag = 0;
|
|
tty.c_oflag = 0;
|
|
tty.c_cc[VMIN] = 0; // read non blocking
|
|
tty.c_cc[VTIME] = 5; // 0.5 seconds read timeout
|
|
|
|
tty.c_iflag &= ~(IXON | IXOFF | IXANY);
|
|
tty.c_cflag |= (CLOCAL | CREAD);
|
|
|
|
if(tcsetattr(hComm, TCSANOW, &tty) != 0)
|
|
{
|
|
// MessageLog::getInstance()->write("Connection manager: error from tcsetattr\n", LOG_ERROR);
|
|
return FAILURE;
|
|
}
|
|
#endif
|
|
return SUCCESS;
|
|
}
|
|
|
|
/** @brief Checks if chip is connected to currently open port
|
|
@return chip is connected
|
|
*/
|
|
bool ConnectionCOM::TestConnectivity()
|
|
{
|
|
//currently set to always return true to show all com ports
|
|
return true;
|
|
#ifndef __unix__
|
|
if (hComm != INVALID_HANDLE_VALUE)
|
|
#else
|
|
if( hComm >= 0)
|
|
#endif
|
|
{
|
|
// unsigned char out[64];
|
|
// unsigned char in[64];
|
|
// memset(in, 0, 64);
|
|
// out[0] = CMD_GET_INFO;
|
|
// SendData(out, 64);
|
|
// ReadData(in, 64);
|
|
// if(in[0] == CMD_GET_INFO && in[1] == 0x01)
|
|
// return true;
|
|
// else
|
|
// return false;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
/** @brief Finds all chips connected to com ports
|
|
@return number of devices found
|
|
*/
|
|
int ConnectionCOM::RefreshDeviceList()
|
|
{
|
|
int wasOpen = -1;
|
|
string wasOpenName = "";
|
|
if(IsOpen())
|
|
{
|
|
wasOpen = GetOpenedIndex();
|
|
wasOpenName = comPortName;
|
|
}
|
|
Close();
|
|
comPortList.clear();
|
|
if(comPortList.size() == 0)
|
|
FindAllComPorts();
|
|
|
|
m_deviceNames.clear();
|
|
string comName;
|
|
for(unsigned int i=0; i<comPortList.size(); i++)
|
|
{
|
|
comName = comPortList[i];
|
|
#ifndef __unix__
|
|
if( Open(comName.c_str(), comBaudrate) == SUCCESS)
|
|
{
|
|
if( TestConnectivity() ) //if responds add it to device list
|
|
m_deviceNames.push_back(comName);
|
|
}
|
|
Close();
|
|
#else
|
|
m_deviceNames.push_back(comName);
|
|
#endif
|
|
}
|
|
if(wasOpen != -1)
|
|
{
|
|
for(unsigned i=0; i<m_deviceNames.size(); ++i)
|
|
if(m_deviceNames[i] == wasOpenName)
|
|
{
|
|
Open(i);
|
|
break;
|
|
}
|
|
}
|
|
return m_deviceNames.size();
|
|
}
|
|
|
|
/** @brief Returns found devices names
|
|
@return vector of device names
|
|
*/
|
|
vector<string> ConnectionCOM::GetDeviceNames()
|
|
{
|
|
return m_deviceNames;
|
|
}
|
|
|
|
/** @brief Purges communication buffers
|
|
*/
|
|
void ConnectionCOM::ClearComm()
|
|
{
|
|
#ifndef __unix__
|
|
PurgeComm(hComm, PURGE_TXCLEAR|PURGE_RXCLEAR);
|
|
#endif
|
|
}
|