• 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 "daemon_usb.h"
16 #include <cerrno>
17 #include <cstddef>
18 #include "arpa/inet.h"
19 #include "asm-generic/int-ll64.h"
20 #include "fcntl.h"
21 #include "linux/usb/functionfs.h"
22 #include "new"
23 #include "sched.h"
24 #include "system_depend.h"
25 #include "unistd.h"
26 #include "uv/unix.h"
27 #include "daemon.h"
28 #include "usb_ffs.h"
29 
30 namespace Hdc {
HdcDaemonUSB(const bool serverOrDaemonIn,void * ptrMainBase)31 HdcDaemonUSB::HdcDaemonUSB(const bool serverOrDaemonIn, void *ptrMainBase)
32     : HdcUSBBase(serverOrDaemonIn, ptrMainBase)
33 {
34 }
35 
~HdcDaemonUSB()36 HdcDaemonUSB::~HdcDaemonUSB()
37 {
38     // Closed in the IO loop, no longer closing CLOSE ENDPOINT
39     Base::CloseFd(controlEp);
40     if (ctxRecv.buf) {
41         delete[] ctxRecv.buf;
42     }
43     uv_fs_req_cleanup(&ctxRecv.req);
44 }
45 
Stop()46 void HdcDaemonUSB::Stop()
47 {
48     WRITE_LOG(LOG_DEBUG, "HdcDaemonUSB Stop");
49     // Here only clean up the IO-related resources, session related resources clear reason to clean up the session
50     // module
51     modRunning = false;
52     WRITE_LOG(LOG_DEBUG, "HdcDaemonUSB Stop free main session");
53     Base::TryCloseHandle((uv_handle_t *)&checkEP);
54     CloseEndpoint(&usbHandle);
55     WRITE_LOG(LOG_DEBUG, "HdcDaemonUSB Stop free main session finish");
56 }
57 
GetDevPath(const std::string & path)58 string HdcDaemonUSB::GetDevPath(const std::string &path)
59 {
60     DIR *dir = ::opendir(path.c_str());
61     if (dir == nullptr) {
62         WRITE_LOG(LOG_WARN, "%s: cannot open devpath: errno: %d", path.c_str(), errno);
63         return "";
64     }
65 
66     string res = USB_FFS_BASE;
67     string node;
68     int count = 0;
69     struct dirent *entry = nullptr;
70     while ((entry = ::readdir(dir))) {
71         if (*entry->d_name == '.') {
72             continue;
73         }
74         node = entry->d_name;
75         ++count;
76     }
77     if (count > 1) {
78         res += "hdc";
79     } else {
80         res += node;
81     }
82     ::closedir(dir);
83     return res;
84 }
85 
GetMaxPacketSize(int fdFfs)86 int HdcDaemonUSB::GetMaxPacketSize(int fdFfs)
87 {
88     // no ioctl support, todo dynamic get
89     return MAX_PACKET_SIZE_HISPEED;
90 }
91 
Initial()92 int HdcDaemonUSB::Initial()
93 {
94     // after Linux-3.8,kernel switch to the USB Function FS
95     // Implement USB hdc function in user space
96     WRITE_LOG(LOG_DEBUG, "HdcDaemonUSB init");
97     basePath = GetDevPath(USB_FFS_BASE);
98     if (access((basePath + "/ep0").c_str(), F_OK) != 0) {
99         WRITE_LOG(LOG_DEBUG, "Only support usb-ffs, make sure kernel3.8+ and usb-ffs enabled, usbmode disabled");
100         return ERR_API_FAIL;
101     }
102     ctxRecv.thisClass = this;
103     ctxRecv.bufSizeMax = Base::GetUsbffsBulkSize();
104     ctxRecv.buf = new uint8_t[ctxRecv.bufSizeMax]();
105     if (!ctxRecv.buf) {
106         WRITE_LOG(LOG_FATAL, "Init alloc memory failed");
107         return ERR_BUF_ALLOC;
108     }
109 
110     HdcDaemon *daemon = (HdcDaemon *)clsMainBase;
111     WRITE_LOG(LOG_DEBUG, "HdcDaemonUSB::Initiall");
112     uv_timer_init(&daemon->loopMain, &checkEP);
113     checkEP.data = this;
114     uv_timer_start(&checkEP, WatchEPTimer, 0, TIME_BASE);
115     return 0;
116 }
117 
118 // make gnuc++ happy. Clang support direct assignment value to structure, buf g++ weakness
FillUsbV2Head(usb_functionfs_desc_v2 & descUsbFfs)119 void HdcDaemonUSB::FillUsbV2Head(usb_functionfs_desc_v2 &descUsbFfs)
120 {
121     descUsbFfs.head.magic = LONG_LE(FUNCTIONFS_DESCRIPTORS_MAGIC_V2);
122     descUsbFfs.head.length = LONG_LE(sizeof(descUsbFfs));
123     descUsbFfs.head.flags
124         = FUNCTIONFS_HAS_FS_DESC | FUNCTIONFS_HAS_HS_DESC | FUNCTIONFS_HAS_SS_DESC | FUNCTIONFS_HAS_MS_OS_DESC;
125     descUsbFfs.config1Count = 3;
126     descUsbFfs.config2Count = 3;
127     descUsbFfs.config3Count = 5;
128     descUsbFfs.configWosCount = 1;
129     descUsbFfs.config1Desc = config1;
130     descUsbFfs.config2Desc = config2;
131     descUsbFfs.config3Desc = config3;
132     descUsbFfs.wosHead.interface = 1;
133     descUsbFfs.wosHead.dwLength = LONG_LE(sizeof(descUsbFfs.wosHead) + sizeof(descUsbFfs.wosDesc));
134     descUsbFfs.wosHead.bcdVersion = SHORT_LE(1);
135     descUsbFfs.wosHead.wIndex = SHORT_LE(4);
136     descUsbFfs.wosHead.bCount = 1;
137     descUsbFfs.wosHead.Reserved = 0;
138     descUsbFfs.wosDesc.bFirstInterfaceNumber = 0;
139     descUsbFfs.wosDesc.Reserved1 = 1;
140     descUsbFfs.wosDesc.CompatibleID[0] = 'W';
141     descUsbFfs.wosDesc.CompatibleID[1] = 'I';
142     descUsbFfs.wosDesc.CompatibleID[2] = 'N';
143     descUsbFfs.wosDesc.CompatibleID[3] = 'U';
144     descUsbFfs.wosDesc.CompatibleID[4] = 'S';
145     descUsbFfs.wosDesc.CompatibleID[5] = 'B';
146     descUsbFfs.wosDesc.CompatibleID[6] = '\0';
147     Base::ZeroArray(descUsbFfs.wosDesc.SubCompatibleID);
148     Base::ZeroArray(descUsbFfs.wosDesc.Reserved2);
149 }
150 
151 // DAEMON end USB module USB-FFS EP port connection
ConnectEPPoint(HUSB hUSB)152 int HdcDaemonUSB::ConnectEPPoint(HUSB hUSB)
153 {
154     int ret = ERR_GENERIC;
155     struct usb_functionfs_desc_v2 descUsbFfs = {};
156     FillUsbV2Head(descUsbFfs);
157     while (true) {
158         if (controlEp <= 0) {
159             // After the control port sends the instruction, the device is initialized by the device to the HOST host,
160             // which can be found for USB devices. Do not send initialization to the EP0 control port, the USB
161             // device will not be initialized by Host
162             WRITE_LOG(LOG_DEBUG, "Begin send to control(EP0) for usb descriptor init");
163             string ep0Path = basePath + "/ep0";
164             if ((controlEp = open(ep0Path.c_str(), O_RDWR)) < 0) {
165                 WRITE_LOG(LOG_WARN, "%s: cannot open control endpoint: errno=%d", ep0Path.c_str(), errno);
166                 break;
167             }
168             if (write(controlEp, &descUsbFfs, sizeof(descUsbFfs)) < 0) {
169                 WRITE_LOG(LOG_WARN, "%s: write ffs configs failed: errno=%d", ep0Path.c_str(), errno);
170                 break;
171             }
172             if (write(controlEp, &USB_FFS_VALUE, sizeof(USB_FFS_VALUE)) < 0) {
173                 WRITE_LOG(LOG_WARN, "%s: write USB_FFS_VALUE failed: errno=%d", ep0Path.c_str(), errno);
174                 break;
175             }
176             // active usbrc,Send USB initialization signal
177             SystemDepend::SetDevItem("sys.usb.ffs.ready", "1");
178             WRITE_LOG(LOG_DEBUG, "ConnectEPPoint ctrl init finish, set usb-ffs ready");
179         }
180         string outPath = basePath + "/ep1";
181         if ((hUSB->bulkOut = open(outPath.c_str(), O_RDWR)) < 0) {
182             WRITE_LOG(LOG_WARN, "%s: cannot open bulk-out ep: errno=%d", outPath.c_str(), errno);
183             break;
184         }
185         string inPath = basePath + "/ep2";
186         if ((hUSB->bulkIn = open(inPath.c_str(), O_RDWR)) < 0) {
187             WRITE_LOG(LOG_WARN, "%s: cannot open bulk-in ep: errno=%d", inPath.c_str(), errno);
188             break;
189         }
190         // cannot open with O_CLOEXEC, must fcntl
191         fcntl(controlEp, F_SETFD, FD_CLOEXEC);
192         fcntl(hUSB->bulkOut, F_SETFD, FD_CLOEXEC);
193         fcntl(hUSB->bulkIn, F_SETFD, FD_CLOEXEC);
194         hUSB->wMaxPacketSizeSend = GetMaxPacketSize(hUSB->bulkIn);
195 
196         WRITE_LOG(LOG_DEBUG, "New bulk in\\out open bulkout:%d bulkin:%d", hUSB->bulkOut, hUSB->bulkIn);
197         ret = RET_SUCCESS;
198         break;
199     }
200     if (ret != RET_SUCCESS) {
201         CloseEndpoint(hUSB, true);
202     }
203     return ret;
204 }
205 
CloseEndpoint(HUSB hUSB,bool closeCtrlEp)206 void HdcDaemonUSB::CloseEndpoint(HUSB hUSB, bool closeCtrlEp)
207 {
208     Base::CloseFd(hUSB->bulkIn);
209     Base::CloseFd(hUSB->bulkOut);
210     if (controlEp > 0 && closeCtrlEp) {
211         Base::CloseFd(controlEp);
212         controlEp = 0;
213     }
214     isAlive = false;
215     WRITE_LOG(LOG_FATAL, "DaemonUSB close endpoint");
216 }
217 
ResetOldSession(uint32_t sessionId)218 void HdcDaemonUSB::ResetOldSession(uint32_t sessionId)
219 {
220     HdcDaemon *daemon = reinterpret_cast<HdcDaemon *>(clsMainBase);
221     if (sessionId == 0) {
222         sessionId = currentSessionId;
223     }
224     HSession hSession = daemon->AdminSession(OP_QUERY, sessionId, nullptr);
225     if (hSession == nullptr) {
226         return;
227     }
228     // The Host side is restarted, but the USB cable is still connected
229     WRITE_LOG(LOG_WARN, "Hostside softreset to restart daemon, old sessionId:%u", sessionId);
230     daemon->FreeSession(sessionId);
231 }
232 
233 // Prevent other USB data misfortunes to send the program crash
AvailablePacket(uint8_t * ioBuf,int ioBytes,uint32_t * sessionId)234 int HdcDaemonUSB::AvailablePacket(uint8_t *ioBuf, int ioBytes, uint32_t *sessionId)
235 {
236     int ret = RET_SUCCESS;
237     while (true) {
238         if (!IsUsbPacketHeader(ioBuf, ioBytes)) {
239             break;
240         }
241         // usb header
242         USBHead *usbPayloadHeader = reinterpret_cast<struct USBHead *>(ioBuf);
243         uint32_t inSessionId = ntohl(usbPayloadHeader->sessionId);
244         if ((usbPayloadHeader->option & USB_OPTION_RESET)) {
245             ResetOldSession(inSessionId);
246             ret = ERR_IO_SOFT_RESET;
247             break;
248         }
249         *sessionId = inSessionId;
250         break;
251     }
252     return ret;
253 }
254 
255 // Work in subcrete,Work thread is ready
ReadyForWorkThread(HSession hSession)256 bool HdcDaemonUSB::ReadyForWorkThread(HSession hSession)
257 {
258     HdcUSBBase::ReadyForWorkThread(hSession);
259     return true;
260 };
261 
CloseBulkEp(bool bulkInOut,int bulkFd,uv_loop_t * loop)262 int HdcDaemonUSB::CloseBulkEp(bool bulkInOut, int bulkFd, uv_loop_t *loop)
263 {
264     struct CtxCloseBulkEp {
265         uv_fs_t req;
266         HdcDaemonUSB *thisClass;
267         bool bulkInOut;
268     };
269     CtxCloseBulkEp *ctx = new(std::nothrow) CtxCloseBulkEp();
270     if (ctx == nullptr) {
271         WRITE_LOG(LOG_FATAL, "CloseBulkEp new ctx failed");
272         return -1;
273     }
274     uv_fs_t *req = &ctx->req;
275     req->data = ctx;
276     ctx->bulkInOut = bulkInOut;
277     ctx->thisClass = this;
278     isAlive = false;
279     WRITE_LOG(LOG_DEBUG, "CloseBulkEp bulkFd:%d", bulkFd);
280     uv_fs_close(loop, req, bulkFd, [](uv_fs_t *req) {
281         auto ctx = (CtxCloseBulkEp *)req->data;
282         WRITE_LOG(LOG_DEBUG, "Try to abort blukin write callback %s", ctx->bulkInOut ? "bulkin" : "bulkout");
283         if (ctx->bulkInOut) {
284             ctx->thisClass->usbHandle.bulkIn = 0;
285         } else {
286             ctx->thisClass->usbHandle.bulkOut = 0;
287         }
288         uv_fs_req_cleanup(req);
289         delete ctx;
290     });
291     return 0;
292 }
293 
SendUSBIOSync(HSession hSession,HUSB hMainUSB,const uint8_t * data,const int length)294 int HdcDaemonUSB::SendUSBIOSync(HSession hSession, HUSB hMainUSB, const uint8_t *data, const int length)
295 {
296     int bulkIn = hMainUSB->bulkIn;
297     int childRet = 0;
298     int ret = ERR_IO_FAIL;
299     int offset = 0;
300     while (modRunning && isAlive && !hSession->isDead) {
301         childRet = write(bulkIn, const_cast<uint8_t *>(data) + offset, length - offset);
302         if (childRet <= 0) {
303             int err = errno;
304             if (err == EINTR) {
305                 WRITE_LOG(LOG_DEBUG, "BulkinWrite write EINTR, try again, offset:%u", offset);
306                 continue;
307             } else {
308                 WRITE_LOG(LOG_FATAL, "BulkinWrite write fatal errno %d", err);
309                 isAlive = false;
310             }
311             break;
312         }
313         offset += childRet;
314         if (offset >= length) {
315             break;
316         }
317     }
318     if (offset == length) {
319         ret = length;
320     } else {
321         WRITE_LOG(LOG_FATAL, "BulkinWrite write failed, nsize:%d really:%d modRunning:%d isAlive:%d SessionDead:%d",
322                   length, offset, modRunning, isAlive, hSession->isDead);
323     }
324     return ret;
325 }
326 
SendUSBRaw(HSession hSession,uint8_t * data,const int length)327 int HdcDaemonUSB::SendUSBRaw(HSession hSession, uint8_t *data, const int length)
328 {
329     HdcDaemon *daemon = (HdcDaemon *)hSession->classInstance;
330     std::unique_lock<std::mutex> lock(mutexUsbFfs);
331     ++hSession->ref;
332     int ret = SendUSBIOSync(hSession, &usbHandle, data, length);
333     --hSession->ref;
334     if (ret < 0) {
335         daemon->FreeSession(hSession->sessionId);
336         WRITE_LOG(LOG_DEBUG, "SendUSBRaw try to freesession");
337     }
338     return ret;
339 }
340 
341 // cross thread call
OnNewHandshakeOK(const uint32_t sessionId)342 void HdcDaemonUSB::OnNewHandshakeOK(const uint32_t sessionId)
343 {
344     currentSessionId = sessionId;  // sync with server, and set server's real Id
345 }
346 
347 // MainThreadCall, when seession was freed
OnSessionFreeFinally(const HSession hSession)348 void HdcDaemonUSB::OnSessionFreeFinally(const HSession hSession)
349 {
350     if (currentSessionId == hSession->sessionId) {
351         isAlive = false;
352         // uv_cancel ctxRecv.req == UV_EBUSY, not effect immediately. It must be close by logic
353     }
354 }
355 
PrepareNewSession(uint32_t sessionId,uint8_t * pRecvBuf,int recvBytesIO)356 HSession HdcDaemonUSB::PrepareNewSession(uint32_t sessionId, uint8_t *pRecvBuf, int recvBytesIO)
357 {
358     HdcDaemon *daemon = reinterpret_cast<HdcDaemon *>(clsMainBase);
359     HSession hChildSession = daemon->MallocSession(false, CONN_USB, this, sessionId);
360     if (!hChildSession) {
361         return nullptr;
362     }
363     currentSessionId = sessionId;
364     Base::StartWorkThread(&daemon->loopMain, daemon->SessionWorkThread, Base::FinishWorkThread, hChildSession);
365     auto funcNewSessionUp = [](uv_timer_t *handle) -> void {
366         HSession hChildSession = reinterpret_cast<HSession>(handle->data);
367         HdcDaemon *daemon = reinterpret_cast<HdcDaemon *>(hChildSession->classInstance);
368         if (hChildSession->childLoop.active_handles == 0) {
369             return;
370         }
371         if (!hChildSession->isDead) {
372             auto ctrl = daemon->BuildCtrlString(SP_START_SESSION, 0, nullptr, 0);
373             Base::SendToStream((uv_stream_t *)&hChildSession->ctrlPipe[STREAM_MAIN], ctrl.data(), ctrl.size());
374             WRITE_LOG(LOG_DEBUG, "Main thread usbio migrate finish");
375         }
376         Base::TryCloseHandle(reinterpret_cast<uv_handle_t *>(handle), Base::CloseTimerCallback);
377     };
378     Base::TimerUvTask(&daemon->loopMain, hChildSession, funcNewSessionUp);
379     return hChildSession;
380 }
381 
UsbToHdcProtocol(uv_stream_t * stream,uint8_t * appendData,int dataSize)382 int HdcDaemonUSB::UsbToHdcProtocol(uv_stream_t *stream, uint8_t *appendData, int dataSize)
383 {
384     return Base::SendToStream(stream, appendData, dataSize);
385 }
386 
DispatchToWorkThread(uint32_t sessionId,uint8_t * readBuf,int readBytes)387 int HdcDaemonUSB::DispatchToWorkThread(uint32_t sessionId, uint8_t *readBuf, int readBytes)
388 {
389     HSession hChildSession = nullptr;
390     HdcDaemon *daemon = reinterpret_cast<HdcDaemon *>(clsMainBase);
391     int childRet = RET_SUCCESS;
392     if (sessionId == 0) {
393         // hdc packet data
394         sessionId = currentSessionId;
395     }
396     if (currentSessionId != 0 && sessionId != currentSessionId) {
397         WRITE_LOG(LOG_WARN, "New session coming, restart old sessionId:%u", currentSessionId);
398         ResetOldSession(currentSessionId);
399         currentSessionId = 0;
400     }
401     hChildSession = daemon->AdminSession(OP_QUERY, sessionId, nullptr);
402     if (!hChildSession) {
403         hChildSession = PrepareNewSession(sessionId, readBuf, readBytes);
404         if (!hChildSession) {
405             WRITE_LOG(LOG_WARN, "prep new session err for sessionId:%u", sessionId);
406             return ERR_SESSION_NOFOUND;
407         }
408     }
409 
410     if (hChildSession->childCleared || hChildSession->isDead) {
411         WRITE_LOG(LOG_WARN, "session dead clr:%d - %d", hChildSession->childCleared, hChildSession->isDead);
412         return ERR_SESSION_DEAD;
413     }
414     uv_stream_t *stream = reinterpret_cast<uv_stream_t *>(&hChildSession->dataPipe[STREAM_MAIN]);
415     if ((childRet = SendToHdcStream(hChildSession, stream, readBuf, readBytes)) < 0) {
416         WRITE_LOG(LOG_WARN, "DispatchToWorkThread SendToHdcStream err ret:%d", childRet);
417         return ERR_IO_FAIL;
418     }
419     return childRet;
420 }
421 
JumpAntiquePacket(const uint8_t & buf,ssize_t bytes) const422 bool HdcDaemonUSB::JumpAntiquePacket(const uint8_t &buf, ssize_t bytes) const
423 {
424     constexpr size_t antiqueFlagSize = 4;
425     constexpr size_t antiqueFullSize = 24;
426     // anti CNXN 0x4e584e43
427     uint8_t flag[] = { 0x43, 0x4e, 0x58, 0x4e };
428     if (bytes == antiqueFullSize && !memcmp(&buf, flag, antiqueFlagSize)) {
429         return true;
430     }
431     return false;
432 }
433 
434 // Only physically swap EP ports will be reset
OnUSBRead(uv_fs_t * req)435 void HdcDaemonUSB::OnUSBRead(uv_fs_t *req)
436 {  // Only read at the main thread
437     auto ctxIo = reinterpret_cast<CtxUvFileCommonIo *>(req->data);
438     auto hUSB = reinterpret_cast<HUSB>(ctxIo->data);
439     auto thisClass = reinterpret_cast<HdcDaemonUSB *>(ctxIo->thisClass);
440     uint8_t *bufPtr = ctxIo->buf;
441     ssize_t bytesIOBytes = req->result;
442     uint32_t sessionId = 0;
443     bool ret = false;
444     int childRet = 0;
445     if (bytesIOBytes > hUSB->wMaxPacketSizeSend && bytesIOBytes != thisClass->saveNextReadSize) {
446         WRITE_LOG(LOG_WARN, "Not full packet, wanted:%d really:%d", thisClass->saveNextReadSize, bytesIOBytes);
447     }
448     while (thisClass->isAlive) {
449         // Don't care is module running, first deal with this
450         if (bytesIOBytes < 0) {
451             // logic alive and EINTER is gdb attach
452             //
453             // [about gdb attach known issue]
454             // When GDB debugging is loaded, the number of USB read interrupts of libuv will increase. Multiple
455             // interrupts will increase the correctness of USB data reading. Setting GDB to asynchronous mode or using
456             // log debugging can avoid this problem
457             if (bytesIOBytes != -EINTR) {  // Epoll will be broken when gdb attach
458                 constexpr int bufSize = 1024;
459                 char buf[bufSize] = { 0 };
460                 uv_strerror_r(bytesIOBytes, buf, bufSize);
461                 WRITE_LOG(LOG_WARN, "USBIO ret:%d failed:%s", bytesIOBytes, buf);
462                 ret = false;
463                 break;
464             } else {
465                 WRITE_LOG(LOG_ALL, "OnUSBRead signal EINTR");
466             }
467         } else if (bytesIOBytes == 0) {  // zero packet
468             WRITE_LOG(LOG_ALL, "Zero packet received");
469         } else {
470             if (thisClass->JumpAntiquePacket(*bufPtr, bytesIOBytes)) {
471                 WRITE_LOG(LOG_DEBUG, "JumpAntiquePacket auto jump");
472                 ret = true;
473                 break;
474             }
475             // guess is head of packet
476             if ((childRet = thisClass->AvailablePacket((uint8_t *)bufPtr, bytesIOBytes, &sessionId)) != RET_SUCCESS) {
477                 if (childRet != ERR_IO_SOFT_RESET) {
478                     WRITE_LOG(LOG_WARN, "AvailablePacket check failed, ret:%d buf:%-50s", bytesIOBytes, bufPtr);
479                     break;
480                 }
481                 // reset packet
482                 childRet = 0;  // need max size
483             } else {
484                 // AvailablePacket case
485                 if ((childRet = thisClass->DispatchToWorkThread(sessionId, bufPtr, bytesIOBytes)) < 0) {
486                     WRITE_LOG(LOG_FATAL, "DispatchToWorkThread failed");
487                     break;
488                 }
489             }
490         }
491         int nextReadSize = childRet == 0 ? hUSB->wMaxPacketSizeSend : std::min(childRet, Base::GetUsbffsBulkSize());
492         thisClass->saveNextReadSize = nextReadSize;
493         if (thisClass->LoopUSBRead(hUSB, nextReadSize) < 0) {
494             WRITE_LOG(LOG_FATAL, "LoopUSBRead failed");
495             break;
496         }
497         ret = true;
498         break;
499     }
500     if (!ret) {
501         thisClass->isAlive = false;
502         thisClass->ctxRecv.atPollQueue = false;
503     }
504 }
505 
LoopUSBRead(HUSB hUSB,int readMaxWanted)506 int HdcDaemonUSB::LoopUSBRead(HUSB hUSB, int readMaxWanted)
507 {
508     int ret = ERR_GENERIC;
509     HdcDaemon *daemon = reinterpret_cast<HdcDaemon *>(clsMainBase);
510     uv_buf_t iov;
511     ctxRecv.data = hUSB;
512     ctxRecv.bufSize = readMaxWanted;
513     ctxRecv.req = {};
514     uv_fs_t *req = &ctxRecv.req;
515     req->data = &ctxRecv;
516     iov = uv_buf_init(reinterpret_cast<char *>(ctxRecv.buf), ctxRecv.bufSize);
517     ret = uv_fs_read(&daemon->loopMain, req, hUSB->bulkOut, &iov, 1, -1, OnUSBRead);
518     if (ret < 0) {
519         WRITE_LOG(LOG_FATAL, "uv_fs_read < 0");
520         return ERR_API_FAIL;
521     }
522     ctxRecv.atPollQueue = true;
523     return RET_SUCCESS;
524 }
525 
526 // Because USB can connect to only one host,daemonUSB is only one Session by default
WatchEPTimer(uv_timer_t * handle)527 void HdcDaemonUSB::WatchEPTimer(uv_timer_t *handle)
528 {
529     HdcDaemonUSB *thisClass = (HdcDaemonUSB *)handle->data;
530     HUSB hUSB = &thisClass->usbHandle;
531     HdcDaemon *daemon = reinterpret_cast<HdcDaemon *>(thisClass->clsMainBase);
532     if (thisClass->isAlive || thisClass->ctxRecv.atPollQueue) {
533         return;
534     }
535     bool resetEp = false;
536     do {
537         if (hUSB->bulkIn > 0) {
538             WRITE_LOG(LOG_DEBUG, "Watchdog close bulkin");
539             thisClass->CloseBulkEp(true, thisClass->usbHandle.bulkIn, &daemon->loopMain);
540             resetEp = true;
541         }
542         if (hUSB->bulkOut > 0) {
543             WRITE_LOG(LOG_DEBUG, "Watchdog close bulkout");
544             thisClass->CloseBulkEp(false, thisClass->usbHandle.bulkOut, &daemon->loopMain);
545             resetEp = true;
546         }
547         if (thisClass->controlEp > 0) {
548             Base::CloseFd(thisClass->controlEp);
549             resetEp = true;
550         }
551     } while (false);
552     if (resetEp || thisClass->usbHandle.bulkIn != 0 || thisClass->usbHandle.bulkOut != 0) {
553         return;
554     }
555     // until all bulkport reset
556     if (thisClass->ConnectEPPoint(hUSB) != RET_SUCCESS) {
557         return;
558     }
559     // connect OK
560     thisClass->isAlive = true;
561     thisClass->LoopUSBRead(hUSB, hUSB->wMaxPacketSizeSend);
562 }
563 }  // namespace Hdc
564