• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1# SCSI Peripheral DDK Development
2
3## Overview
4
5Small Computer System Interface (SCSI) devices, such as disk arrays, tape libraries, and specific types of storage servers, are widely used in enterprise-level storage solutions and industrial application scenarios. If the operating system does not have an adaptation driver for these devices, the devices cannot be identified or used after being connected. SCSI Peripheral Driver Development Kit (DDK) is a suite provided for SCSI device driver development. It allows you to develop SCSI device drivers at the application layer based on the user mode.
6
7The SCSI Peripheral DDK supports seven common commands (including INQUIRY, READ CAPACITY, TEST UNIT READY, REQUEST SENSE, READ, WRITE, and VERIFY) in the three command sets: SCSI Primary Commands (SPC), SCSI Block Commands (SBC), and Multimedia Commands (MMC). You can use these commands at your preference.
8
9### Basic Concepts
10
11Before developing the SCSI Peripheral DDK, you must understand the following basic concepts:
12
13- **SCSI**
14
15    SCSI is a standard protocol set used for communication between computers and peripherals such as hard disk drives, tape drives, optical disk drives, and scanners.
16
17- **AMS**
18
19    The Ability Manager Service (AMS) is a system service used to coordinate the running relationships of abilities and schedule the lifecycle of abilities.
20
21- **BMS**
22
23    The Bundle Manager Service (BMS) is responsible for application installation, uninstallation, and data management on OpenHarmony.
24
25- **DDK**
26
27    The Driver Development Kit (DDK) is a tool package provided by OpenHarmony for developing drivers for non-standard SCSI peripherals based on the peripheral framework.
28
29- **Non-standard peripherals**
30
31    Non-standard peripherals (also called custom peripherals or dedicated peripherals) are peripherals that do not comply with general standards or are customized for specific application scenarios. This type of device usually requires special software support or special interfaces to implement communication with the host system.
32
33- **Standard peripherals**
34
35    Standard peripherals refer to peripherals (such as USB keyboards and mouse devices) that comply with industry standards and specifications. Such devices typically have uniform interface protocols, physical dimensions, and electrical characteristics, so that they can be used interchangeably between different systems.
36
37- **Logical block**
38
39    A logical block is a basic data storage unit. It represents a data area of a fixed size on a device and is usually used for data read and write operations. The size of a logical block may be 512 bytes, 1024 bytes, 2048 bytes, and so on. A specific size depends on a configuration of the device and a design of the file system.
40
41- **CDB**
42
43    A command descriptor block (CDB) is a standard data structure used to send commands in the SCSI protocol. A CDB is a byte array with a fixed length. It contains the operation code (Opcode) and related parameters of the SCSI command and is used to instruct the device to perform operations (such as read, write, and query).
44
45### Implementation Principles
46
47A non-standard peripheral application obtains the SCSI device ID by using the peripheral management service, and delivers the ID and the action to the SCSI driver application through RPC. The SCSI driver application can obtain the basic information about the SCSI device and read and write data by invoking the SCSI Peripheral DDK API. Then, the DDK API uses the HDI service to deliver instructions to the kernel driver, and the kernel driver uses instructions to communicate with the device.
48
49**Figure 1** Principle of invoking the SCSI Peripheral DDK
50
51![SCSI_Peripheral_DDK schematic diagram](figures/ddk-schematic-diagram.png)
52
53### Constraints
54
55- The open APIs of SCSI Peripheral DDK support the development of standard SCSI peripheral drivers.
56
57- The open APIs of SCSI Peripheral DDK can be used only within the lifecycle of **DriverExtensionAbility**.
58
59- To use the open APIs of SCSI Peripheral DDK, you need to declare the corresponding ACL permission **ohos.permission.ACCESS_DDK_SCSI_PERIPHERAL** in **module.json5**.
60
61## Environment Setup
62
63Before you get started, make necessary preparations by following instructions in [Environment Preparation](environmental-preparation.md).
64
65## How to Develop
66
67### Available APIs
68
69| **Name**| Description|
70| -------- | -------- |
71| int32_t OH_ScsiPeripheral_Init(void) | Initializes the SCSI Peripheral DDK.|
72| int32_t OH_ScsiPeripheral_Release(void) | Releases the SCSI Peripheral DDK.|
73| int32_t OH_ScsiPeripheral_Open(uint64_t deviceId, uint8_t interfaceIndex, ScsiPeripheral_Device **dev) | Opens the SCSI device specified by **deviceId** and **interfaceIndex**.|
74| int32_t OH_ScsiPeripheral_Close(ScsiPeripheral_Device **dev) | Disables the SCSI device.|
75| int32_t OH_ScsiPeripheral_TestUnitReady(ScsiPeripheral_Device *dev, ScsiPeripheral_TestUnitReadyRequest *request, ScsiPeripheral_Response *response) | Checks whether the logical units are ready.|
76| int32_t OH_ScsiPeripheral_Inquiry(ScsiPeripheral_Device *dev, ScsiPeripheral_InquiryRequest *request, ScsiPeripheral_InquiryInfo *inquiryInfo, ScsiPeripheral_Response *response) | Queries basic information about the SCSI device.|
77| int32_t OH_ScsiPeripheral_ReadCapacity10(ScsiPeripheral_Device *dev, ScsiPeripheral_ReadCapacityRequest *request, ScsiPeripheral_CapacityInfo *capacityInfo, ScsiPeripheral_Response *response) | Obtains the capacity information about the SCSI device.|
78| int32_t OH_ScsiPeripheral_RequestSense(ScsiPeripheral_Device *dev, ScsiPeripheral_RequestSenseRequest *request, ScsiPeripheral_Response *response) | Obtains sense data, that is, information returned by the SCSI device to the host, which is used to report the device status, error information, and diagnosis information.|
79| int32_t OH_ScsiPeripheral_Read10(ScsiPeripheral_Device *dev, ScsiPeripheral_IORequest *request, ScsiPeripheral_Response *response) | Reads data from a specified logical block.|
80| int32_t OH_ScsiPeripheral_Write10(ScsiPeripheral_Device *dev, ScsiPeripheral_IORequest *request, ScsiPeripheral_Response *response) | Writes data to a specified logical block of a device.|
81| int32_t OH_ScsiPeripheral_Verify10(ScsiPeripheral_Device *dev, ScsiPeripheral_VerifyRequest *request, ScsiPeripheral_Response *response) | Verifies a specified logical block.|
82| int32_t OH_ScsiPeripheral_SendRequestByCdb(ScsiPeripheral_Device *dev, ScsiPeripheral_Request *request, ScsiPeripheral_Response *response) | Sends the SCSI command in CDB mode.|
83| int32_t OH_ScsiPeripheral_CreateDeviceMemMap(ScsiPeripheral_Device *dev, size_t size, ScsiPeripheral_DeviceMemMap **devMmap) | Creates a buffer.|
84| int32_t OH_ScsiPeripheral_DestroyDeviceMemMap(ScsiPeripheral_DeviceMemMap *devMmap) | Destroys a buffer.|
85| int32_t OH_ScsiPeripheral_ParseBasicSenseInfo(uint8_t *senseData, uint8_t senseDataLen, ScsiPeripheral_BasicSenseInfo *senseInfo) | Parses basic sense data, including the **Information**, **Command specific information**, and **Sense key specific** fields.|
86
87For details about the interface, see [SCSI Peripheral DDK](../../reference/apis-driverdevelopment-kit/_s_c_s_i.md).
88
89### How to Develop
90
91The following describes how to use the SCSI Peripheral DDK to develop non-standard SCSI peripheral drivers.
92
93**Adding Dynamic Link Libraries**
94
95Add the following libraries to **CMakeLists.txt**.
96```txt
97libscsi.z.so
98```
99
100**Including Header Files**
101```c++
102#include <scsi_peripheral/scsi_peripheral_api.h>
103#include <scsi_peripheral/scsi_peripheral_types.h>
104```
105
1061. Initialize the SCSI Peripheral DDK.
107
108    Use **OH_ScsiPeripheral_Init** in **scsi_peripheral_api.h** to initialize the SCSI Peripheral DDK.
109
110    ```c++
111    // Initialize the SCSI Peripheral DDK.
112    int32_t ret = OH_ScsiPeripheral_Init();
113    ```
114
1152. Open the device.
116
117    After the SCSI Peripheral DDK is initialized, use **OH_ScsiPeripheral_Open** in **scsi_peripheral_api.h** to open the SCSI device.
118
119    ```c++
120    uint64_t deviceId = 0x100000003;
121    uint8_t interfaceIndex = 0;
122    ScsiPeripheral_Device *dev = NULL;
123    // Open the SCSI device specified by deviceId and interfaceIndex1.
124    ret = OH_ScsiPeripheral_Open(deviceId, interfaceIndex, &dev);
125    ```
126
1273. Create a buffer.
128
129    Use **OH_ScsiPeripheral_CreateDeviceMemMap** in **scsi_peripheral_api.h** to create the memory buffer **devMmap**.
130
131    ```c++
132    constexpr size_t DEVICE_MEM_MAP_SIZE = 1024;
133    ScsiPeripheral_DeviceMemMap *g_scsiDeviceMemMap = nullptr;
134    ret = OH_ScsiPeripheral_CreateDeviceMemMap(dev, DEVICE_MEM_MAP_SIZE, &g_scsiDeviceMemMap);
135    ```
136
1374. Check whether the logical units are ready.
138
139    Use **OH_ScsiPeripheral_TestUnitReady** in **scsi_peripheral_api.h** to check whether the logical unit is ready.
140
141    ```c++
142    ScsiPeripheral_TestUnitReadyRequest testUnitReadyRequest = {0};
143    testUnitReadyRequest.timeout = 5000;
144    ScsiPeripheral_Response testUnitReadyResponse = {0};
145    ret = OH_ScsiPeripheral_TestUnitReady(dev, &testUnitReadyRequest, &testUnitReadyResponse);
146    ```
147
1485. Query basic information about the SCSI device.
149
150    Use **OH_ScsiPeripheral_Inquiry** in **scsi_peripheral_api.h** to obtain the basic information about the SCSI device.
151
152    ```c++
153    ScsiPeripheral_InquiryRequest inquiryRequest = {0};
154    inquiryRequest.allocationLength = 512;
155    inquiryRequest.timeout = 5000;
156    ScsiPeripheral_InquiryInfo inquiryInfo = {0};
157    inquiryInfo.data = g_scsiDeviceMemMap;
158    ScsiPeripheral_Response inquiryResponse = {0};
159    ret = OH_ScsiPeripheral_Inquiry(dev, &inquiryRequest, &inquiryInfo, &inquiryResponse);
160    ```
161
1626. Obtain the capacity information about the SCSI device.
163
164    Use **OH_ScsiPeripheral_ReadCapacity10** in **scsi_peripheral_api.h** to obtain the SCSI device capacity information.
165
166    ```c++
167    ScsiPeripheral_ReadCapacityRequest readCapacityRequest = {0};
168    readCapacityRequest.lbAddress = 0;
169    readCapacityRequest.control = 0;
170    readCapacityRequest.byte8 = 0;
171    readCapacityRequest.timeout = 5000;
172    ScsiPeripheral_CapacityInfo capacityInfo = {0};
173    ScsiPeripheral_Response readCapacityResponse = {0};
174    ret = OH_ScsiPeripheral_ReadCapacity10(dev, &readCapacityRequest, &capacityInfo, &readCapacityResponse);
175    ```
176
1777. Obtain sense data.
178
179    Use **OH_ScsiPeripheral_RequestSense** in **scsi_peripheral_api.h** to obtain sense data.
180
181    ```c++
182    ScsiPeripheral_RequestSenseRequest senseRequest = {0};
183    senseRequest.allocationLength = SCSIPERIPHERAL_MAX_SENSE_DATA_LEN + 1;
184    senseRequest.control = 0;
185    senseRequest.byte1 = 0;
186    senseRequest.timeout = 5000;
187    ScsiPeripheral_Response senseResponse = {0};
188    // Information returned by the SCSI device to the host, used to report the device status, error information, and diagnosis information.
189    ret = OH_ScsiPeripheral_RequestSense(dev, &senseRequest, &senseResponse);
190    ```
191
1928. Parse the sense data.
193
194    Use **OH_ScsiPeripheral_ParseBasicSenseInfo** in **scsi_peripheral_api.h** to parse basic sense data, including the **Information**, **Command specific information**, and **Sense key specific** fields.
195
196    ```c++
197    ScsiPeripheral_BasicSenseInfo senseInfo = {0};
198    ret = OH_ScsiPeripheral_ParseBasicSenseInfo(senseResponse.senseData, SCSIPERIPHERAL_MAX_SENSE_DATA_LEN, &senseInfo);
199    ```
200
2019. Read data.
202
203    Use **OH_ScsiPeripheral_Read10** in **scsi_peripheral_api.h** to read data from a specified logical block.
204
205    ```c++
206    ScsiPeripheral_IORequest readRequest = {0};
207    readRequest.lbAddress = 1;
208    readRequest.transferLength = 1;
209    readRequest.control = 0;
210    readRequest.byte1 = 0;
211    readRequest.byte6 = 0;
212    readRequest.timeout = 20000;
213    readRequest.data = g_scsiDeviceMemMap;
214    ScsiPeripheral_Response readResponse = {0};
215    ret = OH_ScsiPeripheral_Read10(dev, &readRequest, &readResponse);
216    ```
217
21810. Write data.
219
220    Use **OH_ScsiPeripheral_Write10** in **scsi_peripheral_api.h** to write data to a specified logical block of the device.
221
222    ```c++
223    ScsiPeripheral_IORequest writeRequest = {0};
224    writeRequest.lbAddress = 1;
225    writeRequest.transferLength = 1;
226    writeRequest.control = 0;
227    writeRequest.byte1 = 0;
228    writeRequest.byte6 = 0;
229    writeRequest.timeout = 5000;
230    writeRequest.data = g_scsiDeviceMemMap;
231    ScsiPeripheral_Response writeResponse = {0};
232    ret = OH_ScsiPeripheral_Write10(dev, &writeRequest, &writeResponse);
233    ```
234
23511. Verifies a specified logical block.
236
237    Use **OH_ScsiPeripheral_Verify10** in **scsi_peripheral_api.h** to verify a specified logical block.
238
239    ```c++
240    ScsiPeripheral_VerifyRequest verifyRequest = {0};
241    verifyRequest.lbAddress = 1;
242    verifyRequest.verificationLength = 1;
243    verifyRequest.timeout = 5000;
244    ScsiPeripheral_Response verifyResponse = {0};
245    ret = OH_ScsiPeripheral_Verify10(dev, &verifyRequest, &verifyResponse);
246    ```
247
24812. Send SCSI commands in CDB mode.
249
250    Use **OH_SCSIPeripheral_SendRequestByCdb** in **scsi_peripheral_api.h** to send SCSI commands.
251
252    ```c++
253    ScsiPeripheral_Request sendRequest = {0};
254    uint8_t cdbData[SCSIPERIPHERAL_MAX_CMD_DESC_BLOCK_LEN] = {0x28, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; // The cstring header file needs to be imported.
255    memcpy(sendRequest.commandDescriptorBlock, cdbData, SCSIPERIPHERAL_MAX_CMD_DESC_BLOCK_LEN);
256    sendRequest.cdbLength = 10;
257    sendRequest.dataTransferDirection = -3;
258    sendRequest.timeout = 5000;
259    sendRequest.data = g_scsiDeviceMemMap;
260    ScsiPeripheral_Response sendResponse = {0};
261    ret = OH_ScsiPeripheral_SendRequestByCdb(dev, &sendRequest, &sendResponse);
262    ```
263
26413. Destroy the buffer.
265
266    After all requests are processed and before the program exits, use **OH_ScsiPeripheral_DestroyDeviceMemMap** in **scsi_peripheral_api.h** to destroy the buffer.
267
268    ```c++
269    ret = OH_ScsiPeripheral_DestroyDeviceMemMap(g_scsiDeviceMemMap);
270    ```
271
27214. Close the HID device.
273
274    After the buffer is destroyed, use **OH_ScsiPeripheral_Close** in **scsi_peripheral_api.h** to close the device.
275
276    ```c++
277    // Stop the SCSI device.
278    ret = OH_ScsiPeripheral_Close(&dev);
279    ```
280
28115. Release the SCSI Peripheral DDK.
282
283    After the SCSI device is closed, use **OH_ScsiPeripheral_Release** in **scsi_peripheral_api.h** to release the SCSI Peripheral DDK.
284
285    ```c++
286    // Release the SCSI Peripheral DDK.
287    ret = OH_ScsiPeripheral_Release();
288    ```
289
290### Debugging and Verification
291
292Upon completion of driver application development, you can install the application on the OpenHarmony device. The test procedure is as follows:
293
2941. Click the driver application on the device. The application is started on the device.
2952. Check whether the application can read the basic information about the SCSI device.
2963. Select a SCSI command, enter parameters, and click the **Send** button.
2974. (Optional) Set the direction, CDB data, and CDB length, and click the **Send** button to run the corresponding SCSI command.
298