1 /*
2 * Copyright (c) 2025 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
16 #include "scsi_linux_adapter.h"
17
18 #include <cerrno>
19 #include <cstdint>
20 #include <fcntl.h>
21 #include <hdf_base.h>
22 #include <hdf_log.h>
23 #include <cstdlib>
24 #include <cstring>
25 #include <scsi/sg.h>
26 #include <securec.h>
27 #include <sys/ioctl.h>
28 #include <termios.h>
29 #include <unistd.h>
30 #include <sstream>
31 #include <iomanip>
32
33 #include "usbd_wrapper.h"
34
35 #define HDF_LOG_TAG scsi_linux_adapter
36
37 namespace OHOS {
38 namespace HDI {
39 namespace Usb {
40 namespace ScsiDdk {
41 namespace V1_0 {
42
43 constexpr uint8_t SCSIPERIPHERAL_MAX_SENSE_DATA_LEN = 252;
44 constexpr uint8_t TWO_BYTE = 2;
45 constexpr uint8_t OPERATION_CODE_REQUEST_SENSE = 0x03;
46
BytesToHex(const uint8_t * data,size_t length)47 static std::string BytesToHex(const uint8_t* data, size_t length)
48 {
49 std::ostringstream oss;
50 oss << std::hex << std::setfill('0');
51 for (size_t i = 0; i < length; ++i) {
52 oss << std::setw(TWO_BYTE) << static_cast<int>(data[i]) << ' ';
53 }
54 return oss.str();
55 }
56
DxferDirectionToString(int direction)57 static std::string DxferDirectionToString(int direction)
58 {
59 switch (direction) {
60 case SG_DXFER_NONE: return "SG_DXFER_NONE";
61 case SG_DXFER_TO_DEV: return "SG_DXFER_TO_DEV";
62 case SG_DXFER_FROM_DEV: return "SG_DXFER_FROM_DEV";
63 case SG_DXFER_TO_FROM_DEV: return "SG_DXFER_TO_FROM_DEV";
64 default: return "UNKNOWN (" + std::to_string(direction) + ")";
65 }
66 }
ToString(const struct sg_io_hdr & ioHdr)67 static std::string ToString(const struct sg_io_hdr& ioHdr)
68 {
69 std::ostringstream buffer;
70 buffer << "sg_io_hdr{\n" << " interface_id='" << ioHdr.interface_id << "' (" <<
71 static_cast<int>(ioHdr.interface_id) << ")\n" << " cmdp=" << static_cast<void*>(ioHdr.cmdp) << " (";
72 if (ioHdr.cmdp && ioHdr.cmd_len > 0) {
73 buffer << "CMD: " << BytesToHex(reinterpret_cast<const uint8_t*>(ioHdr.cmdp), ioHdr.cmd_len);
74 } else {
75 buffer << "NULL or empty CMD";
76 }
77 buffer << ")\n" << " cmd_len=" << static_cast<int>(ioHdr.cmd_len) << '\n' << " dxfer_direction=" <<
78 DxferDirectionToString(ioHdr.dxfer_direction) << " (" << std::dec << ioHdr.dxfer_direction << ")\n" <<
79 " dxferp=" << static_cast<void*>(ioHdr.dxferp) << '\n' << " dxfer_len=" << ioHdr.dxfer_len << '\n' <<
80 " sbp=" << static_cast<void*>(ioHdr.sbp) << '\n' << " mx_sb_len=" << static_cast<int>(ioHdr.mx_sb_len) <<
81 '\n' << " timeout=" << ioHdr.timeout << "\n" << "}";
82
83 return buffer.str();
84 }
85
UpdateResponse(const struct sg_io_hdr & ioHdr,Response & response)86 static void UpdateResponse(const struct sg_io_hdr& ioHdr, Response& response)
87 {
88 response.status = ioHdr.status;
89 response.maskedStatus = ioHdr.masked_status;
90 response.msgStatus = ioHdr.msg_status;
91 response.sbLenWr = ioHdr.sb_len_wr;
92 response.hostStatus = ioHdr.host_status;
93 response.driverStatus = ioHdr.driver_status;
94 response.resId = ioHdr.resid;
95 response.duration = ioHdr.duration;
96 response.transferredLength = ioHdr.dxfer_len - ioHdr.resid;
97 }
98
SendRequest(const Request & request,uint8_t * buffer,uint32_t bufferSize,Response & response)99 int32_t LinuxScsiOsAdapter::SendRequest(const Request& request, uint8_t *buffer, uint32_t bufferSize,
100 Response& response)
101 {
102 uint8_t cmdLen = request.commandDescriptorBlock.size();
103 if (cmdLen == 0) {
104 HDF_LOGE("SendRequest: Command Descriptor Block is empty");
105 return SCSIPERIPHERAL_DDK_INVALID_PARAMETER;
106 }
107
108 std::vector<uint8_t> cmd(request.commandDescriptorBlock.data(), request.commandDescriptorBlock.data() + cmdLen);
109
110 struct sg_io_hdr ioHdr;
111 int32_t result = memset_s(&ioHdr, sizeof(ioHdr), 0, sizeof(ioHdr));
112 if (result != 0) {
113 HDF_LOGE("%{public}s memset_s(ioHdr) failed, result=%{public}d", __func__, result);
114 return SCSIPERIPHERAL_DDK_MEMORY_ERROR;
115 }
116 ioHdr.interface_id = 'S';
117 ioHdr.cmdp = cmd.data();
118 ioHdr.cmd_len = cmdLen;
119 ioHdr.dxfer_direction = request.dataTransferDirection;
120
121 if ((buffer != nullptr) && (bufferSize != 0)) {
122 ioHdr.dxferp = buffer;
123 ioHdr.dxfer_len = bufferSize;
124 }
125
126 if (cmd[0] == OPERATION_CODE_REQUEST_SENSE) {
127 ioHdr.sbp = nullptr;
128 ioHdr.mx_sb_len = 0;
129 } else {
130 response.senseData.resize(SCSIPERIPHERAL_MAX_SENSE_DATA_LEN);
131 ioHdr.sbp = response.senseData.data();
132 ioHdr.mx_sb_len = response.senseData.size();
133 }
134 ioHdr.timeout = request.timeout;
135 HDF_LOGD("SendRequest, request.devFd=%{public}d, \n%{public}s", request.devFd, ToString(ioHdr).c_str());
136
137 if (ioctl(request.devFd, SG_IO, &ioHdr) < 0) {
138 if (errno == ETIME) {
139 HDF_LOGE("%{public}s: ioctl timeout", __func__);
140 return SCSIPERIPHERAL_DDK_TIMEOUT;
141 } else {
142 HDF_LOGE("%{public}s: ioctl failed, errno=%{public}d (%{public}s)", __func__, errno, strerror(errno));
143 return SCSIPERIPHERAL_DDK_IO_ERROR;
144 }
145 }
146
147 UpdateResponse(ioHdr, response);
148 HDF_LOGD("response.status=%{public}x, transferredLength=%{public}d", response.status, response.transferredLength);
149 HDF_LOGD("SendRequest, response.senseData=[ %{public}s ]",
150 BytesToHex(reinterpret_cast<const uint8_t*>(response.senseData.data()), response.senseData.size()).c_str());
151
152 return HDF_SUCCESS;
153 }
154 } // namespace V1_0
155 } // namespace ScsiDdk
156 } // namespace Usb
157 } // namespace HDI
158 } // namespace OHOS
159