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 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