• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
22 using namespace std;
23 using namespace Hdc;
24 
25 bool g_ioCancel = false;
26 
27 // review why not use QueryDosDevice ?
EnumSerialPort(bool & portChange)28 bool 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     while (dir != nullptr && ((p = readdir(dir)) != nullptr)) {
100 #ifdef HOST_LINUX
101         if (p->d_name[0] != '.' && string(p->d_name).find("tty") != std::string::npos) {
102 #else
103         if (p->d_name[0] != '.' && string(p->d_name).find("serial") != std::string::npos) {
104 #endif
105             string port = "/dev/" + string(p->d_name);
106             if (port.find("/dev/ttyUSB") == 0 || port.find("/dev/ttySerial") == 0 || port.find("/dev/cu.") == 0) {
107                 newPortInfo.push_back(port);
108                 auto it = std::find(serialPortInfo.begin(), serialPortInfo.end(), port);
109                 if (it == serialPortInfo.end()) {
110                     portChange = true;
111                     WRITE_LOG(LOG_INFO, "new port:%s", port.c_str());
112                 }
113             }
114         }
115     }
116     if (dir != nullptr) {
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 
141 std::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 
158 static constexpr int PORT_NAME_LEN = 10;
159 static constexpr int NUM = 2;
160 
161 HANDLE 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 
182 bool 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 
236 bool 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 
245 ssize_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 
293 ssize_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 
318 int 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 
337 int 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 
348 int 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     flag &= ~O_NONBLOCK;
359     fcntl(uartHandle, F_SETFL, flag);
360 
361     return uartHandle;
362 }
363 
364 #ifdef HOST_MAC
365 int SetSerial(int fd, int nSpeed, int nBits, char nEvent, int nStop) {
366     struct termios options, oldttys1;
367     if (tcgetattr(fd, &oldttys1) != 0) {
368         constexpr int buf_size = 1024;
369         char buf[buf_size] = { 0 };
370         strerror_r(errno, buf, buf_size);
371         return ERR_GENERIC;
372     }
373 
374     errno_t nRet = 0;
375     nRet = memcpy_s(&options, sizeof(options), &oldttys1, sizeof(options));
376     if (nRet != EOK) {
377         return ERR_GENERIC;
378     }
379     cfmakeraw(&options);
380     options.c_cc[VMIN] = 0;
381     options.c_cc[VTIME] = 10; // 10 * 1/10 sec : 1 sec
382 
383     cfsetspeed(&options, B19200);
384     options.c_cflag |= GetUartBits(nBits); // Use 8 bit words
385     options.c_cflag &= ~PARENB;
386 
387     speed_t speed = nSpeed;
388     if (ioctl(fd, IOSSIOSPEED, &speed) == -1) {
389     }
390     if ((tcsetattr(fd, TCSANOW, &options)) != 0) {
391         return ERR_GENERIC;
392     }
393     if (ioctl(fd, IOSSIOSPEED, &speed) == -1) {
394     }
395     return RET_SUCCESS;
396 }
397 #else
398 int SetSerial(int fd, int nSpeed, int nBits, char nEvent, int nStop) {
399     struct termios newttys1, oldttys1;
400     if (tcgetattr(fd, &oldttys1) != 0) {
401         constexpr int buf_size = 1024;
402         char buf[buf_size] = { 0 };
403         strerror_r(errno, buf, buf_size);
404         return ERR_GENERIC;
405     }
406     bzero(&newttys1, sizeof(newttys1));
407     newttys1.c_cflag = GetUartSpeed(nSpeed);
408     newttys1.c_cflag |= (CLOCAL | CREAD);
409     newttys1.c_cflag &= ~CSIZE;
410     newttys1.c_lflag &= ~ICANON;
411     newttys1.c_cflag |= GetUartBits(nBits);
412     switch (nEvent) {
413         case 'O':
414             newttys1.c_cflag |= PARENB;
415             newttys1.c_iflag |= (INPCK | ISTRIP);
416             newttys1.c_cflag |= PARODD;
417             break;
418         case 'E':
419             newttys1.c_cflag |= PARENB;
420             newttys1.c_iflag |= (INPCK | ISTRIP);
421             newttys1.c_cflag &= ~PARODD;
422             break;
423         case 'N':
424             newttys1.c_cflag &= ~PARENB;
425             break;
426         default:
427             break;
428     }
429     if (nStop == UART_STOP1) {
430         newttys1.c_cflag &= ~CSTOPB;
431     } else if (nStop == UART_STOP2) {
432         newttys1.c_cflag |= CSTOPB;
433     }
434     newttys1.c_cc[VTIME] = 0;
435     newttys1.c_cc[VMIN] = 0;
436     if (tcflush(fd, TCIOFLUSH)) {
437         return ERR_GENERIC;
438     }
439     if ((tcsetattr(fd, TCSANOW, &newttys1)) != 0) {
440         return ERR_GENERIC;
441     }
442     return ERR_SUCCESS;
443 }
444 #endif
445 
446 ssize_t ReadUartDev(int handle, std::vector<uint8_t> &readBuf, size_t expectedSize) {
447     ssize_t totalBytesRead = 0;
448     uint8_t uartReadBuffer[MAX_UART_SIZE_IOBUF];
449     ssize_t bytesRead = 0;
450 
451     do {
452         bytesRead = 0;
453         int ret = 0;
454         fd_set readFds;
455         FD_ZERO(&readFds);
456         FD_SET(handle, &readFds);
457         const constexpr int msTous = 1000;
458         const constexpr int sTous = 1000 * msTous;
459         struct timeval tv;
460         tv.tv_sec = 0;
461 
462         if (expectedSize == 0) {
463             tv.tv_usec = WAIT_RESPONSE_TIME_OUT_MS * msTous;
464             tv.tv_sec = tv.tv_usec / sTous;
465             tv.tv_usec = tv.tv_usec % sTous;
466 #ifdef HDC_HOST
467             // only host side need this
468             // in this caes
469             // We need a way to exit from the select for the destruction and recovery of the
470             // serial port read thread.
471             ret = select(handle + 1, &readFds, nullptr, nullptr, &tv);
472 #else
473             ret = select(handle + 1, &readFds, nullptr, nullptr, nullptr);
474 #endif
475         } else {
476             // when we have expect size , we need timeout for link data drop issue
477             tv.tv_usec = READ_GIVE_UP_TIME_OUT_TIME_MS * msTous;
478             tv.tv_sec = tv.tv_usec / sTous;
479             tv.tv_usec = tv.tv_usec % sTous;
480             ret = select(handle + 1, &readFds, nullptr, nullptr, &tv);
481         }
482         if (ret == 0 and expectedSize == 0) {
483             // no expect but timeout
484             if (g_ioCancel) {
485                 g_ioCancel = true;
486                 return totalBytesRead;
487             } else {
488                 continue;
489             }
490         } else if (ret == 0) {
491             // we expected some byte , but not arrive before timeout
492             return totalBytesRead;
493         } else if (ret < 0) {
494             return -1; // wait failed.
495         } else {
496             // select > 0
497             size_t maxReadSize = expectedSize - totalBytesRead;
498             if (maxReadSize > MAX_UART_SIZE_IOBUF) {
499                 maxReadSize = MAX_UART_SIZE_IOBUF;
500             }
501             bytesRead = read(handle, uartReadBuffer, maxReadSize);
502             if (bytesRead <= 0) {
503                 // read failed !
504                 return -1;
505             }
506         }
507         if (bytesRead > 0) {
508             readBuf.insert(readBuf.end(), uartReadBuffer, uartReadBuffer + bytesRead);
509             totalBytesRead += bytesRead;
510         }
511     } while (readBuf.size() < expectedSize or bytesRead == 0); // if caller know how many bytes it want
512     return totalBytesRead;
513 }
514 
515 ssize_t WriteUartDev(int handle, uint8_t *data, const size_t length) {
516     ssize_t totalBytesWrite = 0;
517     do {
518         ssize_t bytesWrite = 0;
519         bytesWrite = write(handle, data + totalBytesWrite, length - totalBytesWrite);
520         if (bytesWrite < 0) {
521             if (errno == EINTR or errno == EAGAIN) {
522                 continue;
523             } else {
524                 // we don't know how to recory in this function
525                 // need reopen device ?
526                 constexpr int buf_size = 1024;
527                 char buf[buf_size] = { 0 };
528                 strerror_r(errno, buf, buf_size);
529                 return -1;
530             }
531         } else {
532             // waits until all output written to the object referred to by fd has been transmitted.
533             tcdrain(handle);
534         }
535         totalBytesWrite += bytesWrite;
536     } while (totalBytesWrite < signed(length));
537 
538     return totalBytesWrite;
539 }
540 
541 bool CloseSerialPort(int &handle) {
542     if (handle != -1)
543     {
544         return CloseFd(handle) >= 0;
545     } else {
546         return true;
547     }
548 }
549 
550 int CloseFd(int &fd) {
551     int rc = 0;
552 #ifndef HDC_HOST
553 #endif
554     if (fd > 0) {
555         rc = close(fd);
556         if (rc < 0) {
557             char buffer[Hdc::BUF_SIZE_DEFAULT] = { 0 };
558 #ifdef _WIN32
559             strerror_s(buffer, Hdc::BUF_SIZE_DEFAULT, errno);
560 #else
561             strerror_r(errno, buffer, Hdc::BUF_SIZE_DEFAULT);
562 #endif
563         } else {
564             fd = -1;
565         }
566     }
567     return rc;
568 }
569 
570 #endif
571