* Copyright (C) 2021 Huawei Device Co., Ltd.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "securec.h"
#include "uart.h"
#include "fcntl.h"
#include <dirent.h>
#include <cstring>
#include "log.h"
using namespace std;
using namespace Hdc;
bool g_ioCancel = false;
bool EnumSerialPort(bool &portChange)
{
std::vector<string> newPortInfo;
std::vector<string> serialPortInfo;
std::vector<string> serialPortRemoved;
serialPortRemoved.clear();
bool bRet = true;
#ifdef HOST_MINGW
constexpr int MAX_KEY_LENGTH = 255;
constexpr int MAX_VALUE_NAME = 16383;
HKEY hKey;
TCHAR achValue[MAX_VALUE_NAME];
DWORD cchValue = MAX_VALUE_NAME;
TCHAR achClass[MAX_PATH] = _T("");
DWORD cchClassName = MAX_PATH;
DWORD cSubKeys = 0;
DWORD cbMaxSubKey;
DWORD cchMaxClass;
DWORD cKeyNum;
DWORD cchMaxValue;
DWORD cbMaxValueData;
DWORD cbSecurityDescriptor;
FILETIME ftLastWriteTime;
LSTATUS iRet = -1;
std::string port;
TCHAR strDSName[MAX_VALUE_NAME];
errno_t nRet = 0;
nRet = memset_s(strDSName, sizeof(TCHAR) * MAX_VALUE_NAME, 0, sizeof(TCHAR) * MAX_VALUE_NAME);
if (nRet != EOK) {
return false;
}
DWORD nBuffLen = 10;
if (ERROR_SUCCESS == RegOpenKeyEx(HKEY_LOCAL_MACHINE, _T("HARDWARE\\DEVICEMAP\\SERIALCOMM"), 0,
KEY_READ, &hKey)) {
iRet = RegQueryInfoKey(hKey, achClass, &cchClassName, NULL, &cSubKeys, &cbMaxSubKey,
&cchMaxClass, &cKeyNum, &cchMaxValue, &cbMaxValueData,
&cbSecurityDescriptor, &ftLastWriteTime);
if (ERROR_SUCCESS == iRet) {
for (DWORD i = 0; i < cKeyNum; i++) {
cchValue = MAX_VALUE_NAME;
achValue[0] = '\0';
nBuffLen = MAX_KEY_LENGTH;
if (ERROR_SUCCESS == RegEnumValue(hKey, i, achValue, &cchValue, NULL, NULL,
(LPBYTE)strDSName, &nBuffLen)) {
#ifdef UNICODE
strPortName = WstringToString(strDSName);
#else
port = std::string(strDSName);
#endif
newPortInfo.push_back(port);
auto it = std::find(serialPortInfo.begin(), serialPortInfo.end(), port);
if (it == serialPortInfo.end()) {
portChange = true;
}
} else {
bRet = false;
}
}
} else {
bRet = false;
}
} else {
bRet = false;
}
RegCloseKey(hKey);
#else
DIR *dir = opendir("/dev");
dirent *p = nullptr;
if (dir != nullptr) {
while ((p = readdir(dir)) != nullptr) {
#ifdef HOST_LINUX
if (p->d_name[0] != '.' && string(p->d_name).find("tty") != std::string::npos) {
#else
if (p->d_name[0] != '.' && string(p->d_name).find("serial") != std::string::npos) {
#endif
string port = "/dev/" + string(p->d_name);
if (port.find("/dev/ttyUSB") == 0 || port.find("/dev/ttySerial") == 0 || port.find("/dev/cu.") == 0) {
newPortInfo.push_back(port);
auto it = std::find(serialPortInfo.begin(), serialPortInfo.end(), port);
if (it == serialPortInfo.end()) {
portChange = true;
WRITE_LOG(LOG_INFO, "new port:%s", port.c_str());
}
}
}
}
closedir(dir);
}
#endif
for (auto &oldPort : serialPortInfo) {
auto it = std::find(newPortInfo.begin(), newPortInfo.end(), oldPort);
if (it == newPortInfo.end()) {
serialPortRemoved.emplace_back(oldPort);
}
}
if (!portChange) {
if (serialPortInfo.size() != newPortInfo.size()) {
portChange = true;
}
}
if (portChange) {
serialPortInfo.swap(newPortInfo);
}
return bRet;
}
std::string CanonicalizeSpecPath(std::string &src) {
char resolvedPath[PATH_MAX] = { 0 };
#ifdef HOST_MINGW
if (!_fullpath(resolvedPath, src.c_str(), PATH_MAX)) {
return "";
}
#else
if (realpath(src.c_str(), resolvedPath) == nullptr) {
return "";
}
#endif
std::string res(resolvedPath);
return res;
}
#ifdef HOST_MINGW
static constexpr int PORT_NAME_LEN = 10;
static constexpr int NUM = 2;
HANDLE WinOpenSerialPort(std::string portName) {
WRITE_LOG(LOG_INFO, "WinOpenSerialPort start, portName %s", portName.c_str());
TCHAR buf[PORT_NAME_LEN * NUM];
#ifdef UNICODE
_stprintf_s(buf, MAX_PATH, _T("\\\\.\\%S"), portName.c_str());
#else
_stprintf_s(buf, MAX_PATH, _T("\\\\.\\%s"), portName.c_str());
#endif
DWORD dwFlagsAndAttributes = FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED;
HANDLE devUartHandle = CreateFile(buf, GENERIC_READ | GENERIC_WRITE, 0, NULL,
OPEN_EXISTING, dwFlagsAndAttributes, NULL);
if (devUartHandle == INVALID_HANDLE_VALUE)
{
WRITE_LOG(LOG_INFO, "CreateFile, open handle ok");
} else {
WRITE_LOG(LOG_WARN, "CreateFile open failed");
}
return devUartHandle;
}
bool WinSetSerialPort(HANDLE devUartHandle, string serialport, int byteSize, int baudRate) {
bool winRet = true;
COMMTIMEOUTS timeouts;
GetCommTimeouts(devUartHandle, &timeouts);
int interTimeout = 5;
timeouts.ReadIntervalTimeout = interTimeout;
timeouts.ReadTotalTimeoutMultiplier = 0;
timeouts.ReadTotalTimeoutConstant = 0;
timeouts.WriteTotalTimeoutMultiplier = 0;
timeouts.WriteTotalTimeoutConstant = 0;
SetCommTimeouts(devUartHandle, &timeouts);
constexpr int max = DEFAULT_BAUD_RATE_VALUE / 8 * 2;
do {
if (!SetupComm(devUartHandle, max, max)) {
WRITE_LOG(LOG_WARN, "SetupComm %s fail, err:%lu.", serialport.c_str(), GetLastError());
winRet = false;
break;
}
DCB dcb;
if (!GetCommState(devUartHandle, &dcb)) {
WRITE_LOG(LOG_WARN, "GetCommState %s fail, err:%lu.", serialport.c_str(), GetLastError());
winRet = false;
}
dcb.DCBlength = sizeof(DCB);
dcb.BaudRate = baudRate;
dcb.Parity = 0;
dcb.ByteSize = byteSize;
dcb.StopBits = ONESTOPBIT;
if (!SetCommState(devUartHandle, &dcb)) {
WRITE_LOG(LOG_WARN, "SetCommState %s fail, err:%lu.", serialport.c_str(), GetLastError());
winRet = false;
break;
}
if (!PurgeComm(devUartHandle,
PURGE_RXCLEAR | PURGE_TXCLEAR | PURGE_RXABORT | PURGE_TXABORT)) {
WRITE_LOG(LOG_WARN, "PurgeComm %s fail, err:%lu.", serialport.c_str(), GetLastError());
winRet = false;
break;
}
DWORD dwError;
COMSTAT cs;
if (!ClearCommError(devUartHandle, &dwError, &cs)) {
WRITE_LOG(LOG_WARN, "ClearCommError %s fail, err:%lu.", serialport.c_str(), GetLastError());
winRet = false;
break;
}
} while (false);
WRITE_LOG(LOG_INFO, "WinSetSerialPort ret %d\n", winRet);
if (!winRet) {
WinCloseSerialPort(devUartHandle);
}
return winRet;
}
bool WinCloseSerialPort(HANDLE &handle) {
WRITE_LOG(LOG_DEBUG, "CloseSerialPort");
if (handle != INVALID_HANDLE_VALUE) {
CloseHandle(handle);
handle = INVALID_HANDLE_VALUE;
}
return true;
}
ssize_t WinReadUartDev(HANDLE handle, std::vector<uint8_t> &readBuf, size_t expectedSize, OVERLAPPED &overRead) {
ssize_t totalBytesRead = 0;
uint8_t uartReadBuffer[MAX_UART_SIZE_IOBUF];
DWORD bytesRead = 0;
do {
bytesRead = 0;
BOOL bReadStatus = ReadFile(handle, uartReadBuffer, sizeof(uartReadBuffer), &bytesRead, &overRead);
if (!bReadStatus) {
if (GetLastError() == ERROR_IO_PENDING) {
bytesRead = 0;
DWORD dwMilliseconds = READ_GIVE_UP_TIME_OUT_TIME_MS;
if (expectedSize == 0) {
dwMilliseconds = INFINITE;
}
if (!GetOverlappedResultEx(handle, &overRead, &bytesRead,
dwMilliseconds, FALSE)) {
DWORD error = GetLastError();
if (error == ERROR_OPERATION_ABORTED) {
totalBytesRead += bytesRead;
WRITE_LOG(LOG_WARN, "%s error cancel read. %lu %zd",
__FUNCTION__, bytesRead, totalBytesRead);
return totalBytesRead;
} else if (error == WAIT_TIMEOUT) {
totalBytesRead += bytesRead;
WRITE_LOG(LOG_WARN, "%s error timeout. %lu %zd",
__FUNCTION__, bytesRead, totalBytesRead);
return totalBytesRead;
} else {
WRITE_LOG(LOG_WARN, "%s error wait io:%lu.", __FUNCTION__, GetLastError());
}
return -1;
}
} else {
WRITE_LOG(LOG_WARN, "%s err:%lu. ", __FUNCTION__, GetLastError());
return -1;
}
}
if (bytesRead > 0) {
readBuf.insert(readBuf.end(), uartReadBuffer, uartReadBuffer + bytesRead);
totalBytesRead += bytesRead;
}
} while (readBuf.size() < expectedSize || bytesRead == 0);
return totalBytesRead;
}
ssize_t WinWriteUartDev(HANDLE handle, uint8_t *data, const size_t length, OVERLAPPED &ovWrite) {
ssize_t totalBytesWrite = 0;
do {
DWORD bytesWrite = 0;
BOOL bWriteStat = WriteFile(handle, data + totalBytesWrite, length - totalBytesWrite, &bytesWrite, &ovWrite);
if (!bWriteStat) {
if (GetLastError() == ERROR_IO_PENDING) {
if (!GetOverlappedResult(handle, &ovWrite, &bytesWrite, TRUE)) {
WRITE_LOG(LOG_WARN, "%s error wait io:%lu. bytesWrite %lu", __FUNCTION__,
GetLastError(), bytesWrite);
return -1;
}
} else {
WRITE_LOG(LOG_WARN, "%s err:%lu. bytesWrite %lu", __FUNCTION__, GetLastError(),
bytesWrite);
return -1;
}
}
totalBytesWrite += bytesWrite;
} while (totalBytesWrite < signed(length));
return totalBytesWrite;
}
#else
int GetUartSpeed(int speed) {
switch (speed) {
case UART_SPEED2400:
return (B2400);
case UART_SPEED4800:
return (B4800);
case UART_SPEED9600:
return (B9600);
case UART_SPEED115200:
return (B115200);
case UART_SPEED921600:
return (B921600);
case UART_SPEED1500000:
return (B1500000);
default:
return (B921600);
}
}
int GetUartBits(int bits) {
switch (bits) {
case UART_BIT1:
return (CS7);
case UART_BIT2:
return (CS8);
default:
return (CS8);
}
}
int OpenSerialPort(std::string portName) {
int uartHandle = -1;
if ((uartHandle = open(portName.c_str(), O_RDWR | O_NOCTTY | O_NDELAY)) < 0) {
WRITE_LOG(LOG_WARN, "%s: cannot open uartHandle: errno=%d\n", portName.c_str(), errno);
return -1;
}
usleep(UART_IO_WAIT_TIME_100);
fcntl(uartHandle, F_SETFD, FD_CLOEXEC);
int flag = fcntl(uartHandle, F_GETFL);
if (flag < 0) {
WRITE_LOG(LOG_WARN, "fcntl failed: error=%d\n", errno);
return -1;
}
uint32_t ret = static_cast<uint32_t>(flag);
ret &= ~O_NONBLOCK;
flag &= static_cast<int>(ret);
fcntl(uartHandle, F_SETFL, flag);
return uartHandle;
}
#ifdef HOST_MAC
int SetSerial(int fd, int nSpeed, int nBits, char nEvent, int nStop) {
struct termios options, oldttys1;
if (tcgetattr(fd, &oldttys1) != 0) {
constexpr int buf_size = 1024;
char buf[buf_size] = { 0 };
strerror_r(errno, buf, buf_size);
return ERR_GENERIC;
}
errno_t nRet = 0;
nRet = memcpy_s(&options, sizeof(options), &oldttys1, sizeof(options));
if (nRet != EOK) {
return ERR_GENERIC;
}
cfmakeraw(&options);
options.c_cc[VMIN] = 0;
options.c_cc[VTIME] = 10;
cfsetspeed(&options, B19200);
options.c_cflag |= GetUartBits(nBits);
options.c_cflag &= ~PARENB;
speed_t speed = nSpeed;
if (ioctl(fd, IOSSIOSPEED, &speed) == -1) {
}
if ((tcsetattr(fd, TCSANOW, &options)) != 0) {
return ERR_GENERIC;
}
if (ioctl(fd, IOSSIOSPEED, &speed) == -1) {
}
return RET_SUCCESS;
}
#else
int SetSerial(int fd, int nSpeed, int nBits, char nEvent, int nStop) {
struct termios newttys1, oldttys1;
if (tcgetattr(fd, &oldttys1) != 0) {
constexpr int buf_size = 1024;
char buf[buf_size] = { 0 };
strerror_r(errno, buf, buf_size);
return ERR_GENERIC;
}
bzero(&newttys1, sizeof(newttys1));
newttys1.c_cflag = GetUartSpeed(nSpeed);
newttys1.c_cflag |= (CLOCAL | CREAD);
newttys1.c_cflag &= ~CSIZE;
newttys1.c_lflag &= ~ICANON;
newttys1.c_cflag |= GetUartBits(nBits);
switch (nEvent) {
case 'O':
newttys1.c_cflag |= PARENB;
newttys1.c_iflag |= (INPCK | ISTRIP);
newttys1.c_cflag |= PARODD;
break;
case 'E':
newttys1.c_cflag |= PARENB;
newttys1.c_iflag |= (INPCK | ISTRIP);
newttys1.c_cflag &= ~PARODD;
break;
case 'N':
newttys1.c_cflag &= ~PARENB;
break;
default:
break;
}
if (nStop == UART_STOP1) {
newttys1.c_cflag &= ~CSTOPB;
} else if (nStop == UART_STOP2) {
newttys1.c_cflag |= CSTOPB;
}
newttys1.c_cc[VTIME] = 0;
newttys1.c_cc[VMIN] = 0;
if (tcflush(fd, TCIOFLUSH)) {
return ERR_GENERIC;
}
if ((tcsetattr(fd, TCSANOW, &newttys1)) != 0) {
return ERR_GENERIC;
}
return ERR_SUCCESS;
}
#endif
ssize_t ReadUartDev(int handle, std::vector<uint8_t> &readBuf, size_t expectedSize) {
ssize_t totalBytesRead = 0;
uint8_t uartReadBuffer[MAX_UART_SIZE_IOBUF];
ssize_t bytesRead = 0;
do {
bytesRead = 0;
int ret = 0;
fd_set readFds;
FD_ZERO(&readFds);
FD_SET(handle, &readFds);
const constexpr int msTous = 1000;
const constexpr int sTous = 1000 * msTous;
struct timeval tv;
tv.tv_sec = 0;
if (expectedSize == 0) {
tv.tv_usec = WAIT_RESPONSE_TIME_OUT_MS * msTous;
tv.tv_sec = tv.tv_usec / sTous;
tv.tv_usec = tv.tv_usec % sTous;
#ifdef HDC_HOST
ret = select(handle + 1, &readFds, nullptr, nullptr, &tv);
#else
ret = select(handle + 1, &readFds, nullptr, nullptr, nullptr);
#endif
} else {
tv.tv_usec = READ_GIVE_UP_TIME_OUT_TIME_MS * msTous;
tv.tv_sec = tv.tv_usec / sTous;
tv.tv_usec = tv.tv_usec % sTous;
ret = select(handle + 1, &readFds, nullptr, nullptr, &tv);
}
if (ret == 0 and expectedSize == 0) {
if (g_ioCancel) {
g_ioCancel = true;
return totalBytesRead;
} else {
continue;
}
} else if (ret == 0) {
return totalBytesRead;
} else if (ret < 0) {
return -1;
} else {
size_t maxReadSize = expectedSize - static_cast<size_t>(totalBytesRead);
if (maxReadSize > MAX_UART_SIZE_IOBUF) {
maxReadSize = MAX_UART_SIZE_IOBUF;
}
bytesRead = read(handle, uartReadBuffer, maxReadSize);
if (bytesRead <= 0) {
return -1;
}
}
if (bytesRead > 0) {
readBuf.insert(readBuf.end(), uartReadBuffer, uartReadBuffer + bytesRead);
totalBytesRead += bytesRead;
}
} while (readBuf.size() < expectedSize or bytesRead == 0);
return totalBytesRead;
}
ssize_t WriteUartDev(int handle, uint8_t *data, const size_t length) {
ssize_t totalBytesWrite = 0;
do {
ssize_t bytesWrite = 0;
bytesWrite = write(handle, data + totalBytesWrite, length - totalBytesWrite);
if (bytesWrite < 0) {
if (errno == EINTR or errno == EAGAIN) {
continue;
} else {
constexpr int buf_size = 1024;
char buf[buf_size] = { 0 };
strerror_r(errno, buf, buf_size);
return -1;
}
} else {
tcdrain(handle);
}
totalBytesWrite += bytesWrite;
} while (totalBytesWrite < signed(length));
return totalBytesWrite;
}
bool CloseSerialPort(int &handle) {
if (handle != -1)
{
return CloseFd(handle) >= 0;
} else {
return true;
}
}
int CloseFd(int &fd) {
int rc = 0;
#ifndef HDC_HOST
#endif
if (fd > 0) {
rc = close(fd);
if (rc < 0) {
char buffer[Hdc::BUF_SIZE_DEFAULT] = { 0 };
#ifdef _WIN32
strerror_s(buffer, Hdc::BUF_SIZE_DEFAULT, errno);
#else
strerror_r(errno, buffer, Hdc::BUF_SIZE_DEFAULT);
#endif
} else {
fd = -1;
}
}
return rc;
}
#endif