• 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 #ifndef HDC_UART_H
16 #define HDC_UART_H
17 #include "common.h"
18 
19 #include <chrono>
20 #include <numeric>
21 #include <sstream>
22 #include <unordered_set>
23 
24 #ifndef _WIN32
25 #include <termios.h> // struct termios
26 #endif               // _WIN32
27 
28 namespace Hdc {
29 #define USE_UART_CHECKSUM // all the data and head will have a checksum
30 #undef HDC_UART_TIMER_LOG // will have a lot of log from timer
31 
32 enum UartProtocolOption {
33     PKG_OPTION_TAIL = 1,  // makr is the last packget, can be send to session.
34     PKG_OPTION_RESET = 2, // host request reset session in daemon
35     PKG_OPTION_ACK = 4,   // response the pkg is received
36     PKG_OPTION_NAK = 8,   // request resend pkg again
37     PKG_OPTION_FREE = 16, // request free this session, some unable recovery error happened
38 };
39 
40 static_assert(MAX_UART_SIZE_IOBUF != 0);
41 
42 #pragma pack(push)
43 #pragma pack(1)
44 struct UartHead {
45     UartHead(const UartHead &) = delete;
46     UartHead &operator=(const UartHead &) = delete;
47     UartHead(UartHead &&) = default;
48 
49     uint8_t flag[2];           // magic word
50     uint16_t option;           // UartProtocolOption
51     uint32_t sessionId;        // the package owner (COM dev owner)
52     uint32_t dataSize;         // data size not include head
53     uint32_t packageIndex;     // package index in this session
54     uint32_t dataCheckSum = 0; // data checksum
55     uint32_t headCheckSum = 0; // head checksum
56     std::string ToPkgIdentityString(bool responsePackage = false) const
57     {
58         std::ostringstream oss;
59         if (responsePackage) {
60             oss << "R-";
61         }
62         oss << "Id:" << sessionId;
63         oss << "pkgIdx:" << packageIndex;
64         return oss.str();
65     };
ToDebugStringUartHead66     std::string ToDebugString() const
67     {
68         std::ostringstream oss;
69         oss << "UartHead [";
70         oss << " flag:" << std::hex << unsigned(flag[0]) << " " << unsigned(flag[1]) << std::dec;
71         oss << " option:" << unsigned(option);
72         oss << " sessionId:" << sessionId;
73         oss << " dataSize:" << dataSize;
74         oss << " packageIndex:" << packageIndex;
75         if (dataSize != 0) {
76             oss << " dataCheckSum:" << std::hex << dataCheckSum;
77         }
78         oss << " headCheckSum:" << std::hex << headCheckSum;
79         oss << std::dec;
80         oss << "]";
81         return oss.str();
82     };
83     UartHead(uint32_t sessionIdIn = 0, uint8_t optionIn = 0, uint32_t dataSizeIn = 0,
84              uint32_t packageIndexIn = 0)
85         : flag {PACKET_FLAG[0], PACKET_FLAG[1]},
86           option(optionIn),
87           sessionId(sessionIdIn),
88           dataSize(dataSizeIn),
89           packageIndex(packageIndexIn)
90     {
91     }
92     bool operator==(const UartHead &r) const
93     {
94         return flag[0] == r.flag[0] and flag[1] == r.flag[1] and option == r.option and
95                dataSize == r.dataSize and packageIndex == r.packageIndex;
96     }
IsResponsePackageUartHead97     bool IsResponsePackage() const
98     {
99         return (option & PKG_OPTION_ACK) or (option & PKG_OPTION_NAK);
100     }
UpdateCheckSumUartHead101     void UpdateCheckSum()
102     {
103 #ifdef USE_UART_CHECKSUM
104         if (dataSize != 0) {
105             const uint8_t *data = reinterpret_cast<const uint8_t *>(this) + sizeof(UartHead);
106             dataCheckSum = std::accumulate(data, data + dataSize, 0u);
107         }
108         const uint8_t *head = reinterpret_cast<const uint8_t *>(this);
109         size_t headCheckSumLen = sizeof(UartHead) - sizeof(headCheckSum);
110         headCheckSum = std::accumulate(head, head + headCheckSumLen, 0u);
111 #endif
112     }
ValidateHeadUartHead113     bool ValidateHead() const
114     {
115 #ifdef USE_UART_CHECKSUM
116         const uint8_t *head = reinterpret_cast<const uint8_t *>(this);
117         size_t headCheckSumLen = sizeof(UartHead) - sizeof(headCheckSum);
118         return (headCheckSum == std::accumulate(head, head + headCheckSumLen, 0u));
119 #else
120         return true;
121 #endif
122     }
ValidateDataUartHead123     bool ValidateData() const
124     {
125 #ifdef USE_UART_CHECKSUM
126         const uint8_t *data = reinterpret_cast<const uint8_t *>(this) + sizeof(UartHead);
127         if (dataSize == 0) {
128             return true;
129         } else {
130             return (dataCheckSum == std::accumulate(data, data + dataSize, 0u));
131         }
132 #else
133         return true;
134 #endif
135     }
136 };
137 #pragma pack(pop)
138 
139 // we need virtual interface for UT the free function
140 class ExternInterface {
141 public:
142     virtual void SetTcpOptions(uv_tcp_t *tcpHandle);
143     virtual int SendToStream(uv_stream_t *handleStream, const uint8_t *buf, const int bufLen);
144     virtual int UvTcpInit(uv_loop_t *, uv_tcp_t *, int);
145     virtual int UvRead(uv_stream_t *, uv_alloc_cb, uv_read_cb);
146     virtual int StartWorkThread(uv_loop_t *loop, uv_work_cb pFuncWorkThread,
147                                 uv_after_work_cb pFuncAfterThread, void *pThreadData);
148     virtual void TryCloseHandle(const uv_handle_t *handle, uv_close_cb closeCallBack = nullptr);
149     virtual bool TimerUvTask(uv_loop_t *loop, void *data, uv_timer_cb cb);
150     virtual bool UvTimerStart(uv_timer_t *handle, uv_timer_cb cb, uint64_t timeout,
151                               uint64_t repeat);
152     using DelayCB = std::function<void(const uint8_t, string &, const void *)>;
153     virtual bool DelayDo(uv_loop_t *loop, const int delayMs, const uint8_t flag, string msg,
154                          void *data, DelayCB cb);
155     virtual ~ExternInterface() = default;
156 };
157 class HdcSessionBase;
158 class HdcUARTBase {
159 public:
160     static ExternInterface defaultInterface;
161     HdcUARTBase(HdcSessionBase &, ExternInterface & = defaultInterface);
162     virtual ~HdcUARTBase();
163     bool ReadyForWorkThread(HSession hSession);
164     int SendUARTData(HSession hSession, uint8_t *data, const size_t length);
165     // call from session side
166     // we need know when we need clear the pending send data
167     virtual void StopSession(HSession hSession);
168 
169 protected:
170     static constexpr uint32_t DEFAULT_BAUD_RATE_VALUE = 1500000;
171 
172     bool stopped = false; // stop only can be call one times
173 
174     // something is processing on working thread
175     // Mainly used to reply a data back before stop.
176     std::mutex workThreadProcessingData;
177 
178     // review how about make a HUART in daemon side and put the devhandle in it ?
179     int uartHandle = -1;
180     virtual bool SendUARTRaw(HSession hSession, uint8_t *data, const size_t length);
SendUartSoftReset(HSession hUART,uint32_t sessionId)181     virtual void SendUartSoftReset(HSession hUART, uint32_t sessionId) {};
182     virtual RetErrCode ValidateUartPacket(vector<uint8_t> &data, uint32_t &sessionId,
183                                           uint32_t &packageIndex, size_t &fullPackageLength);
184     virtual void NotifyTransfer();
ResetOldSession(uint32_t sessionId)185     virtual void ResetOldSession(uint32_t sessionId)
186     {
187         return;
188     }
189     virtual void Restartession(const HSession session);
190 
191 #ifndef _WIN32
192     int SetSerial(int fd, int nSpeed, int nBits, char nEvent, int nStop);
193 #endif // _WIN32
194     virtual bool UartSendToHdcStream(HSession hSession, uint8_t *data, size_t size);
195     static void ReadDataFromUARTStream(uv_stream_t *stream, ssize_t nread, const uv_buf_t *buf);
196     bool uartOpened;
197 
198     static constexpr size_t MAX_READ_BUFFER = MAX_UART_SIZE_IOBUF * 10;
199     static constexpr int ReadGiveUpTimeOutTimeMs = 500; // 500ms
200     virtual int UartToHdcProtocol(uv_stream_t *stream, uint8_t *appendData, int dataSize);
201     int GetUartSpeed(int speed);
202     int GetUartBits(int bits);
203     virtual void ResponseUartTrans(uint32_t sessionId, uint32_t packageIndex,
204                                    UartProtocolOption option);
205 
206     virtual size_t PackageProcess(vector<uint8_t> &data, HSession hSession = nullptr);
207     virtual RetErrCode DispatchToWorkThread(HSession hSession, uint8_t *readBuf, int readBytes);
208 
209     virtual void OnTransferError(const HSession session) = 0;
210     virtual HSession GetSession(const uint32_t sessionId, bool create = false) = 0;
211 
212     /*
213         read data from uart devices
214         Args:
215         readBuf         data will append to readBuf
216         expectedSize    function will not return until expected size read
217 
218         Return:
219         ssize_t         >   0 how many bytes read after this function called
220                         ==  0 nothing read , timeout happened(expectedSize > 0)
221                         <   0 means devices error
222     */
223 
224     // we have some oswait in huart(bind to each session/uart device)
225     virtual ssize_t ReadUartDev(std::vector<uint8_t> &readBuf, size_t expectedSize, HdcUART &uart);
226 
227     virtual ssize_t WriteUartDev(uint8_t *data, const size_t length, HdcUART &uart);
228 
229     ExternInterface &externInterface;
230 
231     virtual void RequestSendPackage(uint8_t *data, const size_t length, bool queue = true);
232     virtual void ProcessResponsePackage(const UartHead &head);
233     virtual void SendPkgInUARTOutMap();
234     virtual void ClearUARTOutMap(uint32_t sessionId);
235     virtual void EnsureAllPkgsSent();
236     static constexpr int WaitResponseTimeOutMs = 1000; // 1000ms
237     static constexpr int OneMoreMs = 1;
238 
239     class TransferStateMachine {
240     public:
Request()241         void Request()
242         {
243             std::unique_lock<std::mutex> lock(mutex);
244             requested = true;
245             cv.notify_one();
246         }
247 
Sent()248         void Sent()
249         {
250             std::unique_lock<std::mutex> lock(mutex);
251             timeout = true;
252             // wait_for will timeout in 999ms in linux platform, so we add one more
253             timeoutPoint = std::chrono::steady_clock::now() +
254                            std::chrono::milliseconds(WaitResponseTimeOutMs + OneMoreMs);
255             cv.notify_one();
256         }
257 
258         void Wait();
259 
260     private:
261         std::mutex mutex;
262         std::condition_variable cv;
263         bool requested = false; // some one request send something
264         std::chrono::steady_clock::time_point timeoutPoint;
265         bool timeout = false; // some data is sendout and next wait need wait response
266     } transfer;
267 
268 private:
269     HdcSessionBase &sessionBase;
270 
271     enum PkgStatus {
272         PKG_WAIT_SEND,
273         PKG_WAIT_RESPONSE,
274     };
275     struct HandleOutputPkg {
276         std::string key;
277         uint32_t sessionId = 0; // like group , sometimes we will delete by this filter
278         bool response;          // PKG for response
279         bool ack;               // UartResponseCode for this package
280         uint8_t pkgStatus;
281         vector<uint8_t> msgSendBuf;
282         size_t retryChance = 4; // how many time need retry
283         std::chrono::time_point<std::chrono::steady_clock> sendTimePoint;
284         // reivew if we need direct process UartHead ?
285         HandleOutputPkg(std::string keyIn, uint32_t sessionIdIn, uint8_t *data, size_t length,
286                         bool responseIn = false, bool ackIn = false)
keyHandleOutputPkg287             : key(keyIn),
288               sessionId(sessionIdIn),
289               response(responseIn),
290               ack(ackIn),
291               pkgStatus(PKG_WAIT_SEND),
292               msgSendBuf(data, data + length)
293         {
294         }
ToDebugStringHandleOutputPkg295         std::string ToDebugString()
296         {
297             std::string debug;
298             debug.append(key);
299             debug.append(" pkgStatus:");
300             debug.append(std::to_string(pkgStatus));
301             if (pkgStatus == PKG_WAIT_RESPONSE) {
302                 debug.append(" sent:");
303                 auto elapsedTime = std::chrono::duration_cast<std::chrono::milliseconds>(
304                     std::chrono::steady_clock::now() - sendTimePoint);
305                 debug.append(std::to_string(elapsedTime.count()));
306                 debug.append(" ms");
307                 debug.append(" retry Chance:");
308                 debug.append(std::to_string(retryChance));
309             }
310             if (response) {
311                 debug.append(" response:");
312                 if (ack) {
313                     debug.append(" ACK");
314                 } else {
315                     debug.append(" NAK");
316                 }
317             }
318             return debug;
319         }
320     };
321 
322     class TransferSlot {
323     public:
Wait(uint32_t sessionId)324         void Wait(uint32_t sessionId)
325         {
326             std::unique_lock<std::mutex> lock(mutex);
327             cv.wait(lock, [=] { return hasWaitPkg.find(sessionId) == hasWaitPkg.end(); });
328             hasWaitPkg.emplace(sessionId);
329         }
330 
Free(uint32_t sessionId)331         void Free(uint32_t sessionId)
332         {
333             std::unique_lock<std::mutex> lock(mutex);
334             hasWaitPkg.erase(sessionId);
335             cv.notify_one();
336         }
337 
338         // call when exit
WaitFree()339         void WaitFree()
340         {
341             std::unique_lock<std::mutex> lock(mutex);
342             cv.wait_for(lock, std::chrono::milliseconds(WaitResponseTimeOutMs),
343                         [=] { return hasWaitPkg.size() == 0; });
344         }
345 
346     private:
347         std::mutex mutex;
348         std::condition_variable cv;
349         std::unordered_set<uint32_t> hasWaitPkg;
350     } slots;
351 
352     vector<HandleOutputPkg> outPkgs; // Pkg label, HOutPkg
353     std::recursive_mutex mapOutPkgsMutex;
354     struct HandleOutputPkgKeyFinder {
355         const std::string &key;
HandleOutputPkgKeyFinderHandleOutputPkgKeyFinder356         HandleOutputPkgKeyFinder(const std::string &keyIn) : key(keyIn) {}
operatorHandleOutputPkgKeyFinder357         bool operator()(const HandleOutputPkg &other)
358         {
359             return key == other.key;
360         }
361     };
362 };
363 } // namespace Hdc
364 #endif
365