USB host development aims to provide host-related functions, including protocol encapsulation, device management, and driver installation and uninstall.
The universal serial bus (USB) consists of a USB host and multiple USB devices. The USB host implement data transfer and port management in the USB bus, and the USB device can connect to various peripherals. Therefore, USB driver development is divided into USB host driver development and USB device driver development.
USB device development aims to provide device-related functions, including device management, configuration management, and I/O management. These functions implement creation, configuration, and data communication of USB devices.
The USB module of OpenHarmony supports the development of USB services, provides USB-related functions, provides interfaces to read and write USB device data of third-party function drivers in user mode, creates and deletes USB devices, obtains notification events, enables or disables event listening, implements non-isochronous and isochronous data transfer over USB pipes, and sets custom USB attributes.
The following figures show the USB host and device driver models.
The USB DriverDevelop Kit (DDK) is the USB driver development kit provided by the Framework of the OpenHarmony Driver Foundation (HDF). This kit consists of the USB Host DDK and USB Device DDK. It supports the development of USB device drivers based on the user mode and provides rich USB driver development capabilities that help you to efficiently develop USB drivers.
A pipe is a model for data transfer between the USB host and a device endpoint. Once a USB device is powered on, a pipe, that is, the default control pipe, is established. The USB host obtains the description, configuration, and status of the USB device through the pipe, and configures the device as requested. Pipes and endpoints are associated and share the same attributes, such as the supported transfer type, maximum packet length, and data transfer direction.
The minimum unit that transfers and receives data in a USB device. It supports unidirectional or bidirectional data transfer. One USB device may include several endpoints, and different endpoints are distinguished by endpoint numbers and directions. Different endpoints can support different data transfer types, access intervals, and maximum packet sizes. All endpoints except endpoint 0 support data transfer in only one direction. Endpoint 0 is a special endpoint that supports bidirectional control transfer.
The USB driver model offers the following APIs:
- Interface
- The USB host Driver Development Kit (DDK) provides driver capability APIs that can be directly called in user mode. The APIs can be classified into the DDK initialization class, interface operation class, and request operation class by function. These APIs can be used to perform DDK initialization, bind/release and open/close an interface, allocate/release a request, and implement synchronous or asynchronous transfer.
The application implements device control and data transfer through exchanging data with the device. Because a pipe supports only one data transfer type, multiple pipes are usually required to complete data exchange in this process. A collection of pipes that are used together to control a device is called an interface.
-The USB device DDK provides device management, I/O management, and configuration management APIs, which can be used to create or delete a device, obtain or open an interface, and perform synchronous or asynchronous transfer.
-Descriptor
A data structure used to describe device attributes. The first byte indicates the descriptor size (number of bytes), and the second byte indicates the descriptor type.
### Available APIs
### Working Principles
The tables below describe the APIs provided by the USB host driver model.
#### USB Host DDK
**Table 1** usb_ddk_interface.h
The USB Host DDK provides the capability of developing USB drivers on the host. Based on functions, APIs of the USB Host DDK are classified into three types: DDK initialization, **interface** object operation, and **request** object operation.
| API| Description|
**Figure 1** USB host driver model
| -------- | -------- |
| int32_t UsbInitHostSdk(struct UsbSession \*\*session); | Initializes the USB host driver DDK.|
| int32_t UsbExitHostSdk(const struct UsbSession<br>\*session); | Exits the USB host driver DDK.|
| const struct UsbInterface \*UsbClaimInterface(const<br>struct UsbSession \*session, uint8_t busNum, uint8_t<br>usbAddr, uint8_t interfaceIndex); | Obtains a USB interface.|
| int UsbReleaseInterface(const struct UsbInterface<br>\*interfaceObj); | Releases a USB interface.|
| int UsbAddOrRemoveInterface(const struct UsbSession<br>\*session, uint8_t busNum, uint8_t usbAddr, uint8_t<br>interfaceIndex, UsbInterfaceStatus status); | Adds or removes a USB interface.|
| UsbInterfaceHandle \*UsbOpenInterface(const struct<br>UsbInterface \*interfaceObj); | Opens a USB interface.|
| int32_t UsbCloseInterface(const UsbInterfaceHandle<br>\*interfaceHandle); | Closes a USB interface.|
| int32_t UsbSelectInterfaceSetting(const<br>UsbInterfaceHandle \*interfaceHandle, uint8_t<br>settingIndex, struct UsbInterface \*\*interfaceObj); | Sets a USB interface.|
| int32_t UsbGetPipeInfo(const UsbInterfaceHandle<br>\*interfaceHandle, uint8_t settingIndex, uint8_t pipeId,<br>struct UsbPipeInfo \*pipeInfo); | Obtains USB pipe information.|
| int32_t UsbClearInterfaceHalt(const<br>UsbInterfaceHandle \*interfaceHandle, uint8_t<br>pipeAddress); | Clears the state of the pipe with the specified index.|
| struct UsbRequest \*UsbAllocRequest(const<br>UsbInterfaceHandle \*interfaceHandle, int isoPackets<br>, int length); | Allocates a request object.|
| int UsbFreeRequest(const struct UsbRequest<br>\*request); | Releases a request object.|
| int UsbSubmitRequestAsync(const struct UsbRequest<br>\*request); | Sends an asynchronous request.|
| int32_t UsbFillRequest(const struct UsbRequest<br>\*request, const UsbInterfaceHandle \*interfaceHandle,<br>const struct UsbRequestParams \*params); | Fills in a request.|
| sint UsbCancelRequest(const struct UsbRequest<br>\*request); | Cancels an asynchronous request.|
| int UsbSubmitRequestSync(const struct UsbRequest<br>\*request); | Sends a synchronous request.|
- The USB Interface Pool module manages USB interfaces. It applies for and reclaims USB interface objects, which are used to record device port information and resources. The module manages USB interfaces by USB port. In addition, it provides USB DDK APIs to read and write USB data.
| -------- | -------- |
| int UsbRawInit(struct UsbSession \*\*session); | Initializes the USB raw APIs.|
| int UsbRawExit(const struct UsbSession \*session); | Exits the USB raw APIs.|
| UsbRawHandle \*UsbRawOpenDevice(const struct<br>UsbSession \*session, uint8_t busNum, uint8_t<br>usbAddr); | Opens a USB device.|
| int UsbRawCloseDevice(const UsbRawHandle<br>\*devHandle); | Closes a USB device.|
| int UsbRawSendControlRequest(const struct<br>UsbRawRequest \*request, const UsbRawHandle<br>\*devHandle, const struct UsbControlRequestData<br>\*requestData); | Performs a control transfer synchronously.|
| int UsbRawSendBulkRequest(const struct<br>UsbRawRequest \*request, const UsbRawHandle<br>\*devHandle, const struct UsbRequestData<br>\*requestData); | Performs a bulk transfer synchronously.|
| int UsbRawSendInterruptRequest(const struct<br>UsbRawRequest \*request, const UsbRawHandle<br>\*devHandle, const struct UsbRequestData<br>\*requestData); | Performs an interrupt transfer synchronously.|
| int UsbRawGetConfigDescriptor(const UsbRawDevice<br>\*rawDev, uint8_t configIndex, struct<br>UsbRawConfigDescriptor \*\*config); | Obtains the configuration descriptor of a device.|
| void UsbRawFreeConfigDescriptor(const struct<br>UsbRawConfigDescriptor \*config); | Releases the memory space of a configuration descriptor.|
| int UsbRawGetConfiguration(const UsbRawHandle<br>\*devHandle, int \*config); | Obtains the configuration in use.|
| int UsbRawSetConfiguration(const UsbRawHandle<br>\*devHandle, int config); | Sets the configuration in use.|
| UsbRawDevice \*UsbRawGetDevice(const UsbRawHandle<br>\*devHandle); | Obtains the device pointer based on the device handle.|
| int UsbRawGetDeviceDescriptor(const UsbRawDevice<br>\*rawDev, struct<br>UsbDeviceDescriptor \*desc); | Obtains the device descriptor of the specified USB device.|
| int UsbRawClaimInterface(const UsbRawHandle<br>\*devHandle, int<br>interfaceNumber); | Declares the interface on the specified device handle.|
| int UsbRawReleaseInterface(const UsbRawHandle<br>\*devHandle, in<br>t interfaceNumber); | Releases the previously declared interface.|
| int UsbRawResetDevice(const UsbRawHandle<br>\*devHandle); | Resets a device.|
| struct UsbRawRequest \*UsbRawAllocRequest(const<br>UsbRawHandle<br>\*devHandle, int isoPackets, int length); | Allocates a transfer request with the specified number of sync packet descriptors.|
| int UsbRawFreeRequest(const struct UsbRawRequest<br>\*request); | Releases the previously allocated transfer request.|
| int UsbRawFillBulkRequest(const struct UsbRawRequest<br>\*request, const UsbRawHandle \*devHandle, const struct<br>UsbRawFillRequestData \*fillData); | Fills in a bulk transfer request.|
| int UsbRawFillControlSetup(const unsigned char \*setup,<br>const struct UsbControlRequestData \*requestData); | Fills in a control setup packet.|
| int UsbRawFillControlRequest(const struct UsbRawRequest<br>\*request, const UsbRawHandle \*devHandle, const struct<br>UsbRawFillRequestData \*fillData); | Fills in a control transfer request.|
| int UsbRawFillInterruptRequest(const struct UsbRawRequest<br>\*request, const UsbRawHandle \*devHandle, const struct<br>UsbRawFillRequestData \*fillData); | Fills in an interrupt transfer request.|
| int UsbRawFillIsoRequest(const struct UsbRawRequest<br>\*request, const UsbRawHandle \*devHandle, const struct<br>UsbRawFillRequestData \*fillData); | Fills in an isochronous transfer request.|
| int UsbRawSubmitRequest(const struct UsbRawRequest<br>\*request); | Submits a transfer request.|
| int UsbRawCancelRequest(const struct UsbRawRequest<br>\*request); | Cancels a transfer request.|
| int UsbRawHandleRequests(const UsbRawHandle<br>\*devHandle); | Handles a transfer request event.|
The tables below describe the APIs provided by the USB device driver model.
**Table 3** usbfn_device.h
| API| Description|
- The USB Protocol Layer module provides USB protocol encapsulation, translates and parses device I/O and control commands based on the USB protocol, manages device descriptors, and matches descriptors based on the enum information reported by the USB device. This module creates the corresponding USB interface objects and adds them to the USB Interface Pool module for management.
| -------- | -------- |
| const struct UsbFnDevice \*UsbFnCreateDevice(const<br>char \*udcName, const struct UsbFnDescriptorData<br>\*descriptor); | Creates a USB device.|
| int UsbFnRemoveDevice(struct UsbFnDevice<br>\*fnDevice); | Deletes a USB device.|
| const struct UsbFnDevice \*UsbFnGetDevice(const char<br>\*udcName); | Obtains a USB device.|
**Table 4** usbfn_interface.h
- The Device I/O Manager module manages USB I/O requests and provides synchronous and asynchronous I/O management mechanisms. For the asynchronous I/O management mechanism, the module records the asynchronous I/O requests and processes the requests to be sent through the APIs provided by the Raw API Library module. After receiving the processing result from the USB controller, the I/O request receiving thread parses the processing result and reports it to the upper-layer caller.
| API| Description|
- The Raw API Library module abstracts underlying OS capabilities, defines unified OS capability APIs, and provides the USB RAW APIs needed to implement more complex driver functions.
- The OS Adapter module encapsulates operations related to platforms (Linux and LiteOS). It compiles encapsulation APIs depending on the configuration of the specific platform. On the Linux platform, all USB FS access operations are encapsulated in this module. On the LiteOS platform, all device access operations based on the FreeBSD USB framework are encapsulated in this module.
| API| Description|
- The PNP Notify module dynamically monitors USB status changes. This module updates the device information when a device is added or removed. Meanwhile, it reports all USB device information to the PNP Notify Manager module on the UHDF side through the KHDF to load or uninstall third-party function drivers.
| -------- | -------- |
| struct UsbFnRequest<br>\*UsbFnAllocCtrlRequest(UsbFnInterfaceHandle handle,<br>uint32_t len); | Allocates a control transfer request.|
| struct UsbFnRequest \*UsbFnAllocRequest(UsbFnInterfaceHandle handle,<br>uint8_t pipe, uint32_t len); | Allocates a data request.|
| int UsbFnFreeRequest(struct UsbFnRequest \*req); | Releases a request.|
| int UsbFnSubmitRequestAsync(struct UsbFnRequest<br>\*req); | Sends an asynchronous request.|
| int UsbFnSubmitRequestSync(struct UsbFnRequest<br>\*req, uint32_t timeout); | Sends a synchronous request.|
| int UsbFnCancelRequest(struct UsbFnRequest \*req); | Cancels a request.|
#### USB Device DDK
## How to Develop
The USB Device DDK provides the capability of developing USB drivers on the device side. For example, with the dynamic registration and deregistration capabilities, you can dynamically add and combine USB ports based on the actual requirement; with the dynamic instantiation capability, you can create device instances and transmission channels based on dynamically delivered device, configuration, interface, and endpoint descriptors. In addition, the following functions are supported: sending and receiving data in user mode, isolating multiple logical devices from each other on a physical device, and accessing different logical devices from different application processes at the same time.
The USB driver is developed based on the Hardware Driver Foundation (HDF), platform, and Operating System Abstraction Layer (OSAL) APIs. A unified driver model is provided for USB devices, irrespective of the operating system and chip architecture. This document uses a serial port as an example to describe how to develop drivers for the USB host and USB device.
- The SDK IF module divides USB devices logically by device, interface, and pipe, and encapsulates functions including configuration management, device management, and I/O management. This module also provides APIs for device driver development, such as creating and obtaining devices, receiving events, and sending and receiving data.
1. Configure the driver mapping table.
- The Configuration Manager module parses the .hcs file for the USB descriptor information, which will be used for creating USB devices. In addition, the module provides operations such as reading, creating, deleting, and modifying custom USB attributes.
2. Initialize the USB host DDK.
- The Device Manager module parses USB descriptor information and creates USB devices accordingly. It also provides functions such as adding or deleting USB devices, obtaining USB device status, and obtaining USB device interface information.
3. Obtain a **UsbInterface** object.
- The IO Manager module reads and writes data, including common events and data read and write events. It supports data read and write in synchronous and asynchronous modes.
4. Open the **UsbInterface** object to obtain the **UsbInterfaceHandle** object.
- The Adapter IF module encapsulates device node operations of composite device configuration drivers and common function drivers to provide unified device management APIs for the upper layer.
5. Obtain pipe information of the specified **pipeIndex** based on the **UsbInterfaceHandle** object.
- The Adapter module is provided by the composite device configuration driver and common function driver.
6. Allocate an I/O request for the **UsbInterfaceHandle** object.
## How to Develop
7. Fill in the I/O request based on the input parameters.
The USB driver development in kernel mode is complex. Therefore, you need to have a deep understanding of the USB protocol. The USB DDK is introduced to help you to develop USB drivers in user mode more conveniently.
8. Submit the I/O request in synchronous or asynchronous mode.
### When to Use
The USB Host DDK comes with two modes, namely, common mode and expert mode. In common mode, you can directly read and write USB data by using USB DDK APIs without knowing details about data transfer at the bottom layer. In expert mode, you can use USB RAW APIs to directly access the USB channel interfaces provided by the OS platform to implement more complex functions. The USB Device DDk provides functions such as USB device management, interface definition, and USB data request.
### Developing Driver Using Host Raw APIs
### Available APIs
1. Configure the driver mapping table.
The following table lists the APIs related to USB host driver development (common mode). For details about the API definitions, see the [source code](https://gitee.com/openharmony/drivers_peripheral/blob/master/usb/interfaces/ddk/host/usb_ddk_interface.h).
2. Initialize the host raw data, open the USB device, obtain the descriptor, and then obtain interface and endpoint information based on the descriptor.
**Table 1** APIs for USB host driver development (common mode)
3. Allocate a request and fill in the request based on the transfer type.
| API| Description|
| -------- | -------- |
| int32_t UsbInitHostSdk(struct UsbSession \*\*session); | Initializes the USB host driver DDK.|
| const struct UsbInterface \*UsbClaimInterface(const<br>struct UsbSession \*session, uint8_t busNum, uint8_t<br>usbAddr, uint8_t interfaceIndex); | Obtains a USB interface.|
| UsbInterfaceHandle \*UsbOpenInterface(const struct<br>UsbInterface \*interfaceObj); | Opens a USB interface.|
| int32_t UsbGetPipeInfo(const UsbInterfaceHandle<br>\*interfaceHandle, uint8_t settingIndex, uint8_t pipeId,<br>struct UsbPipeInfo \*pipeInfo); | Obtains USB pipe information.|
| struct UsbRequest \*UsbAllocRequest(const<br>UsbInterfaceHandle \*interfaceHandle, int32_t isoPackets<br>, int32_t length); | Allocates a request object.|
| int32_t UsbFillRequest(const struct UsbRequest<br>\*request, const UsbInterfaceHandle \*interfaceHandle,<br>const struct UsbRequestParams \*params); | Fills in a request.|
| int32_t UsbSubmitRequestSync(const struct UsbRequest<br>\*request); | Sends a synchronous request.|
4. Submit the I/O request in synchronous or asynchronous mode.
The following table lists the APIs related to USB host driver development (expert mode). For details about the API definitions, see the [source code](https://gitee.com/openharmony/drivers_peripheral/blob/master/usb/interfaces/ddk/host/usb_raw_api.h).
**Table 2** APIs for USB host driver development (expert mode)
### Developing Driver Using Device DDK APIs
| API| Description|
| -------- | -------- |
| int32_t UsbRawInit(struct UsbSession \*\*session); | Initializes the USB raw APIs.|
| UsbRawHandle \*UsbRawOpenDevice(const struct<br>UsbSession \*session, uint8_t busNum, uint8_t<br>usbAddr); | Opens a USB device.|
| int32_t UsbRawSendControlRequest(const struct<br>UsbRawRequest \*request, const UsbRawHandle<br>\*devHandle, const struct UsbControlRequestData<br>\*requestData); | Performs a control transfer synchronously.|
| int32_t UsbRawSendBulkRequest(const struct<br>UsbRawRequest \*request, const UsbRawHandle<br>\*devHandle, const struct UsbRequestData<br>\*requestData); | Performs a bulk transfer synchronously.|
| int32_t UsbRawSendInterruptRequest(const struct<br>UsbRawRequest \*request, const UsbRawHandle<br>\*devHandle, const struct UsbRequestData<br>\*requestData); | Performs an interrupt transfer synchronously.|
| int32_t UsbRawGetConfigDescriptor(const UsbRawDevice<br>\*rawDev, uint8_t configIndex, struct<br>UsbRawConfigDescriptor \*\*config); | Obtains the configuration descriptor of a device.|
| int32_t UsbRawFillInterruptRequest(const struct UsbRawRequest<br>\*request, const UsbRawHandle \*devHandle, const struct<br>UsbRawFillRequestData \*fillData); | Fills in an interrupt transfer request.|
| int32_t UsbRawFillIsoRequest(const struct UsbRawRequest<br>\*request, const UsbRawHandle \*devHandle, const struct<br>UsbRawFillRequestData \*fillData); | Fills in an isochronous transfer request.|
| int32_t UsbRawSubmitRequest(const struct UsbRawRequest<br>\*request); | Submits a transfer request.|
| int32_t UsbRawCancelRequest(const struct UsbRawRequest<br>\*request); | Cancels a transfer request.|
| int32_t UsbRawHandleRequests(const UsbRawHandle<br>\*devHandle); | Handles a transfer request event.|
1. Construct a descriptor.
The following table lists the APIs for USB device management on the device side. For details about the API definitions, see the [source code](https://gitee.com/openharmony/drivers_peripheral/blob/master/usb/interfaces/ddk/device/usbfn_device.h).
2. Instantiate a USB device using the descriptor constructed.
**Table 3** APIs for USB device management on the device side
3. Call **UsbFnDeviceGetInterface** to obtain an interface, call **UsbFnInterfaceGetPipeInfo** to obtain pipe information based on the interface, call **UsbFnInterfaceOpen** to open the interface to obtain the handle, and call **UsbFnRequestAlloc** to obtain the request based on the handle and pipe ID.
| API| Description|
| -------- | -------- |
| const struct UsbFnDevice \*UsbFnCreateDevice(const<br>char \*udcName, const struct UsbFnDescriptorData<br>\*descriptor); | Creates a USB device.|
| int32_t UsbFnRemoveDevice(struct UsbFnDevice<br>\*fnDevice); | Deletes a USB device.|
| const struct UsbFnDevice \*UsbFnGetDevice(const char<br>\*udcName); | Obtains a USB device.|
4. Call **UsbFnInterfaceStartRecvEvent** to receive events such as Enable and Setup, and respond to the events in **UsbFnEventCallback**.
The following table lists the APIs for USB interface definition on the device side. For details about the API definitions, see the [source code](https://gitee.com/openharmony/drivers_peripheral/blob/master/usb/interfaces/ddk/device/usbfn_interface.h).
5. Send and receive data in synchronous or asynchronous mode.
**Table 4** APIs for USB interface definition on the device side
The following table lists the APIs for USB data request on the device side. For details about the API definitions, see the [source code](https://gitee.com/openharmony/drivers_peripheral/blob/master/usb/interfaces/ddk/device/usbfn_request.h).
The following examples help you better understand the development of the USB serial port driver.
**Table 5** APIs for USB data request on the device side
| API| Description|
| -------- | -------- |
| struct UsbFnRequest<br>\*UsbFnAllocCtrlRequest(UsbFnInterfaceHandle handle,<br>uint32_t len); | Allocates a control transfer request.|
| struct UsbFnRequest \*UsbFnAllocRequest(UsbFnInterfaceHandle handle,<br>uint8_t pipe, uint32_t len); | Allocates a data request.|
| int32_t UsbFnFreeRequest(struct UsbFnRequest \*req); | Releases a request.|
| int32_t UsbFnSubmitRequestAsync(struct UsbFnRequest<br>\*req); | Sends an asynchronous request.|
| int32_t UsbFnSubmitRequestSync(struct UsbFnRequest<br>\*req, uint32_t timeout); | Sends a synchronous request.|
| int32_t UsbFnCancelRequest(struct UsbFnRequest \*req); | Cancels a request.|
### Developing Driver Using Host DDK APIs
### How to Develop
```
USB drivers are developed based on the Hardware Driver Foundation (HDF), platform, and Operating System Abstraction Layer (OSAL) APIs. A unified driver model is provided for USB devices, irrespective of the operating system and chip architecture. This section uses the serial port as an example to describe how to develop USB host and USB device drivers.
root {
#### Developing Driver Using Host DDK APIs
1. Configure USB host driver information in the .hcs file of private device data.
1. Configure USB host driver information in the .hcs file of private device data. For details, see step 1 in the previous section.
2. Initialize the host raw data, open the USB device, obtain the descriptor, and then obtain interface and endpoint information based on the descriptor.
acm->readReq[i] = UsbAllocRequest(InterfaceIdToHandle(acm, acm->dataInPipe->interfaceId), 0, acm->readSize); // Allocate the readReq I/O request to be sent.
HDF_LOGE("%s: interface%d is null", __func__, acm->interfaceIndex[i]);
gotoerror;
gotoerror;
}
}
}
}
acm->ctrIface = GetUsbInterfaceById((const struct AcmDevice *)acm, USB_CTRL_INTERFACE_ID); // Obtain the UsbInterface object corresponding to the control interface.
// Obtain the UsbInterface object corresponding to the control interface.
acm->ctrPipe = EnumePipe(acm, acm->ctrIface->info.interfaceIndex, USB_PIPE_TYPE_CONTROL, USB_PIPE_DIRECTION_OUT); // Obtain pipe information of the control pipe.
snd->request = UsbAllocRequest(InterfaceIdToHandle(acm, acm->dataOutPipe->interfaceId), 0, acm->writeSize); // Allocate the I/O request object to be sent.
The core code of the USB ACM device is stored in **drivers\peripheral\usb\gadget\function\acm\cdcacm.c**. The following sample code implements driver development by using the Device DDK APIs. To develop a driver, you must create a device based on the descriptor, obtain the interface, open the interface to obtain the pipe information, receive events, and then perform USB communication (such as read and write). When the device is uninstalled, you need to close the interface, stop receiving events, and remove the device.
The core code of the USB Abstract Control Model (ACM) device is available in **drivers\peripheral\usb\gadget\function\acm\cdcacm.c**. The following is an example.