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