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