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