• 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 SendToPollFd(int fd, const uint8_t *buf, const int len);
145     virtual int UvTcpInit(uv_loop_t *, uv_tcp_t *, int);
146     virtual int UvRead(uv_stream_t *, uv_alloc_cb, uv_read_cb);
147     virtual int StartWorkThread(uv_loop_t *loop, uv_work_cb pFuncWorkThread,
148                                 uv_after_work_cb pFuncAfterThread, void *pThreadData);
149     virtual void TryCloseHandle(const uv_handle_t *handle, uv_close_cb closeCallBack = nullptr);
150     virtual bool TimerUvTask(uv_loop_t *loop, void *data, uv_timer_cb cb);
151     virtual bool UvTimerStart(uv_timer_t *handle, uv_timer_cb cb, uint64_t timeout,
152                               uint64_t repeat);
153     using DelayCB = std::function<void(const uint8_t, string &, const void *)>;
154     virtual bool DelayDo(uv_loop_t *loop, const int delayMs, const uint8_t flag, string msg,
155                          void *data, DelayCB cb);
156     virtual ~ExternInterface() = default;
157 };
158 class HdcSessionBase;
159 class HdcUARTBase {
160 public:
161     static ExternInterface defaultInterface;
162     HdcUARTBase(HdcSessionBase &, ExternInterface & = defaultInterface);
163     virtual ~HdcUARTBase();
164     bool ReadyForWorkThread(HSession hSession);
165     int SendUARTData(HSession hSession, uint8_t *data, const size_t length);
166     // call from session side
167     // we need know when we need clear the pending send data
168     virtual void StopSession(HSession hSession);
169 
170 protected:
171     static constexpr uint32_t DEFAULT_BAUD_RATE_VALUE = 1500000;
172 
173     bool stopped = false; // stop only can be call one times
174 
175     // something is processing on working thread
176     // Mainly used to reply a data back before stop.
177     std::mutex workThreadProcessingData;
178 
179     // review how about make a HUART in daemon side and put the devhandle in it ?
180     int uartHandle = -1;
181     virtual bool SendUARTRaw(HSession hSession, uint8_t *data, const size_t length);
SendUartSoftReset(HSession hUART,uint32_t sessionId)182     virtual void SendUartSoftReset(HSession hUART, uint32_t sessionId) {};
183     virtual RetErrCode ValidateUartPacket(vector<uint8_t> &data, uint32_t &sessionId,
184                                           uint32_t &packageIndex, size_t &fullPackageLength);
185     virtual void NotifyTransfer();
ResetOldSession(uint32_t sessionId)186     virtual void ResetOldSession(uint32_t sessionId)
187     {
188         return;
189     }
190     virtual void Restartession(const HSession session);
191 
192 #ifndef _WIN32
193     int SetSerial(int fd, int nSpeed, int nBits, char nEvent, int nStop);
194 #endif // _WIN32
195     virtual bool UartSendToHdcStream(HSession hSession, uint8_t *data, size_t size);
196     static void ReadDataFromUARTStream(uv_stream_t *stream, ssize_t nread, const uv_buf_t *buf);
197     bool uartOpened;
198 
199     static constexpr size_t MAX_READ_BUFFER = MAX_UART_SIZE_IOBUF * 10;
200     static constexpr int READ_GIVE_UP_TIME_OUT_TIME_MS = 500; // 500ms
201     virtual int UartToHdcProtocol(uv_stream_t *stream, uint8_t *appendData, int dataSize);
202     int GetUartSpeed(int speed);
203     int GetUartBits(int bits);
204     virtual void ResponseUartTrans(uint32_t sessionId, uint32_t packageIndex,
205                                    UartProtocolOption option);
206 
207     virtual size_t PackageProcess(vector<uint8_t> &data, HSession hSession = nullptr);
208     virtual RetErrCode DispatchToWorkThread(HSession hSession, uint8_t *readBuf, int readBytes);
209 
210     virtual void OnTransferError(const HSession session) = 0;
211     virtual HSession GetSession(const uint32_t sessionId, bool create = false) = 0;
212 
213     /*
214         read data from uart devices
215         Args:
216         readBuf         data will append to readBuf
217         expectedSize    function will not return until expected size read
218 
219         Return:
220         ssize_t         >   0 how many bytes read after this function called
221                         ==  0 nothing read , timeout happened(expectedSize > 0)
222                         <   0 means devices error
223     */
224 
225     // we have some oswait in huart(bind to each session/uart device)
226     virtual ssize_t ReadUartDev(std::vector<uint8_t> &readBuf, size_t expectedSize, HdcUART &uart);
227 
228     virtual ssize_t WriteUartDev(uint8_t *data, const size_t length, HdcUART &uart);
229 
230     ExternInterface &externInterface;
231 
232     virtual void RequestSendPackage(uint8_t *data, const size_t length, bool queue = true);
233     virtual void ProcessResponsePackage(const UartHead &head);
234     virtual void SendPkgInUARTOutMap();
235     virtual void ClearUARTOutMap(uint32_t sessionId);
236     virtual void EnsureAllPkgsSent();
237     static constexpr int WAIT_RESPONSE_TIME_OUT_MS = 1000; // 1000ms
238     static constexpr int OneMoreMs = 1;
239 
240     class TransferStateMachine {
241     public:
Request()242         void Request()
243         {
244             std::unique_lock<std::mutex> lock(mutex);
245             requested = true;
246             cv.notify_one();
247         }
248 
Sent()249         void Sent()
250         {
251             std::unique_lock<std::mutex> lock(mutex);
252             timeout = true;
253             // wait_for will timeout in 999ms in linux platform, so we add one more
254             timeoutPoint = std::chrono::steady_clock::now() +
255                            std::chrono::milliseconds(WAIT_RESPONSE_TIME_OUT_MS + OneMoreMs);
256             cv.notify_one();
257         }
258 
259         void Wait();
260 
261     private:
262         std::mutex mutex;
263         std::condition_variable cv;
264         bool requested = false; // some one request send something
265         std::chrono::steady_clock::time_point timeoutPoint;
266         bool timeout = false; // some data is sendout and next wait need wait response
267     } transfer;
268 
269 private:
270     HdcSessionBase &sessionBase;
271 
272     enum PkgStatus {
273         PKG_WAIT_SEND,
274         PKG_WAIT_RESPONSE,
275     };
276     struct HandleOutputPkg {
277         std::string key;
278         uint32_t sessionId = 0; // like group , sometimes we will delete by this filter
279         bool response;          // PKG for response
280         bool ack;               // UartResponseCode for this package
281         uint8_t pkgStatus;
282         vector<uint8_t> msgSendBuf;
283         size_t retryChance = 4; // how many time need retry
284         std::chrono::time_point<std::chrono::steady_clock> sendTimePoint;
285         // reivew if we need direct process UartHead ?
286         HandleOutputPkg(std::string keyIn, uint32_t sessionIdIn, uint8_t *data, size_t length,
287                         bool responseIn = false, bool ackIn = false)
keyHandleOutputPkg288             : key(keyIn),
289               sessionId(sessionIdIn),
290               response(responseIn),
291               ack(ackIn),
292               pkgStatus(PKG_WAIT_SEND),
293               msgSendBuf(data, data + length)
294         {
295         }
ToDebugStringHandleOutputPkg296         std::string ToDebugString()
297         {
298             std::string debug;
299             debug.append(key);
300             debug.append(" pkgStatus:");
301             debug.append(std::to_string(pkgStatus));
302             if (pkgStatus == PKG_WAIT_RESPONSE) {
303                 debug.append(" sent:");
304                 auto elapsedTime = std::chrono::duration_cast<std::chrono::milliseconds>(
305                     std::chrono::steady_clock::now() - sendTimePoint);
306                 debug.append(std::to_string(elapsedTime.count()));
307                 debug.append(" ms");
308                 debug.append(" retry Chance:");
309                 debug.append(std::to_string(retryChance));
310             }
311             if (response) {
312                 debug.append(" response:");
313                 if (ack) {
314                     debug.append(" ACK");
315                 } else {
316                     debug.append(" NAK");
317                 }
318             }
319             return debug;
320         }
321     };
322 
323     class TransferSlot {
324     public:
Wait(uint32_t sessionId)325         void Wait(uint32_t sessionId)
326         {
327             std::unique_lock<std::mutex> lock(mutex);
328             cv.wait(lock, [=] { return hasWaitPkg.find(sessionId) == hasWaitPkg.end(); });
329             hasWaitPkg.emplace(sessionId);
330         }
331 
Free(uint32_t sessionId)332         void Free(uint32_t sessionId)
333         {
334             std::unique_lock<std::mutex> lock(mutex);
335             hasWaitPkg.erase(sessionId);
336             cv.notify_one();
337         }
338 
339         // call when exit
WaitFree()340         void WaitFree()
341         {
342             std::unique_lock<std::mutex> lock(mutex);
343             cv.wait_for(lock, std::chrono::milliseconds(WAIT_RESPONSE_TIME_OUT_MS),
344                         [=] { return hasWaitPkg.size() == 0; });
345         }
346 
347     private:
348         std::mutex mutex;
349         std::condition_variable cv;
350         std::unordered_set<uint32_t> hasWaitPkg;
351     } slots;
352 
353     vector<HandleOutputPkg> outPkgs; // Pkg label, HOutPkg
354     std::recursive_mutex mapOutPkgsMutex;
355     struct HandleOutputPkgKeyFinder {
356         const std::string &key;
HandleOutputPkgKeyFinderHandleOutputPkgKeyFinder357         HandleOutputPkgKeyFinder(const std::string &keyIn) : key(keyIn) {}
operatorHandleOutputPkgKeyFinder358         bool operator()(const HandleOutputPkg &other)
359         {
360             return key == other.key;
361         }
362     };
363 };
364 } // namespace Hdc
365 #endif
366