xref: /developtools/hdc/src/common/usb.cpp (revision cc290419)
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 "usb.h"
16
17namespace Hdc {
18HdcUSBBase::HdcUSBBase(const bool serverOrDaemonIn, void *ptrMainBase)
19{
20    serverOrDaemon = serverOrDaemonIn;
21    clsMainBase = ptrMainBase;
22    modRunning = true;
23}
24
25HdcUSBBase::~HdcUSBBase()
26{
27}
28
29void HdcUSBBase::ReadUSB(uv_stream_t *stream, ssize_t nread, const uv_buf_t *buf)
30{
31    StartTraceScope("HdcUSBBase::ReadUSB");
32    HSession hSession = (HSession)stream->data;
33    HdcSessionBase *hSessionBase = (HdcSessionBase *)hSession->classInstance;
34    if (hSessionBase->FetchIOBuf(hSession, hSession->ioBuf, nread) < 0) {
35        WRITE_LOG(LOG_FATAL, "ReadUSB FetchIOBuf error sessionId:%u", hSession->sessionId);
36        hSessionBase->FreeSession(hSession->sessionId);
37    }
38}
39
40bool HdcUSBBase::ReadyForWorkThread(HSession hSession)
41{
42    // Server-end USB IO is handed over to each sub-thread, only the daemon is still read by the main IO to distribute
43    // to each sub-thread by DataPipe.
44    if (uv_tcp_init(&hSession->childLoop, &hSession->dataPipe[STREAM_WORK]) ||
45        uv_tcp_open(&hSession->dataPipe[STREAM_WORK], hSession->dataFd[STREAM_WORK])) {
46        WRITE_LOG(LOG_FATAL, "USBBase ReadyForWorkThread init child TCP failed");
47        return false;
48    }
49    hSession->dataPipe[STREAM_WORK].data = hSession;
50    HdcSessionBase *pSession = (HdcSessionBase *)hSession->classInstance;
51#ifdef HDC_HOST
52    Base::SetTcpOptions(&hSession->dataPipe[STREAM_WORK], HOST_SOCKETPAIR_SIZE);
53#else
54    Base::SetTcpOptions(&hSession->dataPipe[STREAM_WORK]);
55#endif
56    if (uv_read_start((uv_stream_t *)&hSession->dataPipe[STREAM_WORK], pSession->AllocCallback, ReadUSB)) {
57        WRITE_LOG(LOG_FATAL, "USBBase ReadyForWorkThread child TCP read failed");
58        return false;
59    }
60    WRITE_LOG(LOG_DEBUG, "USBBase ReadyForWorkThread finish dataFd[STREAM_WORK]:%d",
61        hSession->dataFd[STREAM_WORK]);
62    return true;
63};
64
65vector<uint8_t> HdcUSBBase::BuildPacketHeader(uint32_t sessionId, uint8_t option, uint32_t dataSize)
66{
67    vector<uint8_t> vecData;
68    USBHead head;
69    head.sessionId = htonl(sessionId);
70    for (size_t i = 0; i < sizeof(head.flag); i++) {
71        head.flag[i] = USB_PACKET_FLAG.data()[i];
72    }
73    head.option = option;
74    head.dataSize = htonl(dataSize);
75    vecData.insert(vecData.end(), (uint8_t *)&head, (uint8_t *)&head + sizeof(USBHead));
76    return vecData;
77}
78
79// USB big data stream, block transmission, mainly to prevent accidental data packets from writing through EP port,
80// inserting the send queue causes the program to crash
81int HdcUSBBase::SendUSBBlock(HSession hSession, uint8_t *data, const int length)
82{
83    int childRet = 0;
84    int ret = ERR_IO_FAIL;
85    StartTraceScope("HdcUSBBase::SendUSBBlock");
86    std::lock_guard<std::mutex> lock(hSession->hUSB->lockSendUsbBlock);
87    auto header = BuildPacketHeader(hSession->sessionId, USB_OPTION_HEADER, length);
88    do {
89        if ((SendUSBRaw(hSession, header.data(), header.size())) <= 0) {
90            WRITE_LOG(LOG_FATAL, "SendUSBRaw index failed");
91            break;
92        }
93        if ((childRet = SendUSBRaw(hSession, data, length)) <= 0) {
94            WRITE_LOG(LOG_FATAL, "SendUSBRaw body failed");
95            break;
96        }
97        if (childRet > 0 && (childRet % hSession->hUSB->wMaxPacketSizeSend == 0)) {
98            // win32 send ZLP will block winusb driver and LIBUSB_TRANSFER_ADD_ZERO_PACKET not effect
99            // so, we send dummy packet to prevent zero packet generate
100            auto dummy = BuildPacketHeader(hSession->sessionId, 0, 0);
101            if ((SendUSBRaw(hSession, dummy.data(), dummy.size())) <= 0) {
102                WRITE_LOG(LOG_FATAL, "SendUSBRaw dummy failed");
103                break;
104            }
105        }
106        ret = length;
107    } while (false);
108    return ret;
109}
110
111bool HdcUSBBase::IsUsbPacketHeader(uint8_t *ioBuf, int ioBytes)
112{
113    StartTraceScope("HdcUSBBase::IsUsbPacketHeader");
114    USBHead *usbPayloadHeader = reinterpret_cast<struct USBHead *>(ioBuf);
115    uint32_t maybeSize = ntohl(usbPayloadHeader->dataSize);
116    bool isHeader = false;
117    do {
118        if (memcmp(usbPayloadHeader->flag, USB_PACKET_FLAG.c_str(), USB_PACKET_FLAG.size())) {
119            break;
120        }
121        if (ioBytes != sizeof(USBHead)) {
122            break;
123        }
124        if (maybeSize == 0) {
125            isHeader = true;  // nop packet
126            break;
127        } else {  // maybeSize != 0
128            if (usbPayloadHeader->option & USB_OPTION_HEADER) {
129                isHeader = true;
130                break;
131            }
132        }
133    } while (false);
134    return isHeader;
135}
136
137void HdcUSBBase::PreSendUsbSoftReset(HSession hSession, uint32_t sessionIdOld)
138{
139    StartTraceScope("HdcUSBBase::PreSendUsbSoftReset");
140    HUSB hUSB = hSession->hUSB;
141    if (hSession->serverOrDaemon && !hUSB->resetIO) {
142        hUSB->lockSendUsbBlock.lock();
143        WRITE_LOG(LOG_WARN, "SendToHdcStream check, sessionId not matched");
144        auto header = BuildPacketHeader(sessionIdOld, USB_OPTION_RESET, 0);
145        if (SendUSBRaw(hSession, header.data(), header.size()) <= 0) {
146            WRITE_LOG(LOG_FATAL, "PreSendUsbSoftReset send failed");
147        }
148        hUSB->lockSendUsbBlock.unlock();
149        hUSB->resetIO = true;
150    }
151}
152
153int HdcUSBBase::CheckPacketOption(HSession hSession, uint8_t *appendData, int dataSize)
154{
155    HUSB hUSB = hSession->hUSB;
156    // special short packet
157    USBHead *header = reinterpret_cast<USBHead *>(appendData);
158    header->sessionId = ntohl(header->sessionId);
159    header->dataSize = ntohl(header->dataSize);
160    if (header->sessionId != hSession->sessionId) {
161        // Only server do it here, daemon 'SendUsbSoftReset' no use
162        // hilog + ctrl^C to reproduction scene
163        //
164        // Because the USB-reset API does not work on all platforms, the last session IO data may be
165        // recveived, we need to ignore it.
166        WRITE_LOG(LOG_WARN, "CheckPacketOption softreset header->sessionId:%u sessionId:%u",
167            header->sessionId, hSession->sessionId);
168        PreSendUsbSoftReset(hSession, header->sessionId);
169        return 0;
170    }
171    if (header->option & USB_OPTION_HEADER) {
172        // header packet
173        hUSB->payloadSize = header->dataSize;
174    }
175    // soft ZLP
176    return hUSB->payloadSize;
177}
178
179// return value: <0 error; = 0 all finish; >0 need size
180int HdcUSBBase::SendToHdcStream(HSession hSession, uv_stream_t *stream, uint8_t *appendData, int dataSize)
181{
182    StartTraceScope("HdcUSBBase::SendToHdcStream");
183    int childRet = 0;
184    HUSB hUSB = hSession->hUSB;
185    if (IsUsbPacketHeader(appendData, dataSize)) {
186        return CheckPacketOption(hSession, appendData, dataSize);
187    }
188    if (hUSB->payloadSize <= static_cast<uint32_t>(childRet)) {
189        // last session data
190        WRITE_LOG(LOG_WARN, "SendToHdcStream softreset dataSize:%d payloadSize:%u childRet:%d",
191            dataSize, hUSB->payloadSize, childRet);
192        PreSendUsbSoftReset(hSession, 0);  // 0 == reset current
193        return 0;
194    }
195    if ((childRet = UsbToHdcProtocol(stream, appendData, dataSize)) < 0) {
196        WRITE_LOG(LOG_FATAL, "Error usb send to stream dataSize:%d", dataSize);
197        return ERR_IO_FAIL;
198    }
199    hUSB->payloadSize -= childRet;
200    return hUSB->payloadSize;
201}
202
203}
204