xref: /developtools/hdc/hdc_rust/src/cffi/uart.cpp (revision cc290419)
1/*
2 * Copyright (C) 2021 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 *     http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15#include "securec.h"
16#include "uart.h"
17#include "fcntl.h"
18#include <dirent.h>
19#include <cstring>
20#include "log.h"
21
22using namespace std;
23using namespace Hdc;
24
25bool g_ioCancel = false;
26
27// review why not use QueryDosDevice ?
28bool EnumSerialPort(bool &portChange)
29{
30    std::vector<string> newPortInfo;
31    std::vector<string> serialPortInfo;
32    std::vector<string> serialPortRemoved;
33    serialPortRemoved.clear();
34    bool bRet = true;
35
36#ifdef HOST_MINGW
37    constexpr int MAX_KEY_LENGTH = 255;
38    constexpr int MAX_VALUE_NAME = 16383;
39    HKEY hKey;
40    TCHAR achValue[MAX_VALUE_NAME];    // buffer for subkey name
41    DWORD cchValue = MAX_VALUE_NAME;   // size of name string
42    TCHAR achClass[MAX_PATH] = _T(""); // buffer for class name
43    DWORD cchClassName = MAX_PATH;     // size of class string
44    DWORD cSubKeys = 0;                // number of subkeys
45    DWORD cbMaxSubKey;                 // longest subkey size
46    DWORD cchMaxClass;                 // longest class string
47    DWORD cKeyNum;                     // number of values for key
48    DWORD cchMaxValue;                 // longest value name
49    DWORD cbMaxValueData;              // longest value data
50    DWORD cbSecurityDescriptor;        // size of security descriptor
51    FILETIME ftLastWriteTime;          // last write time
52    LSTATUS iRet = -1;
53    std::string port;
54    TCHAR strDSName[MAX_VALUE_NAME];
55    errno_t nRet = 0;
56    nRet = memset_s(strDSName, sizeof(TCHAR) * MAX_VALUE_NAME, 0, sizeof(TCHAR) * MAX_VALUE_NAME);
57    if (nRet != EOK) {
58        return false;
59    }
60    DWORD nBuffLen = 10;
61    if (ERROR_SUCCESS == RegOpenKeyEx(HKEY_LOCAL_MACHINE, _T("HARDWARE\\DEVICEMAP\\SERIALCOMM"), 0,
62                                      KEY_READ, &hKey)) {
63        // Get the class name and the value count.
64        iRet = RegQueryInfoKey(hKey, achClass, &cchClassName, NULL, &cSubKeys, &cbMaxSubKey,
65                               &cchMaxClass, &cKeyNum, &cchMaxValue, &cbMaxValueData,
66                               &cbSecurityDescriptor, &ftLastWriteTime);
67        // Enumerate the key values.
68        if (ERROR_SUCCESS == iRet) {
69            for (DWORD i = 0; i < cKeyNum; i++) {
70                cchValue = MAX_VALUE_NAME;
71                achValue[0] = '\0';
72                nBuffLen = MAX_KEY_LENGTH;
73                if (ERROR_SUCCESS == RegEnumValue(hKey, i, achValue, &cchValue, NULL, NULL,
74                                                  (LPBYTE)strDSName, &nBuffLen)) {
75#ifdef UNICODE
76                    strPortName = WstringToString(strDSName);
77#else
78                    port = std::string(strDSName);
79#endif
80                    newPortInfo.push_back(port);
81                    auto it = std::find(serialPortInfo.begin(), serialPortInfo.end(), port);
82                    if (it == serialPortInfo.end()) {
83                        portChange = true;
84                    }
85                } else {
86                    bRet = false;
87                }
88            }
89        } else {
90            bRet = false;
91        }
92    } else {
93        bRet = false;
94    }
95    RegCloseKey(hKey);
96#else
97    DIR *dir = opendir("/dev");
98    dirent *p = nullptr;
99    if (dir != nullptr) {
100        while ((p = readdir(dir)) != nullptr) {
101#ifdef HOST_LINUX
102            if (p->d_name[0] != '.' && string(p->d_name).find("tty") != std::string::npos) {
103#else
104            if (p->d_name[0] != '.' && string(p->d_name).find("serial") != std::string::npos) {
105#endif
106                string port = "/dev/" + string(p->d_name);
107                if (port.find("/dev/ttyUSB") == 0 || port.find("/dev/ttySerial") == 0 || port.find("/dev/cu.") == 0) {
108                    newPortInfo.push_back(port);
109                    auto it = std::find(serialPortInfo.begin(), serialPortInfo.end(), port);
110                    if (it == serialPortInfo.end()) {
111                        portChange = true;
112                        WRITE_LOG(LOG_INFO, "new port:%s", port.c_str());
113                    }
114                }
115            }
116        }
117        closedir(dir);
118    }
119#endif
120    for (auto &oldPort : serialPortInfo) {
121        auto it = std::find(newPortInfo.begin(), newPortInfo.end(), oldPort);
122        if (it == newPortInfo.end()) {
123            // not found in new port list
124            // we need remove the connect info
125            serialPortRemoved.emplace_back(oldPort);
126        }
127    }
128
129    if (!portChange) {
130        // new scan empty , same as port changed
131        if (serialPortInfo.size() != newPortInfo.size()) {
132            portChange = true;
133        }
134    }
135    if (portChange) {
136        serialPortInfo.swap(newPortInfo);
137    }
138    return bRet;
139}
140
141std::string CanonicalizeSpecPath(std::string &src) {
142    char resolvedPath[PATH_MAX] = { 0 };
143#ifdef HOST_MINGW
144    if (!_fullpath(resolvedPath, src.c_str(), PATH_MAX)) {
145        return "";
146    }
147#else
148    if (realpath(src.c_str(), resolvedPath) == nullptr) {
149        return "";
150    }
151#endif
152    std::string res(resolvedPath);
153    return res;
154}
155
156#ifdef HOST_MINGW
157
158static constexpr int PORT_NAME_LEN = 10;
159static constexpr int NUM = 2;
160
161HANDLE WinOpenSerialPort(std::string portName) {
162    WRITE_LOG(LOG_INFO, "WinOpenSerialPort start, portName %s", portName.c_str());
163    TCHAR buf[PORT_NAME_LEN * NUM];
164    #ifdef UNICODE
165        _stprintf_s(buf, MAX_PATH, _T("\\\\.\\%S"), portName.c_str());
166    #else
167        _stprintf_s(buf, MAX_PATH, _T("\\\\.\\%s"), portName.c_str());
168    #endif // UNICODE
169    DWORD dwFlagsAndAttributes = FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED;
170    HANDLE devUartHandle = CreateFile(buf, GENERIC_READ | GENERIC_WRITE, 0, NULL,
171                                        OPEN_EXISTING, dwFlagsAndAttributes, NULL);
172    if (devUartHandle == INVALID_HANDLE_VALUE)
173    {
174        WRITE_LOG(LOG_INFO, "CreateFile, open handle ok");
175    } else {
176        WRITE_LOG(LOG_WARN, "CreateFile open failed");
177    }
178
179    return devUartHandle;
180}
181
182bool WinSetSerialPort(HANDLE devUartHandle, string serialport, int byteSize, int baudRate) {
183    bool winRet = true;
184    COMMTIMEOUTS timeouts;
185    GetCommTimeouts(devUartHandle, &timeouts);
186    int interTimeout = 5;
187    timeouts.ReadIntervalTimeout = interTimeout;
188    timeouts.ReadTotalTimeoutMultiplier = 0;
189    timeouts.ReadTotalTimeoutConstant = 0;
190    timeouts.WriteTotalTimeoutMultiplier = 0;
191    timeouts.WriteTotalTimeoutConstant = 0;
192    SetCommTimeouts(devUartHandle, &timeouts);
193    constexpr int max = DEFAULT_BAUD_RATE_VALUE / 8 * 2; // 2 second buffer size
194    do {
195        if (!SetupComm(devUartHandle, max, max)) {
196            WRITE_LOG(LOG_WARN, "SetupComm %s fail, err:%lu.", serialport.c_str(), GetLastError());
197            winRet = false;
198            break;
199        }
200        DCB dcb;
201        if (!GetCommState(devUartHandle, &dcb)) {
202            WRITE_LOG(LOG_WARN, "GetCommState %s fail, err:%lu.", serialport.c_str(), GetLastError());
203            winRet = false;
204        }
205        dcb.DCBlength = sizeof(DCB);
206        dcb.BaudRate = baudRate;
207        dcb.Parity = 0;
208        dcb.ByteSize = byteSize;
209        dcb.StopBits = ONESTOPBIT;
210        if (!SetCommState(devUartHandle, &dcb)) {
211            WRITE_LOG(LOG_WARN, "SetCommState %s fail, err:%lu.", serialport.c_str(), GetLastError());
212            winRet = false;
213            break;
214        }
215        if (!PurgeComm(devUartHandle,
216                       PURGE_RXCLEAR | PURGE_TXCLEAR | PURGE_RXABORT | PURGE_TXABORT)) {
217            WRITE_LOG(LOG_WARN, "PurgeComm  %s fail, err:%lu.", serialport.c_str(), GetLastError());
218            winRet = false;
219            break;
220        }
221        DWORD dwError;
222        COMSTAT cs;
223        if (!ClearCommError(devUartHandle, &dwError, &cs)) {
224            WRITE_LOG(LOG_WARN, "ClearCommError %s fail, err:%lu.", serialport.c_str(), GetLastError());
225            winRet = false;
226            break;
227        }
228    } while (false);
229    WRITE_LOG(LOG_INFO, "WinSetSerialPort ret %d\n", winRet);
230    if (!winRet) {
231        WinCloseSerialPort(devUartHandle);
232    }
233    return winRet;
234}
235
236bool WinCloseSerialPort(HANDLE &handle) {
237    WRITE_LOG(LOG_DEBUG, "CloseSerialPort");
238    if (handle != INVALID_HANDLE_VALUE) {
239        CloseHandle(handle);
240        handle = INVALID_HANDLE_VALUE;
241    }
242    return true;
243}
244
245ssize_t WinReadUartDev(HANDLE handle, std::vector<uint8_t> &readBuf, size_t expectedSize, OVERLAPPED &overRead) {
246    ssize_t totalBytesRead = 0;
247    uint8_t uartReadBuffer[MAX_UART_SIZE_IOBUF];
248    DWORD bytesRead = 0;
249
250    do {
251        bytesRead = 0;
252        BOOL bReadStatus = ReadFile(handle, uartReadBuffer, sizeof(uartReadBuffer), &bytesRead, &overRead);
253        if (!bReadStatus) {
254            if (GetLastError() == ERROR_IO_PENDING) {
255                bytesRead = 0;
256                DWORD dwMilliseconds = READ_GIVE_UP_TIME_OUT_TIME_MS;
257                if (expectedSize == 0) {
258                    dwMilliseconds = INFINITE;
259                }
260                if (!GetOverlappedResultEx(handle, &overRead, &bytesRead,
261                                           dwMilliseconds, FALSE)) {
262                    // wait io failed
263                    DWORD error = GetLastError();
264                    if (error == ERROR_OPERATION_ABORTED) {
265                        totalBytesRead += bytesRead;
266                        WRITE_LOG(LOG_WARN, "%s error cancel read. %lu %zd",
267                                  __FUNCTION__, bytesRead, totalBytesRead);
268                        return totalBytesRead;
269                    } else if (error == WAIT_TIMEOUT) {
270                        totalBytesRead += bytesRead;
271                        WRITE_LOG(LOG_WARN, "%s error timeout. %lu %zd",
272                                  __FUNCTION__, bytesRead, totalBytesRead);
273                        return totalBytesRead;
274                    } else {
275                        WRITE_LOG(LOG_WARN, "%s error wait io:%lu.", __FUNCTION__, GetLastError());
276                    }
277                    return -1;
278                }
279            } else {
280                // not ERROR_IO_PENDING
281                WRITE_LOG(LOG_WARN, "%s  err:%lu. ", __FUNCTION__, GetLastError());
282                return -1;
283            }
284        }
285        if (bytesRead > 0) {
286            readBuf.insert(readBuf.end(), uartReadBuffer, uartReadBuffer + bytesRead);
287            totalBytesRead += bytesRead;
288        }
289    } while (readBuf.size() < expectedSize || bytesRead == 0);
290    return totalBytesRead;
291}
292
293ssize_t WinWriteUartDev(HANDLE handle, uint8_t *data, const size_t length, OVERLAPPED &ovWrite) {
294    ssize_t totalBytesWrite = 0;
295    do {
296        DWORD bytesWrite = 0;
297        BOOL bWriteStat = WriteFile(handle, data + totalBytesWrite, length - totalBytesWrite, &bytesWrite, &ovWrite);
298        if (!bWriteStat) {
299            if (GetLastError() == ERROR_IO_PENDING) {
300                if (!GetOverlappedResult(handle, &ovWrite, &bytesWrite, TRUE)) {
301                    WRITE_LOG(LOG_WARN, "%s error wait io:%lu. bytesWrite %lu", __FUNCTION__,
302                           GetLastError(), bytesWrite);
303                    return -1;
304                }
305            } else {
306                WRITE_LOG(LOG_WARN, "%s err:%lu. bytesWrite %lu", __FUNCTION__, GetLastError(),
307                       bytesWrite);
308                return -1;
309            }
310        }
311        totalBytesWrite += bytesWrite;
312    } while (totalBytesWrite < signed(length));
313    return totalBytesWrite;
314}
315
316#else
317
318int GetUartSpeed(int speed) {
319    switch (speed) {
320        case UART_SPEED2400:
321            return (B2400);
322        case UART_SPEED4800:
323            return (B4800);
324        case UART_SPEED9600:
325            return (B9600);
326        case UART_SPEED115200:
327            return (B115200);
328        case UART_SPEED921600:
329            return (B921600);
330        case UART_SPEED1500000:
331            return (B1500000);
332        default:
333            return (B921600);
334    }
335}
336
337int GetUartBits(int bits) {
338    switch (bits) {
339        case UART_BIT1:
340            return (CS7);
341        case UART_BIT2:
342            return (CS8);
343        default:
344            return (CS8);
345    }
346}
347
348int OpenSerialPort(std::string portName) {
349    int uartHandle = -1;
350    if ((uartHandle = open(portName.c_str(), O_RDWR | O_NOCTTY | O_NDELAY)) < 0) {
351        WRITE_LOG(LOG_WARN, "%s: cannot open uartHandle: errno=%d\n", portName.c_str(), errno);
352        return -1;
353    }
354    usleep(UART_IO_WAIT_TIME_100);
355    // cannot open with O_CLOEXEC, must fcntl
356    fcntl(uartHandle, F_SETFD, FD_CLOEXEC);
357    int flag = fcntl(uartHandle, F_GETFL);
358    if (flag < 0) {
359        WRITE_LOG(LOG_WARN, "fcntl failed: error=%d\n", errno);
360        return -1;
361    }
362    uint32_t ret = static_cast<uint32_t>(flag);
363    ret &= ~O_NONBLOCK;
364    flag &= static_cast<int>(ret);
365    fcntl(uartHandle, F_SETFL, flag);
366
367    return uartHandle;
368}
369
370#ifdef HOST_MAC
371int SetSerial(int fd, int nSpeed, int nBits, char nEvent, int nStop) {
372    struct termios options, oldttys1;
373    if (tcgetattr(fd, &oldttys1) != 0) {
374        constexpr int buf_size = 1024;
375        char buf[buf_size] = { 0 };
376        strerror_r(errno, buf, buf_size);
377        return ERR_GENERIC;
378    }
379
380    errno_t nRet = 0;
381    nRet = memcpy_s(&options, sizeof(options), &oldttys1, sizeof(options));
382    if (nRet != EOK) {
383        return ERR_GENERIC;
384    }
385    cfmakeraw(&options);
386    options.c_cc[VMIN] = 0;
387    options.c_cc[VTIME] = 10; // 10 * 1/10 sec : 1 sec
388
389    cfsetspeed(&options, B19200);
390    options.c_cflag |= GetUartBits(nBits); // Use 8 bit words
391    options.c_cflag &= ~PARENB;
392
393    speed_t speed = nSpeed;
394    if (ioctl(fd, IOSSIOSPEED, &speed) == -1) {
395    }
396    if ((tcsetattr(fd, TCSANOW, &options)) != 0) {
397        return ERR_GENERIC;
398    }
399    if (ioctl(fd, IOSSIOSPEED, &speed) == -1) {
400    }
401    return RET_SUCCESS;
402}
403#else
404int SetSerial(int fd, int nSpeed, int nBits, char nEvent, int nStop) {
405    struct termios newttys1, oldttys1;
406    if (tcgetattr(fd, &oldttys1) != 0) {
407        constexpr int buf_size = 1024;
408        char buf[buf_size] = { 0 };
409        strerror_r(errno, buf, buf_size);
410        return ERR_GENERIC;
411    }
412    bzero(&newttys1, sizeof(newttys1));
413    newttys1.c_cflag = GetUartSpeed(nSpeed);
414    newttys1.c_cflag |= (CLOCAL | CREAD);
415    newttys1.c_cflag &= ~CSIZE;
416    newttys1.c_lflag &= ~ICANON;
417    newttys1.c_cflag |= GetUartBits(nBits);
418    switch (nEvent) {
419        case 'O':
420            newttys1.c_cflag |= PARENB;
421            newttys1.c_iflag |= (INPCK | ISTRIP);
422            newttys1.c_cflag |= PARODD;
423            break;
424        case 'E':
425            newttys1.c_cflag |= PARENB;
426            newttys1.c_iflag |= (INPCK | ISTRIP);
427            newttys1.c_cflag &= ~PARODD;
428            break;
429        case 'N':
430            newttys1.c_cflag &= ~PARENB;
431            break;
432        default:
433            break;
434    }
435    if (nStop == UART_STOP1) {
436        newttys1.c_cflag &= ~CSTOPB;
437    } else if (nStop == UART_STOP2) {
438        newttys1.c_cflag |= CSTOPB;
439    }
440    newttys1.c_cc[VTIME] = 0;
441    newttys1.c_cc[VMIN] = 0;
442    if (tcflush(fd, TCIOFLUSH)) {
443        return ERR_GENERIC;
444    }
445    if ((tcsetattr(fd, TCSANOW, &newttys1)) != 0) {
446        return ERR_GENERIC;
447    }
448    return ERR_SUCCESS;
449}
450#endif
451
452ssize_t ReadUartDev(int handle, std::vector<uint8_t> &readBuf, size_t expectedSize) {
453    ssize_t totalBytesRead = 0;
454    uint8_t uartReadBuffer[MAX_UART_SIZE_IOBUF];
455    ssize_t bytesRead = 0;
456
457    do {
458        bytesRead = 0;
459        int ret = 0;
460        fd_set readFds;
461        FD_ZERO(&readFds);
462        FD_SET(handle, &readFds);
463        const constexpr int msTous = 1000;
464        const constexpr int sTous = 1000 * msTous;
465        struct timeval tv;
466        tv.tv_sec = 0;
467
468        if (expectedSize == 0) {
469            tv.tv_usec = WAIT_RESPONSE_TIME_OUT_MS * msTous;
470            tv.tv_sec = tv.tv_usec / sTous;
471            tv.tv_usec = tv.tv_usec % sTous;
472#ifdef HDC_HOST
473            // only host side need this
474            // in this caes
475            // We need a way to exit from the select for the destruction and recovery of the
476            // serial port read thread.
477            ret = select(handle + 1, &readFds, nullptr, nullptr, &tv);
478#else
479            ret = select(handle + 1, &readFds, nullptr, nullptr, nullptr);
480#endif
481        } else {
482            // when we have expect size , we need timeout for link data drop issue
483            tv.tv_usec = READ_GIVE_UP_TIME_OUT_TIME_MS * msTous;
484            tv.tv_sec = tv.tv_usec / sTous;
485            tv.tv_usec = tv.tv_usec % sTous;
486            ret = select(handle + 1, &readFds, nullptr, nullptr, &tv);
487        }
488        if (ret == 0 and expectedSize == 0) {
489            // no expect but timeout
490            if (g_ioCancel) {
491                g_ioCancel = true;
492                return totalBytesRead;
493            } else {
494                continue;
495            }
496        } else if (ret == 0) {
497            // we expected some byte , but not arrive before timeout
498            return totalBytesRead;
499        } else if (ret < 0) {
500            return -1; // wait failed.
501        } else {
502            // select > 0
503            size_t maxReadSize = expectedSize - static_cast<size_t>(totalBytesRead);
504            if (maxReadSize > MAX_UART_SIZE_IOBUF) {
505                maxReadSize = MAX_UART_SIZE_IOBUF;
506            }
507            bytesRead = read(handle, uartReadBuffer, maxReadSize);
508            if (bytesRead <= 0) {
509                // read failed !
510                return -1;
511            }
512        }
513        if (bytesRead > 0) {
514            readBuf.insert(readBuf.end(), uartReadBuffer, uartReadBuffer + bytesRead);
515            totalBytesRead += bytesRead;
516        }
517    } while (readBuf.size() < expectedSize or bytesRead == 0); // if caller know how many bytes it want
518    return totalBytesRead;
519}
520
521ssize_t WriteUartDev(int handle, uint8_t *data, const size_t length) {
522    ssize_t totalBytesWrite = 0;
523    do {
524        ssize_t bytesWrite = 0;
525        bytesWrite = write(handle, data + totalBytesWrite, length - totalBytesWrite);
526        if (bytesWrite < 0) {
527            if (errno == EINTR or errno == EAGAIN) {
528                continue;
529            } else {
530                // we don't know how to recory in this function
531                // need reopen device ?
532                constexpr int buf_size = 1024;
533                char buf[buf_size] = { 0 };
534                strerror_r(errno, buf, buf_size);
535                return -1;
536            }
537        } else {
538            // waits until all output written to the object referred to by fd has been transmitted.
539            tcdrain(handle);
540        }
541        totalBytesWrite += bytesWrite;
542    } while (totalBytesWrite < signed(length));
543
544    return totalBytesWrite;
545}
546
547bool CloseSerialPort(int &handle) {
548    if (handle != -1)
549    {
550        return CloseFd(handle) >= 0;
551    } else {
552        return true;
553    }
554}
555
556int CloseFd(int &fd) {
557    int rc = 0;
558#ifndef HDC_HOST
559#endif
560    if (fd > 0) {
561        rc = close(fd);
562        if (rc < 0) {
563            char buffer[Hdc::BUF_SIZE_DEFAULT] = { 0 };
564#ifdef _WIN32
565            strerror_s(buffer, Hdc::BUF_SIZE_DEFAULT, errno);
566#else
567            strerror_r(errno, buffer, Hdc::BUF_SIZE_DEFAULT);
568#endif
569        } else {
570            fd = -1;
571        }
572    }
573    return rc;
574}
575
576#endif
577