diff --git a/en/device-dev/driver/driver-platform-gpio-des.md b/en/device-dev/driver/driver-platform-gpio-des.md index 4368a41d463d17ea912690b837d31e15d7a76085..34d6279ffab201e88471dc0a043de62f0a0413c3 100644 --- a/en/device-dev/driver/driver-platform-gpio-des.md +++ b/en/device-dev/driver/driver-platform-gpio-des.md @@ -1,281 +1,368 @@ # GPIO - ## Overview +### Function + A general-purpose input/output (GPIO) controller manages all GPIO pins by group. Each group of GPIO pins is associated with one or more registers. The GPIO controller manages the pins by reading data from and writing data to the registers. -The GPIO APIs define a set of standard functions for performing operations on GPIO pins, including: +The GPIO module provides APIs for performing operations on GPIO pins, including: - Setting the pin direction, which can be input or output (high impedance is not supported currently) - - Reading and writing the pin level, which can be low or high +- Setting an interrupt service routine (ISR) function and interrupt trigger mode for a pin +- Enabling or disabling interrupts for a pin + +### Basic Concepts + +A GPIO can be used as an input, an output, or both, and is controllable by software. + +- GPIO input -- Setting the interrupt service routine (ISR) function and interrupt trigger mode for a pin + When a GPIO is used as an input, it reads the level state (high or low) of each pin. Common input modes include analog input, floating input, pull-up input, and pull-down input. -- Enabling or disabling pin interrupts +- GPIO output + When a GPIO is used as an output, it sets the pin level. Common output modes include open-drain output, push-pull output, multiplexed open-drain output, and multiplexed push-pull output. -## Available APIs +### Working Principles - **Table 1** GPIO driver APIs +In the Hardware Driver Foundation (HDF), the GPIO module uses the unified service mode for API adaptation. In this mode, a device service is used as the GPIO manager to handle access requests from the devices of the same type in a unified manner. The unified service mode applies to the scenario where there are many device objects of the same type. If the independent service mode is used in this case, more device nodes need to be configured and more memory resources will be consumed. The following figure shows the unified service mode. -| Category| Description| -| -------- | -------- | -| GPIO read/write| - **GpioRead**: reads the pin level.
- **GpioWrite**: writes the pin level.| -| GPIO settings| - **GpioSetDir**: sets the pin direction.
- **GpioGetDir**: obtains the pin direction.| -| GPIO interrupt settings| - **GpioSetIrq**: sets the ISR function for a GPIO pin.
- **GpioUnsetIrq**: cancels the ISR function setting for a GPIO pin.
- **GpioEnableIrq**: enables interrupts for a pin.
- **GpioDisableIrq**: disables interrupts for a pin.| +In the unified service mode, the core layer manages all controllers in a unified manner and publishes a service for the interface layer. That is, the driver does not need to publish a service for each controller. -> ![icon-note.gif](public_sys-resources/icon-note.gif) **NOTE**
-> All APIs described in this document can be called only in kernel mode. +The GPIO module is divided into the following layers: +- Interface layer: provides APIs for operating GPIO pins. +- Core layer: provides the capabilities of adding and removing the GPIO controller and managing GPIO pins. This layer interacts with the adaptation layer through hook functions to allow the GPIO chip drivers of different vendors to quickly access the HDF. +- Adaptation layer: instantiates hook functions to implement specific features. + +**Figure 1** Unified service mode + +![](figures/unified-service-mode.png) ## Usage Guidelines +### When to Use -### How to Use +As a concept at the software layer, GPIO is used to manage GPIO pin resources. You can use the GPIO APIs to control pins. -The figure below shows the general GPIO development process. In the APIs, a GPIO pin is specified by the pin number. +### Available APIs -**Figure 1** Using GPIO driver APIs +The following table describes the APIs provided by the GPIO module. -![](figures/using-GPIO-process.png "using-gpio-process.png") +**Table 1** GPIO driver APIs +| API | Description | +| ------------------------------------------------------------ | ------------------------------ | +| GpioGetByName(const char *gpioName) | Obtains the GPIO pin number. | +| int32_t GpioRead(uint16_t gpio, uint16_t *val) | Reads the level of a GPIO pin. | +| int32_t GpioWrite(uint16_t gpio, uint16_t val) | Writes the level of a GPIO pin. | +| int32_t GpioGetDir(uint16_t gpio, uint16_t *dir) | Obtains the direction of a GPIO pin. | +| int32_t GpioSetDir(uint16_t gpio, uint16_t dir) | Sets the direction for a GPIO pin. | +| int32_t GpioUnsetIrq(uint16_t gpio, void *arg); | Cancels the ISR function for a GPIO pin. | +| int32_t GpioSetIrq(uint16_t gpio, uint16_t mode, GpioIrqFunc func, void *arg) | Sets an ISR function for a GPIO pin. | +| int32_t GpioEnableIrq(uint16_t gpio) | Enables interrupts for a GPIO pin. | +| int32_t GpioDisableIrq(uint16_t gpio) | Disables interrupts for a GPIO pin. | -### Determining the GPIO Pin Number +>![](../public_sys-resources/icon-note.gif) **NOTE** +> +>All GPIO APIs described in this document can be used in kernel mode and user mode. -The method for determining the GPIO pin number varies depending on the GPIO controller model, parameters, and controller driver of the system on chip (SoC). +### How to Develop -- Hi3516D V300 - A controller manages 12 groups of GPIO pins. Each group contains 8 GPIO pins. The group number ranges from 0 to 11. +The fillowing figure shows how to use the GPIO APIs to manage pins. In the APIs, a GPIO pin is identified by the pin number. - GPIO pin number = GPIO group number x Number of GPIO pins in each group + Offset in the group +**Figure 2** Using GPIO driver APIs - Example: +![](figures/using-GPIO-process.png) - GPIO pin number of GPIO10_3 = 10 x 8 + 3 = 83 - -- Hi3518E V300 - A controller manages 10 groups of GPIO pins. Each group contains 10 GPIO pins. The group number ranges from 0 to 9. +#### Determining the GPIO Pin Number - GPIO pin number = GPIO group number x Number of GPIO pins in each group + Offset in the group +You can determine the GPIO pin number in either of the following ways: - Example: - - GPIO pin number of GPIO7_3 = 7 x 10 + 3 = 73 +- Calculating the pin number based on the system on chip (SoC) + The method for determining the GPIO pin number varies depending on the GPIO controller model, parameters, and controller driver of the SoC. -### Using APIs to Operate GPIO Pins + - Hi3516D V300 -- Set the GPIO pin direction. - Before performing read/write operations on a GPIO pin, call **GpioSetDir()** to set the pin direction. + A controller manages 12 groups of GPIO pins. Each group contains 8 GPIO pins. The group number ranges from 0 to 11. - ```c - int32_t GpioSetDir(uint16_t gpio, uint16_t dir); - ``` + GPIO pin number = GPIO group number x Number of GPIO pins in each group + Offset in the group - **Table 2** Description of GpioSetDir - - | **Parameter**| **Description**| - | -------- | -------- | - | gpio | GPIO pin number.| - | dir | Direction to set.| - | **Return Value**| **Description**| - | 0 | The operation is successful.| - | Negative value| The operation failed.| + Example: -- Read or write the pin level. + GPIO pin number of GPIO10_3 = 10 x 8 + 3 = 83 - Call **GpioRead()** to read the level of a GPIO pin. + - Hi3518E V300 - ```c - int32_t GpioRead(uint16_t gpio, uint16_t *val); - ``` + A controller manages 10 groups of GPIO pins. Each group contains 10 GPIO pins. The group number ranges from 0 to 9. - **Table 3** Description of GpioRead - - | **Parameter**| **Description**| - | -------- | -------- | - | gpio | GPIO pin number.| - | val | Pointer to the level to read.| - | **Return Value**| **Description**| - | 0 | The operation is successful.| - | Negative value| The operation failed.| + GPIO pin number = GPIO group number x Number of GPIO pins in each group + Offset in the group - Call **GpioWrite()** to write the level for a GPIO pin. + Example: + + GPIO pin number of GPIO7_3 = 7 x 10 + 3 = 73 + +- Obtaining the pin number based on the pin alias + + Use **GpioGetByName()** to obtain the pin number based on the pin alias. The global pin number is returned. ```c - int32_t GpioWrite(uint16_t gpio, uint16_t val); - ``` - - **Table 4** Description of GpioWrite - - | **Parameter**| **Description**| - | -------- | -------- | - | gpio | GPIO pin number.| - | val | Level to write.| - | **Return Value**| **Description**| - | 0 | The operation is successful.| - | Negative value| The operation failed.| - - Sample code: - - - ``` - int32_t ret; - uint16_t val; - /* Set the direction of GPIO pin 3 to output. */ - ret = GpioSetDir(3, GPIO_DIR_OUT); - if (ret != 0) { - HDF_LOGE("GpioSerDir: failed, ret %d\n", ret); - return; - } - /* Write the low level GPIO_VAL_LOW for GPIO pin 3. */ - ret = GpioWrite(3, GPIO_VAL_LOW); - if (ret != 0) { - HDF_LOGE("GpioWrite: failed, ret %d\n", ret); - return; - } - /* Set the direction of GPIO pin 6 to input. */ - ret = GpioSetDir(6, GPIO_DIR_IN); - if (ret != 0) { - HDF_LOGE("GpioSetDir: failed, ret %d\n", ret); - return; - } - /* Read the level of GPIO pin 6. */ - ret = GpioRead(6, &val); + GpioGetByName(const char *gpioName); ``` -- Set the ISR function for a GPIO pin. +#### Setting the GPIO Pin Direction - Call **GpioSetIrq()** to set the ISR function for a GPIO pin. +Before performing read/write operations on a GPIO pin, use **GpioSetDir()** to set the pin direction. - ```c - int32_t GpioSetIrq(uint16_t gpio, uint16_t mode, GpioIrqFunc func, void *arg); - ``` - - **Table 5** Description of GpioSetIrq - - | **Parameter**| **Description**| - | -------- | -------- | - | gpio | GPIO pin number.| - | mode | Interrupt trigger mode.| - | func | ISR function to set.| - | arg | Pointer to the parameters passed to the ISR function.| - | **Return Value**| **Description**| - | 0 | The operation is successful.| - | Negative value| The operation failed.| - - > ![icon-caution.gif](../public_sys-resources/icon-caution.gif) **CAUTION**
- > Only one ISR function can be set for a GPIO pin. If **GpioSetIrq** is called repeatedly, the previous IRS function will be replaced. - - If the ISR function is no longer required, call **GpioUnsetIrq()** to cancel it. +```c +int32_t GpioSetDir(uint16_t gpio, uint16_t dir); +``` - ```c - int32_t GpioUnsetIrq(uint16_t gpio, void *arg); - ``` +**Table 2** Description of GpioSetDir - **Table 6** Description of GpioUnsetIrq - - | **Parameter**| **Description**| - | -------- | -------- | - | gpio | GPIO pin number.| - | arg | Pointer to the GPIO interrupt parameters.| - | **Return Value**| **Description**| - | 0 | The operation is successful.| - | Negative value| The operation failed.| +| **Parameter** | **Description** | +| ---------- | ------------------ | +| gpio | GPIO pin number.| +| dir | Direction to set. | +| **Return Value**| **Description** | +| 0 | The operation is successful. | +| Negative value | The operation failed. | - After the ISR function is set, call **GpioEnableIrq()** to enable interrupts for the GPIO pin. +Example: Set the direction of GPIO pin 3 to output. - ```c - int32_t GpioEnableIrq(uint16_t gpio); - ``` +```c +int32_t ret; - **Table 7** Description of GpioEnableIrq - - | **Parameter**| **Description**| - | -------- | -------- | - | gpio | GPIO pin number.| - | **Return Value**| **Description**| - | 0 | The operation is successful.| - | Negative value| The operation failed.| +ret = GpioSetDir(3, GPIO_DIR_OUT); // Set GPIO pin 3 as an output. +if (ret != 0) { + HDF_LOGE("GpioSerDir: failed, ret %d\n", ret); + return ret; +} +``` - > ![icon-caution.gif](../public_sys-resources/icon-caution.gif) **CAUTION**
- > The configured ISR function can be responded only after interrupts are enabled for the GPIO pin. +#### Obtaining the GPIO Pin Direction - You can call **GpioDisableIrq** to disable interrupts for the pin. +Use **GpioGetDir()** to obtain the GPIO pin direction. - ```c - int32_t GpioDisableIrq(uint16_t gpio); - ``` +```c +int32_t GpioGetDir(uint16_t gpio, uint16_t *dir); +``` - **Table 8** Description of GpioDisableIrq - - | **Parameter**| **Description**| - | -------- | -------- | - | gpio | GPIO pin number.| - | **Return Value**| **Description**| - | 0 | The operation is successful.| - | Negative value| The operation failed.| +**Table 3** Description of GpioGetDir - Sample code: +| **Parameter** | **Description** | +| ---------- | ------------------ | +| gpio | GPIO pin number.| +| dir | Pointer to the direction value obtained. | +| **Return Value**| **Description** | +| 0 | The operation is successful. | +| Negative value | The operation failed. | - - ``` - /* Set the ISR function. */ - int32_t MyCallBackFunc(uint16_t gpio, void *data) - { - HDF_LOGI("%s: gpio:%u interrupt service in! data=%p\n", __func__, gpio, data); - return 0; - } - - int32_t ret; - /* Set the ISR function to MyCallBackFunc, with input parameter of NULL and the interrupt trigger mode of rising edge. */ - ret = GpioSetIrq(3, OSAL_IRQF_TRIGGER_RISING, MyCallBackFunc, NULL); - if (ret != 0) { - HDF_LOGE("GpioSetIrq: failed, ret %d\n", ret); - return; - } - - /* Enable interrupts for GPIO pin 3. */ - ret = GpioEnableIrq(3); - if (ret != 0) { - HDF_LOGE("GpioEnableIrq: failed, ret %d\n", ret); - return; - } - - /* Disable interrupts for GPIO pin 3. */ - ret = GpioDisableIrq(3); - if (ret != 0) { - HDF_LOGE("GpioDisableIrq: failed, ret %d\n", ret); - return; - } - - /* Cancel the ISR function setting for GPIO pin 3. */ - ret = GpioUnsetIrq(3, NULL); - if (ret != 0) { - HDF_LOGE("GpioUnSetIrq: failed, ret %d\n", ret); - return; - } - ``` +Example: Obtain the direction of GPIO pin 3. +```c +int32_t ret; +uin16_t dir; -## Example +ret = GpioGetDir(3, &dir); // Obtain the direction of GPIO pin 3. +if (ret != 0) { + HDF_LOGE("GpioGetDir: failed, ret %d\n", ret); + return ret; +} +``` -The procedure is as follows: +#### Reading the GPIO Pin Level -1. Select an idle GPIO pin, for example, pin GPIO10\_3 on a Hi3516D V300 development board. +Use **GpioRead()** to read the level of a GPIO pin. - The pin number is 83. +```c +int32_t GpioRead(uint16_t gpio, uint16_t *val); +``` - GPIO pin number of GPIO10_3 = 10 x 8 + 3 = 83 +**Table 4** Description of GpioRead - You can select an idle GPIO pin as required. +| **Parameter** | **Description** | +| ---------- | -------------------- | +| gpio | GPIO pin number. | +| val | Pointer to the level value read. | +| **Return Value**| **Description** | +| 0 | The operation is successful. | +| Negative value | The operation failed. | -2. Set the ISR function for the pin, with the trigger mode of rising edge and failing edge. - -3. Write high and low levels to the pin alternately, and observe the execution of the ISR function. +Example: Read the level of GPIO pin 3. + +```c +int32_t ret; +uint16_t val; + +ret = GpioRead(3, &val); // Read the level of GPIO pin 3. +if (ret != 0) { + HDF_LOGE("GpioRead: failed, ret %d\n", ret); + return ret; +} +``` + +#### Writing the GPIO Pin Level + +Use **GpioWrite()** to write the level for a GPIO pin. + +```c +int32_t GpioWrite(uint16_t gpio, uint16_t val); +``` + +**Table 5** Description of GpioWrite + +| **Parameter** | **Description** | +| ---------- | ------------------ | +| gpio | GPIO pin number.| +| val | Level to write. | +| **Return Value**| **Description** | +| 0 | The operation is successful. | +| Negative value | The operation failed. | + +Example: Write a low level value to the register of GPIO pin 3. + +```c +int32_t ret; + +ret = GpioWrite(3, GPIO_VAL_LOW); // Write a low level value to the register of GPIO pin 3. +if (ret != 0) { + HDF_LOGE("GpioRead: failed, ret %d\n", ret); + return ret; +} ``` + +#### Setting an ISR Function for a GPIO Pin + +Use **GpioSetIrq()** to set an ISR function for a GPIO pin. + +```c +int32_t GpioSetIrq(uint16_t gpio, uint16_t mode, GpioIrqFunc func, void *arg); +``` + +**Table 6** Description of GpioSetIrq + +| **Parameter** | **Description** | +| ---------- | ------------------------ | +| gpio | GPIO pin number. | +| mode | Interrupt trigger mode. | +| func | ISR function to set. | +| arg | Pointer to the parameters passed to the ISR function.| +| **Return Value**| **Description** | +| 0 | The operation is successful. | +| Negative value | The operation failed. | + +> ![icon-caution.gif](../public_sys-resources/icon-caution.gif) **CAUTION**
+> Only one ISR function can be set for a GPIO pin. If **GpioSetIrq** is called repeatedly, the previous IRS function will be replaced. + +#### Canceling the ISR Function for a GPIO Pin + +If the ISR function is no longer required, call **GpioUnsetIrq()** to cancel it. + +```c +int32_t GpioUnsetIrq(uint16_t gpio, void *arg); +``` + +**Table 7** Description of GpioUnsetIrq + +| **Parameter** | **Description** | +| ---------- | -------------- | +| gpio | GPIO pin number. | +| arg | Pointer to the GPIO interrupt parameters. | +| **Return Value**| **Description**| +| 0 | The operation is successful. | +| Negative value | The operation failed. | + +#### Enabling Interrupts for a GPIO Pin + +After the ISR function is set, call **GpioEnableIrq()** to enable interrupts for the GPIO pin. + +```c +int32_t GpioEnableIrq(uint16_t gpio); +``` + +**Table 8** Description of GpioEnableIrq + +| **Parameter** | **Description** | +| ---------- | -------------- | +| gpio | GPIO pin number. | +| **Return Value**| **Description**| +| 0 | The operation is successful. | +| Negative value | The operation failed. | + +> ![icon-caution.gif](../public_sys-resources/icon-caution.gif) **CAUTION**
+> The configured ISR function can be responded only after interrupts are enabled for the GPIO pin. + +#### Disabling Interrupts for a GPIO Pin + +Use **GpioDisableIrq()** to disable interrupts for a pin. + +```c +int32_t GpioDisableIrq(uint16_t gpio); +``` +**Table 9** Description of GpioDisableIrq + +| **Parameter** | **Description** | +| ---------- | -------------- | +| gpio | GPIO pin number. | +| **Return Value**| **Description**| +| 0 | The operation is successful. | +| Negative value | The operation failed. | + +Example: + +```c +/* Set an ISR function. */ +int32_t MyCallBackFunc(uint16_t gpio, void *data) +{ + HDF_LOGI("%s: gpio:%u interrupt service in data\n", __func__, gpio); + return 0; +} + +int32_t ret; +/* Set the ISR function to MyCallBackFunc, with input parameter of NULL and the interrupt trigger mode of rising edge. */ +ret = GpioSetIrq(3, OSAL_IRQF_TRIGGER_RISING, MyCallBackFunc, NULL); +if (ret != 0) { + HDF_LOGE("GpioSetIrq: failed, ret %d\n", ret); + return ret; +} + +/* Enable interrupts for GPIO pin 3. */ +ret = GpioEnableIrq(3); +if (ret != 0) { + HDF_LOGE("GpioEnableIrq: failed, ret %d\n", ret); + return ret; +} + +/* Disable interrupts for GPIO pin 3. */ +ret = GpioDisableIrq(3); +if (ret != 0) { + HDF_LOGE("GpioDisableIrq: failed, ret %d\n", ret); + return ret; +} + +/* Cancel the ISR function for GPIO pin 3. */ +ret = GpioUnsetIrq(3, NULL); +if (ret != 0) { + HDF_LOGE("GpioUnSetIrq: failed, ret %d\n", ret); + return ret; +} +``` + +## Example + +The following example shows how to trigger an interrupt for a GPIO pin. The procedure is as follows: + +1. Select an idle GPIO pin, for example, pin GPIO10_3 on a Hi3516D V300 development board. The pin number of GPIO10_3 is 83. You can select an idle GPIO pin as required. +2. Set an ISR function for the pin, with the interrupt trigger mode of rising edge and falling edge. +3. Write high and low levels to the pin alternately, and observe the execution of the ISR function. + +Sample code: + +```c #include "gpio_if.h" #include "hdf_log.h" #include "osal_irq.h" @@ -286,7 +373,7 @@ static uint32_t g_irqCnt; /* ISR function */ static int32_t TestCaseGpioIrqHandler(uint16_t gpio, void *data) { - HDF_LOGE("%s: irq triggered! on gpio:%u, data=%p", __func__, gpio, data); + HDF_LOGE("%s: irq triggered! on gpio:%u, in data", __func__, gpio); g_irqCnt++; /* If the ISR function is triggered, the global interrupt counter is incremented by 1. */ return GpioDisableIrq(gpio); } diff --git a/en/device-dev/driver/driver-platform-gpio-develop.md b/en/device-dev/driver/driver-platform-gpio-develop.md index 4e8b93552c64ff7faae7ee814c0e2cb0c23776b2..76077fc64d1415b51616150c50d6383ebc082fe8 100644 --- a/en/device-dev/driver/driver-platform-gpio-develop.md +++ b/en/device-dev/driver/driver-platform-gpio-develop.md @@ -1,216 +1,255 @@ # GPIO - ## Overview -In the Hardware Driver Foundation (HDF), the general-purpose input/output (GPIO) module uses the service-free mode for API adaptation. The service-free mode applies to the devices that do not provide user-mode APIs or the operating system (OS) that does not distinguish the user mode and the kernel mode. In the service-free mode, **DevHandle** (a void pointer) directly points to the kernel-mode address of the device object. +### Function - **Figure 1** Service-free mode +A general-purpose input/output (GPIO) controller manages all GPIO pins by group. Each group of GPIO pins is associated with one or more registers. The GPIO controller manages the pins by reading data from and writing data to the registers. - ![](figures/service-free-mode.png "service-free-mode") +### Basic Concepts +A GPIO can be used as an input, an output, or both, and is controllable by software. -## Available APIs +- GPIO input -**GpioMethod**: + When a GPIO is used as an input, it reads the level state (high or low) of each pin. Common input modes include analog input, floating input, pull-up input, and pull-down input. +- GPIO output -``` + When a GPIO is used as an output, it sets the pin level. Common output modes include open-drain output, push-pull output, multiplexed open-drain output, and multiplexed push-pull output. + +### Working Principles + +In the Hardware Driver Foundation (HDF), the GPIO module uses the unified service mode for API adaptation. In this mode, a device service is used as the GPIO manager to handle access requests from the devices of the same type in a unified manner. The unified service mode applies to the scenario where there are many device objects of the same type. If the independent service mode is used in this case, more device nodes need to be configured and more memory resources will be consumed. The following figure shows the unified service mode. + +In the unified service mode, the core layer manages all controllers in a unified manner and publishes a service for the interface layer. That is, the driver does not need to publish a service for each controller. + +The GPIO module is divided into the following layers: + +- Interface layer: provides APIs for operating GPIO pins. +- Core layer: provides the capabilities of adding and removing a GPIO controller and managing GPIO pins. This layer interacts with the adaptation layer through hook functions to allow the GPIO chip drivers of different vendors to quickly access the HDF. +- Adaptation layer: instantiates hook functions to implement specific features. + +**Figure 1** Unified service mode + +![](figures/unified-service-mode.png) + +## Development Guidelines + +### When to Use + +As a concept at the software layer, GPIO is used to manage GPIO pin resources. You can use the APIs provided by the GPIO module to control pins. Before using your GPIO driver with OpenHarmony, you need to perform GPIO driver adaptation. The following sections describe how to adapt the GPIO driver. + +### Available APIs + +To enable the upper layer to successfully operate GPIO pins by calling the GPIO APIs, hook functions are defined in **//drivers/hdf_core/framework/support/platform/include/gpio/gpio_core.h** for the core layer. You need to implement these hook functions at the adaptation layer and hook them to implement the interaction between the interface layer and the core layer. + +**GpioMethod**: + +```c struct GpioMethod { - int32_t (*request)(struct GpioCntlr *cntlr, uint16_t local);// Reserved - int32_t (*release)(struct GpioCntlr *cntlr, uint16_t local);// Reserved - int32_t (*write)(struct GpioCntlr *cntlr, uint16_t local, uint16_t val); - int32_t (*read)(struct GpioCntlr *cntlr, uint16_t local, uint16_t *val); - int32_t (*setDir)(struct GpioCntlr *cntlr, uint16_t local, uint16_t dir); - int32_t (*getDir)(struct GpioCntlr *cntlr, uint16_t local, uint16_t *dir); - int32_t (*toIrq)(struct GpioCntlr *cntlr, uint16_t local, uint16_t *irq);// Reserved - int32_t (*setIrq)(struct GpioCntlr *cntlr, uint16_t local, uint16_t mode, GpioIrqFunc func, void *arg); - int32_t (*unsetIrq)(struct GpioCntlr *cntlr, uint16_t local); - int32_t (*enableIrq)(struct GpioCntlr *cntlr, uint16_t local); - int32_t (*disableIrq)(struct GpioCntlr *cntlr, uint16_t local); + int32_t (*request)(struct GpioCntlr *cntlr, uint16_t local); // Reserved. + int32_t (*release)(struct GpioCntlr *cntlr, uint16_t local); // Reserved. + int32_t (*write)(struct GpioCntlr *cntlr, uint16_t local, uint16_t val); + int32_t (*read)(struct GpioCntlr *cntlr, uint16_t local, uint16_t *val); + int32_t (*setDir)(struct GpioCntlr *cntlr, uint16_t local, uint16_t dir); + int32_t (*getDir)(struct GpioCntlr *cntlr, uint16_t local, uint16_t *dir); + int32_t (*toIrq)(struct GpioCntlr *cntlr, uint16_t local, uint16_t *irq); // Reserved. + int32_t (*setIrq)(struct GpioCntlr *cntlr, uint16_t local, uint16_t mode, GpioIrqFunc func, void *arg); + int32_t (*unsetIrq)(struct GpioCntlr *cntlr, uint16_t local); + int32_t (*enableIrq)(struct GpioCntlr *cntlr, uint16_t local); + int32_t (*disableIrq)(struct GpioCntlr *cntlr, uint16_t local); } ``` - **Table 1** Description of the callback functions in GpioMethod +**Table 1** Hook functions in **GpioMethod** | Function| Input Parameter| Output Parameter| Return Value| Description| | -------- | -------- | -------- | -------- | -------- | -| write | **cntlr**: structure pointer to the GPIO controller at the core layer.
**local**: GPIO port number, which is of the uint16_t type.
**val**: input level, which is of the uint16_t type.| –| HDF_STATUS| Writes the level of a GPIO pin.| -| read | **cntlr**: structure pointer to the GPIO controller at the core layer.
**local**: GPIO port number, which is of the uint16_t type.| **val**: pointer to the output level, which is of the uint16_t type.| HDF_STATUS| Reads the level of a GPIO pin.| +| write | **cntlr**: structure pointer to the GPIO controller at the core layer.
**local**: GPIO port number, which is of the uint16_t type.
**val**: level value to write, which is of the uint16_t type.| –| HDF_STATUS| Writes the level for a GPIO pin.| +| read | **cntlr**: structure pointer to the GPIO controller at the core layer.
**local**: GPIO port number, which is of the uint16_t type.| **val**: level value read, which is of the uint16_t type.| HDF_STATUS| Reads the level of a GPIO pin.| | setDir | **cntlr**: structure pointer to the GPIO controller at the core layer.
**local**: GPIO port number, which is of the uint16_t type.
**dir**: pin direction to set, which is of the uint16_t type.| –| HDF_STATUS| Sets the direction (input or output) for a GPIO pin.| -| getDir | **cntlr**: structure pointer to the GPIO controller at the core layer.
**local**: GPIO port number, which is of the uint16_t type.| **dir**: pointer to the pin direction obtained, which is of the uint16_t type.| HDF_STATUS| Obtains the input or output direction of a GPIO pin.| -| setIrq | **cntlr**: structure pointer to the GPIO controller at the core layer.
**local**: GPIO port number, which is of the uint16_t type.
**mode**: trigger mode, which can be edge or level of the uint16_t type.
**func**: pointer to the interrupt request (IRQ) handler.
**arg**: void pointer to the input parameters of the IRQ handler.| –| HDF_STATUS| Sets an IRQ for a GPIO pin.| -| unsetIrq | **cntlr**: structure pointer to the GPIO controller at the core layer.
**local**: GPIO port number, which is of the uint16_t type.| –| HDF_STATUS| Cancels the IRQ settings for a GPIO pin.| +| getDir | **cntlr**: structure pointer to the GPIO controller at the core layer.
**local**: GPIO port number, which is of the uint16_t type.| **dir**: pin direction read, which is of the uint16_t type.| HDF_STATUS| Obtains the input or output direction of a GPIO pin.| +| setIrq | **cntlr**: structure pointer to the GPIO controller at the core layer.
**local**: GPIO port number, which is of the uint16_t type.
**mode**: interrupt trigger mode, which can be edge or level. The value is of the uint16_t type.
**func**: pointer to the interrupt request (IRQ) handler.
**arg**: void pointer to the input parameters of the IRQ handler.| –| HDF_STATUS| Sets an IRQ function for a GPIO pin.| +| unsetIrq | **cntlr**: structure pointer to the GPIO controller at the core layer.
**local**: GPIO port number, which is of the uint16_t type.| –| HDF_STATUS| Cancels the IRQ function for a GPIO pin.| | enableIrq | **cntlr**: structure pointer to the GPIO controller at the core layer.
**local**: GPIO port number, which is of the uint16_t type.| –| HDF_STATUS| Enables interrupts for a GPIO pin.| | disableIrq | **cntlr**: structure pointer to the GPIO controller at the core layer.
**local**: GPIO port number, which is of the uint16_t type.| –| HDF_STATUS| Disables interrupts for a GPIO pin.| +### How to Develop -## How to Develop - -The GPIO controller manages all pins by group. The related parameters are defined in attribute files. You need to instantiate the driver entry and APIs for the vendor driver to access the HDF. - -The GPIO module adaptation involves the following steps: +The GPIO module adaptation procedure is as follows: 1. Instantiate the driver entry. - - Instantiate the **HdfDriverEntry** structure. - - Call **HDF_INIT** to register the **HdfDriverEntry** instance with the HDF. - 2. Configure attribute files. - - Add the **deviceNode** information to the **device_info.hcs** file. - - (Optional) Add the **gpio_config.hcs** file. - 3. Instantiate the GPIO controller object. - - Initialize **GpioCntlr**. - - Instantiate **GpioMethod** in the **GpioCntlr** object. - > ![icon-note.gif](public_sys-resources/icon-note.gif) **NOTE**
- > For details about the functions in **GpioMethod**, see [Available APIs](#available-apis). - 4. Debug the driver. - - (Optional) For new drivers, verify the basic functions, such as the GPIO status control and response to interrupts. +### Example -## Development Example +The following uses the **//device_soc_hisilicon/common/platform/gpio/gpio_hi35xx.c** driver of the Hi3516D V300 development board as an example to describe the driver adaptation. -The following uses **gpio_hi35xx.c** as an example to present the information required for implementing device functions. +1. Instantiate the driver entry. -1. Instantiate the driver entry. - - The driver entry must be a global variable of the **HdfDriverEntry** type (defined in **hdf\_device\_desc.h**), and the value of **moduleName** must be the same as that in **device\_info.hcs**. In the HDF, the start address of each **HdfDriverEntry** object of all loaded drivers is collected to form a segment address space similar to an array for the upper layer to invoke. + The driver entry must be a global variable of the **HdfDriverEntry** type (defined in **hdf_device_desc.h**), and the value of **moduleName** must be the same as that in **device_info.hcs**. In the HDF, the start address of each **HdfDriverEntry** object of all loaded drivers are collected to form a segment address space similar to an array for the upper layer to invoke. Generally, the HDF calls the **Bind** function and then the **Init** function to load a driver. If **Init** fails to be called, the HDF calls **Release** to release driver resources and exit. GPIO driver entry example: - - ``` + + ```c struct HdfDriverEntry g_gpioDriverEntry = { - .moduleVersion = 1, - .Bind = Pl061GpioBind, // Bind does not need to be implemented for GPIO. It is an empty method in this example. You can add related operations as required. - .Init = Pl061GpioInit, // See the Init function. - .Release = Pl061GpioRelease, // See the Release function. - .moduleName = "hisi_pl061_driver",// (Mandatory) The value must be the same as that of moduleName in the .hcs file. + .moduleVersion = 1, + .Bind = Pl061GpioBind, // GPIO does not use the Bind function, which is an empty implementation in this example. You can add related operations as required. + ..Init = Pl061GpioInit, // See the Init function. + .Release = Pl061GpioRelease, // See the Release function. + .moduleName = "hisi_pl061_driver", // (Mandatory) The value must be the same as that of moduleName in the .hcs file. }; - // Call HDF_INIT to register the driver entry with the HDF. - HDF_INIT(g_gpioDriverEntry); + HDF_INIT(g_gpioDriverEntry); // Call HDF_INIT to register the driver entry with the HDF. ``` - -2. Add the **deviceNode** information to the **device_info.hcs** file, and configure the device attributes in the **gpio_config.hcs** file. - - The **deviceNode** information is related to registration of the driver entry. The device attribute values are closely related to the default values or value ranges of the **GpioCntlr** members at the core layer. - In this example, there is only one GPIO controller. If there are multiple GPIO controllers, you need to add the **deviceNode** information to the **device_info** file and add the corresponding device attributes to the **gpio_config** file for each controller. - - - **device_info.hcs** configuration example - - ``` + +2. Configure attribute files. + + Add the deviceNode information to the **device_info.hcs** file. The deviceNode information is closely related to driver entry registration. In this example, there is only one GPIO controller. If there are multiple GPIO controllers, add the deviceNode information to the **device_info.hcs** file for each controller. The device attribute values are closely related to the default values or value ranges of the **GpioCntlr** members at the core layer, and are configured in **gpio_config.hcs**. + + - **device_info.hcs** example + + Add the deviceNode information to the **//vendor/hisilicon/hispark_taurus/hdf_config/device_info/device_info.hcs** file. + + ```c root { - device_info { - platform :: host { - hostName = "platform_host"; - priority = 50; - device_gpio :: device { - device0 :: deviceNode { - policy = 0; // No service is published. - priority = 10; // Driver startup priority. - permission = 0644; // Permission to create device nodes for the driver. - moduleName = "hisi_pl061_driver"; // (Mandatory) Driver name, which must be the same as moduleName in the driver entry. - deviceMatchAttr = "hisilicon_hi35xx_pl061"; // (Mandatory) Private data of the controller. The value must be the same as the controller information in gpio_config.hcs. - // Add private information about all controllers in this file. + device_info { + platform :: host { + hostName = "platform_host"; + priority = 50; + device_gpio :: device { + device0 :: deviceNode { + policy = 0; // The value 0 indicates that no service needs to be published. + priority = 10; // Driver startup priority. + permission = 0644; // Permission for the device node created. + oduleName = "hisi_pl061_driver"; // (Mandatory) Driver name, which must be the same as moduleName in the driver entry. + deviceMatchAttr = "hisilicon_hi35xx_pl061"; // (Mandatory) Private data of the controller. The value must be the same as the controller information in gpio_config.hcs. + // The private information about all controllers is in the gpio_config.hcs file. + } + } } } - } - } } ``` - - **gpio_config.hcs** configuration example - - ``` + + - **gpio_config.hcs** example + + Configure the device attributes in the **//device/soc/hisilicon/hi3516dv300/sdk_liteos/hdf_config/gpio/gpio_config.hcs** file. The parameters are as follows: + + ```c root { - platform { - gpio_config { - controller_0x120d0000 { - match_attr = "hisilicon_hi35xx_pl061"; // (Mandatory) The value must be the same as that of deviceMatchAttr in device_info.hcs. - groupNum = 12; // (Mandatory) GPIO group number. - bitNum = 8; // (Mandatory) Number of GPIO pins in each group. - regBase = 0x120d0000; // (Mandatory) Physical base address. - regStep = 0x1000; // (Mandatory) Register offset step. - irqStart = 48; // (Mandatory) Enable interrupts. - irqShare = 0; // (Mandatory) Whether to share an interrupt. - } + platform { + gpio_config { + controller_0x120d0000 { + match_attr = "hisilicon_hi35xx_pl061"; // (Mandatory) The value must be the same as that of deviceMatchAttr in device_info.hcs. + groupNum = 12; // (Mandatory) GPIO group number. + bitNum = 8; // (Mandatory) Number of GPIO pins in each group. + regBase = 0x120d0000; // (Mandatory) Physical base address. + regStep = 0x1000; // (Mandatory) Register offset step. + irqStart = 48; // (Mandatory) Enable interrupts. + irqShare = 0; // (Mandatory) Whether to share interrupt. The value 1 means to share interrupt; the value 0 means the opposite. + } + ... + } } } - } ``` -3. Initialize the **GpioCntlr** object at the core layer, including defining a custom structure (to pass parameters and data) and implementing the **HdfDriverEntry** member functions (**Init** and **Release**) to instantiate **GpioMethod** in **GpioCntlr** (so that the underlying driver functions can be called). - - Defining a custom structure + After the **gpio_config.hcs** file is configured, include the file in the **hdf.hcs** file. Otherwise, the configuration file cannot take effect. + + ```c + #include "../../../../device/soc/hisilicon/hi3516dv300/sdk_liteos/hdf_config/gpio/gpio_config.hcs" // Relative path of the gpio_config.hcs file. + ``` + +3. Instantiate the GPIO controller object. + + Initialize the **GpioCntlr** object at the core layer, including defining a custom structure (to pass parameters and data) and implementing the **HdfDriverEntry** member functions (**Bind**, **Init** and **Release**) to instantiate **GpioMethod** in **GpioCntlr** (so that the underlying driver functions can be called). + + - Define a custom structure. To the driver, the custom structure holds parameters and data. The **DeviceResourceIface** method provided by the HDF reads the values in the **gpio_config.hcs** file to initialize the members in the custom structure and passes important parameters, such as the GPIO group number and the number of pins, to the **GpioCntlr** object at the core layer. - - ``` - struct Pl061GpioCntlr { - struct GpioCntlr cntlr;// (Mandatory) Control object at the core layer. The details are as follows: - volatile unsigned char *regBase;// (Mandatory) Register base address. - uint32_t phyBase; // (Mandatory) Physical base address. - uint32_t regStep; // (Mandatory) Register offset step. - uint32_t irqStart; // (Mandatory) Enable interrupts. - uint16_t groupNum; // (Mandatory) Parameter of the GPIO port number. - uint16_t bitNum; // (Mandatory) Parameter of the GPIO port number. - uint8_t irqShare; // (Mandatory) Whether to share an interrupt. - struct Pl061GpioGroup *groups; // (Optional) Set based on the actual requirements. + ```c + // Define the GPIO group information. + struct Pl061GpioGroup { + struct GpioCntlr cntlr // (Mandatory) Control object of the core layer. + volatile unsigned char *regBase; // (Mandatory) Register base address. + unsigned int index; + unsigned int irq; + OsalIRQHandle irqFunc; + OsalSpinlock lock; + uint32_t irqSave; + bool irqShare; + struct PlatformDumper *dumper; + char *dumperName; }; - struct Pl061GpioGroup { // Register address, IRQ number and function, and lock. - volatile unsigned char *regBase; - unsigned int index; - unsigned int irq; - OsalIRQHandle irqFunc; - OsalSpinlock lock; + + struct Pl061GpioData { + volatile unsigned char *regBase; // (Mandatory) Register base address. + uint32_t phyBase; // (Mandatory) Physical base address. + uint32_t regStep;; // (Mandatory) Register offset step. + uint32_t irqStart; // (Mandatory) Enable interrupts. + uint16_t groupNum; // (Mandatory) Parameter of the GPIO port number. + uint16_t bitNum; // (Mandatory) Parameter of the GPIO port number. + uint8_t irqShare; // (Mandatory) Whether to share interrupt. + struct Pl061GpioGroup *groups; // (Optional) Set as required. + struct GpioInfo *gpioInfo; + void *priv; }; + struct GpioInfo { + struct GpioCntlr *cntlr; + char name[GPIO_NAME_LEN]; + OsalSpinlock spin; + uint32_t irqSave; + struct GpioIrqRecord *irqRecord; + }; // GpioCntlr is the controller structure at the core layer. The Init function assigns values to the members of GpioCntlr. struct GpioCntlr { - struct IDeviceIoService service; - struct HdfDeviceObject *device; - struct GpioMethod *ops; - struct DListHead list; - OsalSpinlock spin; - uint16_t start; - uint16_t count; - struct GpioInfo *ginfos; - void *priv; + struct PlatformDevice device; + struct GpioMethod *ops; + uint16_t start; + uint16_t count; + struct GpioInfo *ginfos; + bool isAutoAlloced; + void *priv; }; ``` - - Instantiating the **GpioMethod** structure in **GpioCntlr** (other members are initialized by **Init**) - - ``` - // The members of the GpioMethod structure are all callbacks. You need to implement the functions listed in Table 1. + - Instantiate the **GpioMethod** structure in **GpioCntlr**. + + ```c + // The members of the GpioMethod structure are hook functions. You need to implement them by referring to Table 1. static struct GpioMethod g_method = { .request = NULL, .release = NULL, - .write = Pl061GpioWrite, // Write the pin level. - .read = Pl061GpioRead, // Read the pin level. - .setDir = Pl061GpioSetDir, // Set the pin direction. - .getDir = Pl061GpioGetDir, // Obtain the pin direction. - .toIrq = NULL, - .setIrq = Pl061GpioSetIrq, // Set an IRQ for a pin. Skip it if this capability is not available. - .unsetIrq = Pl061GpioUnsetIrq, // Cancel the IRQ settings for a pin. Skip it if this capability is not available. - .enableIrq = Pl061GpioEnableIrq, // Enable interrupts for a pin. Skip it if this capability is not available. - .disableIrq = Pl061GpioDisableIrq,// Disable interrupts for a pin. Skip it if this capability is not available. + .write = Pl061GpioWrite, // Write the pin level. + .read = Pl061GpioRead, // Read the pin level. + .setDir = Pl061GpioSetDir, // Set the pin direction. + .getDir = Pl061GpioGetDir, // Obtain the pin direction. + .toIrq = NULL, + .setIrq = Pl061GpioSetIrq, // Set an IRQ function for a pin. Skip it if this capability is not available. + .unsetIrq = Pl061GpioUnsetIrq, // Cancel the IRQ function for a pin. Skip it if this capability is not available. + .enableIrq = Pl061GpioEnableIrq, // Enable interrupts for a pin. Skip it if this capability is not available. + .disableIrq = Pl061GpioDisableIrq, // Disable interrupts for a pin. Skip it if this capability is not available. }; ``` - - **Init** function + - Implement the **Init** function. - **Input parameter**: + Input parameters: - **HdfDeviceObject**, an interface parameter exposed by the driver, contains the .hcs information. + **HdfDeviceObject**, a device object created by the HDF for each driver, holds device-related private data and service APIs. - **Return value**: + Return value: - **HDF_STATUS**
The table below describes some status. For more information, see **HDF_STATUS** in the **/drivers/framework/include/utils/hdf_base.h** file. + **HDF_STATUS**
The table below describes some status. For more information, see **HDF_STATUS** in the **//drivers/hdf_core/framework/include/utils/hdf_base.h** file. + + **Table 2** HDF_STATUS - **Table 2** HDF_STATUS - | Status| Description| | -------- | -------- | | HDF_ERR_INVALID_OBJECT | Invalid controller object.| @@ -220,76 +259,185 @@ The following uses **gpio_hi35xx.c** as an example to present the information re | HDF_SUCCESS | Initialization successful.| | HDF_FAILURE | Initialization failed.| - **Function description**: + Function description: - Initializes the custom structure object and **GpioCntlr**, calls the **GpioCntlrAdd** function at the core layer, and (optional) connects to the virtual file system (VFS). + Initializes the custom structure object and **GpioCntlr**, calls **GpioCntlrAdd()** at the core layer, and (optional) accesses the virtual file system (VFS). + ```c + static struct Pl061GpioData g_pl061 = { + .groups = NULL, + .groupNum = PL061_GROUP_MAX, + .bitNum = PL061_BIT_MAX, + }; + + static int32_t Pl061GpioInitGroups(struct Pl061GpioData *pl061) + { + int32_t ret; + uint16_t i; + struct Pl061GpioGroup *groups = NULL; + + if (pl061 == NULL) { + return HDF_ERR_INVALID_PARAM; + } + + groups = (struct Pl061GpioGroup *)OsalMemCalloc(sizeof(*groups) * pl061->groupNum); + if (groups == NULL) { + return HDF_ERR_MALLOC_FAIL; + } + pl061->groups = groups; + + for (i = 0; i < pl061->groupNum; i++) { + // Initialize related information. + groups[i].index = i; + groups[i].regBase = pl061->regBase + i * pl061->regStep; + groups[i].irq = pl061->irqStart + i; + groups[i].irqShare = pl061->irqShare; + groups[i].cntlr.start = i * pl061->bitNum; + groups[i].cntlr.count = pl061->bitNum; + groups[i].cntlr.ops = &g_method; + groups[i].cntlr.ginfos = &pl061->gpioInfo[i * pl061->bitNum]; + + if ((ret = OsalSpinInit(&groups[i].lock)) != HDF_SUCCESS) { + goto ERR_EXIT; + } + + ret = GpioCntlrAdd(&groups[i].cntlr); // Add related information to the HDF core. + if (ret != HDF_SUCCESS) { + HDF_LOGE("%s: err add controller(%hu:%hu):%d", __func__, + groups[i].cntlr.start, groups[i].cntlr.count, ret); + (void)OsalSpinDestroy(&groups[i].lock); + goto ERR_EXIT; + } + } + return HDF_SUCCESS; + + ERR_EXIT: + while (i-- > 0) { + GpioCntlrRemove(&groups[i].cntlr); + (void)OsalSpinDestroy(&groups[i].lock); + } + pl061->groups = NULL; + OsalMemFree(groups); + return ret; + } - ``` static int32_t Pl061GpioInit(struct HdfDeviceObject *device) { - ... - struct Pl061GpioCntlr *pl061 = &g_pl061;// Use static global variables to complete initialization. - // static struct Pl061GpioCntlr g_pl061 = { - // .groups = NULL, - // .groupNum = PL061_GROUP_MAX, - // .bitNum = PL061_BIT_MAX, - //}; - ret = Pl061GpioReadDrs(pl061, device->property);// Use the attribute values read from the gpio_config.hcs file to initialize the members of the custom structure object. - ... - pl061->regBase = OsalIoRemap(pl061->phyBase, pl061->groupNum * pl061->regStep);// Create address mapping. - ... - ret = Pl061GpioInitCntlrMem(pl061); // Allocate memory. - ... - pl061->cntlr.count = pl061->groupNum * pl061->bitNum;// (Mandatory) Calculate the number of pins. - pl061->cntlr.priv = (void *)device->property; // (Mandatory) Store device attributes. - pl061->cntlr.ops = &g_method; // (Mandatory) Attach the GpioMethod instance. - pl061->cntlr.device = device; // (Mandatory) Prerequisites for conversion between HdfDeviceObject and GpioCntlr. - ret = GpioCntlrAdd(&pl061->cntlr); // (Mandatory) Call this function to fill the structure of the core layer. The driver accesses the platform core layer only after a success signal is returned. - ... - Pl061GpioDebugCntlr(pl061); - #ifdef PL061_GPIO_USER_SUPPORT // (Optional) Access the user-level VFS if it is supported. - if (GpioAddVfs(pl061->bitNum) != HDF_SUCCESS) { - HDF_LOGE("%s: add vfs fail!", __func__); - } - #endif - ... + int32_t ret; + struct Pl061GpioData *pl061 = &g_pl061; + + if (device == NULL || device->property == NULL) { + HDF_LOGE("%s: device or property null!", __func__); + return HDF_ERR_INVALID_OBJECT; + } + + pl061->gpioInfo = OsalMemCalloc(sizeof(struct GpioInfo) * GPIO_MAX_INFO_NUM); + if (pl061->gpioInfo == NULL) { + HDF_LOGE("%s: failed to calloc gpioInfo!", __func__); + return HDF_ERR_MALLOC_FAIL; + } + + ret = Pl061GpioReadDrs(pl061, device->property);// Use the attribute values read from the gpio_config.hcs file to initialize the members of the custom structure object. + if (ret != HDF_SUCCESS) { + HDF_LOGE("%s: failed to read drs:%d", __func__, ret); + return ret; + } + + if (pl061->groupNum > PL061_GROUP_MAX || pl061->groupNum <= 0 || + pl061->bitNum > PL061_BIT_MAX || pl061->bitNum <= 0) { + HDF_LOGE("%s: err groupNum:%hu, bitNum:%hu", __func__, pl061->groupNum, pl061->bitNum); + return HDF_ERR_INVALID_PARAM; + } + + pl061->regBase = OsalIoRemap(pl061->phyBase, pl061->groupNum * pl061->regStep);// Create address mapping. + if (pl061->regBase == NULL) { + HDF_LOGE("%s: err remap phy:0x%x", __func__, pl061->phyBase); + return HDF_ERR_IO; + } + + ret = Pl061GpioInitGroups(pl061); // Initialize the group information and add it to the HDF core layer. + if (ret != HDF_SUCCESS) { + HDF_LOGE("%s: err init groups:%d", __func__, ret); + OsalIoUnmap((void *)pl061->regBase); + pl061->regBase = NULL; + return ret; + } + pl061->priv = (void *)device->property; + device->priv = (void *)pl061; + Pl061GpioDebug(pl061); + + #ifdef PL061_GPIO_USER_SUPPORT + if (GpioAddVfs(pl061->bitNum) != HDF_SUCCESS) { + HDF_LOGE("%s: add vfs fail!", __func__); + } + #endif + HDF_LOGI("%s: dev service:%s init success!", __func__, HdfDeviceGetServiceName(device)); + return HDF_SUCCESS; } ``` - - **Release** function - **Input parameter**: + - Implement the **Release** function. + + Input parameters: - **HdfDeviceObject**, an interface parameter exposed by the driver, contains the .hcs configuration file information. + **HdfDeviceObject**, a device object created by the HDF for each driver, holds device-related private data and service APIs. - **Return value**: + Return value: No value is returned. - **Function description**: + Function description: - Releases the memory and deletes the controller. This function assigns values to the **Release** function in the driver entry structure. If the HDF fails to call the **Init** function to initialize the driver, the **Release** function can be called to release driver resources. + Releases the memory and deletes the controller. This function assigns values to **Release()** in the driver entry structure. If the HDF fails to call **Init()** to initialize the driver, **Release()** is called to release driver resources. - > ![icon-note.gif](public_sys-resources/icon-note.gif) **NOTE**
- > All forced conversion operations for obtaining the corresponding object can be successful only when **Init()** has the corresponding value assignment operations. + > ![icon-note.gif](public_sys-resources/icon-note.gif) **NOTE** + > + > All forced conversion operations for obtaining the corresponding object can be successful only when the **Init** function has the value assignment operations. - - ``` + ```c + static void Pl061GpioUninitGroups(struct Pl061GpioData *pl061) + { + uint16_t i; + struct Pl061GpioGroup *group = NULL; + + for (i = 0; i < pl061->groupNum; i++) { + group = &pl061->groups[i]; + GpioDumperDestroy(&pl061->groups[i]); + GpioCntlrRemove(&group->cntlr); // Remove from the HDF core layer. + } + + OsalMemFree(pl061->groups); + pl061->groups = NULL; + } + static void Pl061GpioRelease(struct HdfDeviceObject *device) { - struct GpioCntlr *cntlr = NULL; - struct Pl061GpioCntlr *pl061 = NULL; - ... - cntlr = GpioCntlrFromDevice(device);// (Mandatory) Obtain the control object at the core layer through forced conversion. - // return (device == NULL) ? NULL : (struct GpioCntlr *)device->service; - ... - #ifdef PL061_GPIO_USER_SUPPORT - GpioRemoveVfs();// Reverse operation of GpioAddVfs in Init. - #endif - GpioCntlrRemove(cntlr); // (Mandatory) Remove the device information and services from the core layer. - pl061 = ToPl061GpioCntlr(cntlr); // return (struct Pl061GpioCntlr *)cntlr; - Pl061GpioRleaseCntlrMem(pl061); // (Mandatory) Release the lock and memory. - OsalIoUnmap((void *)pl061->regBase);// (Mandatory) Unmap the addresses. - pl061->regBase = NULL; + struct Pl061GpioData *pl061 = NULL; + + HDF_LOGI("%s: enter", __func__); + if (device == NULL) { + HDF_LOGE("%s: device is null!", __func__); + return; + } + + #ifdef PL061_GPIO_USER_SUPPORT + GpioRemoveVfs(); + #endif + + pl061 = (struct Pl061GpioData *)device->priv; + if (pl061 == NULL) { + HDF_LOGE("%s: device priv is null", __func__); + return; + } + + Pl061GpioUninitGroups(pl061); + OsalMemFree(pl061->gpioInfo); + pl061->gpioInfo = NULL; + OsalIoUnmap((void *)pl061->regBase); + pl061->regBase = NULL; } ``` + +4. Debug the driver. + + (Optional) For new drivers, verify the basic functions, such as the GPIO status control and response to interrupts. diff --git a/en/device-dev/driver/driver-platform-mmc-develop.md b/en/device-dev/driver/driver-platform-mmc-develop.md index 6b9f16227b2c0968dccf04732d54852270fd0b69..e07e4a4e5313f70ead3cd7a5442b8b41e15817e0 100644 --- a/en/device-dev/driver/driver-platform-mmc-develop.md +++ b/en/device-dev/driver/driver-platform-mmc-develop.md @@ -1,50 +1,86 @@ # MMC - ## Overview -In the Hardware Driver Foundation (HDF), the MultiMedia Card (MMC) uses the independent service mode for API adaptation. In this mode, each device independently publishes a service to process external access requests. When receiving an access request, the HDF DeviceManager extracts parameters from the request to call the internal APIs of the target device. In the independent service mode, the HDF DeviceManager provides service management capabilities. However, you need to configure a node for each device, which increases memory usage. +### Function - **Figure 1** Independent service mode +A multimedia card (MMC) is a small-sized and large-capacity flash memory card used for solid-state non-volatile storage. - ![image](figures/independent-service-mode.png "MMC independent service mode") +Nowadays, MMC refers to a standard driver interface to solid-state storage cards. Memory devices that comply with this standard can be called MMCs. An MMC consists of the MMC controller, MMC bus, and memory card, which can be an MMC, Secure Digital (SD) card, Secure Digital Input Output (SDIO) card, or TransFlash (TF) card. +The MMC, SD, and SDIO buses have similar bus specifications, which have evolved from the MMC bus specifications. The MMC features multimedia storage; the SD focuses on security and data protection; the SDIO, evolving from the SD, provides the interface regardless of the specific form of the peer end (Wi-Fi, Bluetooth, or GPS device). -## Available APIs +### Basic Concepts -**MmcCntlrOps**: +- SD card + Introduced as an improvement over the MMC, the SD cards can protect their contents from erasure or modification, prevent unauthorized access, and protect copyrighted content using digital rights management. The size of a standard SD card is 24 mm x 32 mm x 2.1 mm, which is a little thicker than an MMC card. The SD cards are forward compatible with MMC cards, that is, all devices that support SD cards also support MMC cards. -``` +- SDIO + + SDIO is an interface designed as an extension for the SD card standard. It introduces the low-speed transfer standard, which supports low-speed I/O with the minimum hardware overhead. The SDIO interface is compatible with the SD cards. + +### Working Principles + +In the Hardware Driver Foundation (HDF), the MMC uses the independent service mode (see Figure 1) for API adaptation. In this mode, each device independently publishes a service to process external access requests. When receiving an access request, the HDF DeviceManager extracts parameters from the request to call the internal APIs of the target device. In the independent service mode, the HDF DeviceManager provides service management capabilities. However, you need to configure a node for each device, which increases memory usage. + +In the independent service mode, the core layer does not publish a service for the upper layer. Therefore, a service must be published for each controller. To achieve this purpose: + +- You need to implement the **Bind()** function in **HdfDriverEntry** to bind services. +- The **policy** field of **deviceNode** in the **device_info.hcs** file must be **1** or **2**, but not **0**. + +The MMC module is divided into the following layers: + +- Interface layer: provides APIs for opening an MMC device, checking whether the MMC controller has devices, and closing an MMC device. +- Core layer: provides the capabilities of adding or removing an MMC controller, performing device management, and providing common controller services. The core layer interacts with the adaptation layer through hook functions. +- Adaptation layer: instantiates the hook functions to implement specific features. + +**Figure 1** Independent service mode + +![img1](figures/independent-service-mode.png) + +## Development Guidelines + +### When to Use + +The MMC is used to store multimedia files. Before using your MMC device with OpenHarmony, you need to perform MMC driver adaptation. + +### Available APIs + +To enable the upper layer to successfully operate the MMC controller by calling the MMC APIs, hook functions are defined in **//drivers/hdf_core/framework/model/storage/include/mmc/mmc_corex.h** for the core layer. You need to implement these hook functions at the adaptation layer and hook them to implement the interaction between the interface layer and the core layer. + +**MmcCntlrOps**: + +```c struct MmcCntlrOps { - int32_t (*request)(struct MmcCntlr *cntlr, struct MmcCmd *cmd); - int32_t (*setClock)(struct MmcCntlr *cntlr, uint32_t clock); - int32_t (*setPowerMode)(struct MmcCntlr *cntlr, enum MmcPowerMode mode); - int32_t (*setBusWidth)(struct MmcCntlr *cntlr, enum MmcBusWidth width); - int32_t (*setBusTiming)(struct MmcCntlr *cntlr, enum MmcBusTiming timing); - int32_t (*setSdioIrq)(struct MmcCntlr *cntlr, bool enable); - int32_t (*hardwareReset)(struct MmcCntlr *cntlr); - int32_t (*systemInit)(struct MmcCntlr *cntlr); - int32_t (*setEnhanceStrobe)(struct MmcCntlr *cntlr, bool enable); - int32_t (*switchVoltage)(struct MmcCntlr *cntlr, enum MmcVolt volt); - bool (*devReadOnly)(struct MmcCntlr *cntlr); - bool (*devPlugged)(struct MmcCntlr *cntlr); - bool (*devBusy)(struct MmcCntlr *cntlr); - int32_t (*tune)(struct MmcCntlr *cntlr, uint32_t cmdCode); - int32_t (*rescanSdioDev)(struct MmcCntlr *cntlr); + int32_t (*request)(struct MmcCntlr *cntlr, struct MmcCmd *cmd); + int32_t (*setClock)(struct MmcCntlr *cntlr, uint32_t clock); + int32_t (*setPowerMode)(struct MmcCntlr *cntlr, enum MmcPowerMode mode); + int32_t (*setBusWidth)(struct MmcCntlr *cntlr, enum MmcBusWidth width); + int32_t (*setBusTiming)(struct MmcCntlr *cntlr, enum MmcBusTiming timing); + int32_t (*setSdioIrq)(struct MmcCntlr *cntlr, bool enable); + int32_t (*hardwareReset)(struct MmcCntlr *cntlr); + int32_t (*systemInit)(struct MmcCntlr *cntlr); + int32_t (*setEnhanceStrobe)(struct MmcCntlr *cntlr, bool enable); + int32_t (*switchVoltage)(struct MmcCntlr *cntlr, enum MmcVolt volt); + bool (*devReadOnly)(struct MmcCntlr *cntlr); + bool (*devPlugged)(struct MmcCntlr *cntlr); + bool (*devBusy)(struct MmcCntlr *cntlr); + int32_t (*tune)(struct MmcCntlr *cntlr, uint32_t cmdCode); + int32_t (*rescanSdioDev)(struct MmcCntlr *cntlr); }; ``` - **Table 1** Description of callback functions in MmcCntlrOps +**Table 1** Hook functions in **MmcCntlrOps** | Function| Input Parameter| Return Value| Description| | -------- | -------- | -------- | -------- | | doRequest | **cntlr**: structure pointer to the MMC controller at the core layer.
**cmd**: structure pointer to the command to execute.| HDF_STATUS| Processes the request.| | setClock | **cntlr**: structure pointer to the MMC controller at the core layer.
**clock**: clock frequency to set.| HDF_STATUS| Sets the clock frequency.| -| setPowerMode | **cntlr**: structure pointer to the MMC controller at the core layer.
**mode**: power consumption mode, which is an enumerated value.| HDF_STATUS| Sets the power consumption mode.| -| setBusWidth | **cntlr**: structure pointer to the MMC controller at the core layer.
**width**: bus width, which is an enumerated value.| HDF_STATUS| Sets the bus width.| -| setBusTiming | **cntlr**: structure pointer to the MMC controller at the core layer.
**timing**: bus timing, which is an enumerated value.| HDF_STATUS| Sets the bus timing.| -| setSdioIrq | **cntlr**: structure pointer to the MMC controller at the core layer.
**enable**: whether to enable Secure Digital Input Output (SDIO) interrupts.| HDF_STATUS| Enables or disables SDIO interrupts.| +| setPowerMode | **cntlr**: structure pointer to the MMC controller at the core layer.
**mode**: power consumption mode. For details, see **MmcPowerMode**.| HDF_STATUS| Sets the power consumption mode.| +| setBusWidth | **cntlr**: structure pointer to the MMC controller at the core layer.
**width**: bus width. For details, see **MmcBusWidth**.| HDF_STATUS| Sets the bus width.| +| setBusTiming | **cntlr**: structure pointer to the MMC controller at the core layer.
**timing**: bus timing. For details, see **MmcBusTiming**.| HDF_STATUS| Sets the bus timing.| +| setSdioIrq | **cntlr**: structure pointer to the MMC controller at the core layer.
**enable**: whether to enable SDIO interrupts.| HDF_STATUS| Enables or disables SDIO interrupts.| | hardwareReset | **cntlr**: structure pointer to the MMC controller at the core layer.| HDF_STATUS| Resets hardware.| | systemInit | **cntlr**: structure pointer to the MMC controller at the core layer.| HDF_STATUS| Performs system initialization.| | setEnhanceStrobe | **cntlr**: structure pointer to the MMC controller at the core layer.
**enable**: whether to enable the enhanced strobe feature.| HDF_STATUS| Sets the enhanced strobe feature.| @@ -52,170 +88,161 @@ struct MmcCntlrOps { | devReadOnly | **cntlr**: structure pointer to the MMC controller at the core layer.| Boolean value| Checks whether the device is read-only.| | cardPlugged | **cntlr**: structure pointer to the MMC controller at the core layer.| Boolean value| Checks whether the device is removed.| | devBusy | **cntlr**: structure pointer to the MMC controller at the core layer.| Boolean value| Checks whether the device is being used.| -| tune | **cntlr**: structure pointer to the MMC controller at the core layer.
**cmdCode**: command code of the uint32_t type.| HDF_STATUS| Tunes the oscillator circuit frequency.| +| tune | **cntlr**: structure pointer to the MMC controller at the core layer.
**cmdCode**: command code, which is of the uint32_t type.| HDF_STATUS| Tunes the oscillator circuit frequency.| | rescanSdioDev | **cntlr**: structure pointer to the MMC controller at the core layer.| HDF_STATUS| Scans and adds an SDIO device.| +### How to Develop -## How to Develop - -The MMC module adaptation involves the following steps: +The MMC module adaptation procedure is as follows: 1. Instantiate the driver entry. - - Instantiate the **HdfDriverEntry** structure. - - Call **HDF_INIT** to register the **HdfDriverEntry** instance with the HDF. - 2. Configure attribute files. - - Add the **deviceNode** information to the **device_info.hcs** file. - - (Optional) Add the **mmc_config.hcs** file. - 3. Instantiate the MMC controller object. - - Initialize **MmcCntlr**. - - Instantiate **MmcCntlrOps** in the **MmcCntlr** object. - > ![icon-note.gif](public_sys-resources/icon-note.gif) **NOTE**
- > For details about the functions in **MmcCntlrOps**, see [Available APIs](#available-apis). - 4. Debug the driver. - (Optional) For new drivers, verify the basic functions, for example, check the information returned after the **MmcCntlrOps** instance is attached and whether the device starts successfully. - - -## Development Example +### Example -The following uses **himci.c** as an example to present the information required for implementing device functions. +The following uses the **//device_soc_hisilicon/common/platform/mmc/himci_v200/himci.c** driver of the Hi3516D V300 development board as an example to describe the driver adaptation. 1. Instantiate the driver entry. The driver entry must be a global variable of the **HdfDriverEntry** type (defined in **hdf_device_desc.h**), and the value of **moduleName** must be the same as that in **device_info.hcs**. In the HDF, the start address of each **HdfDriverEntry** object of all loaded drivers is collected to form a segment address space similar to an array for the upper layer to invoke. - Generally, the HDF calls the **Bind** function and then the **Init** function to load a driver. If **Init** fails to be called, the HDF calls **Release** to release driver resources and exit. - MMC driver entry example: - - ``` + MMC driver entry example: + + ```c struct HdfDriverEntry g_mmcDriverEntry = { .moduleVersion = 1, - .Bind = HimciMmcBind, // See the Bind function. - .Init = HimciMmcInit, // See the Init function. - .Release = HimciMmcRelease, // See the Release function. - .moduleName = "hi3516_mmc_driver",// (Mandatory) The value must be the same as that of moduleName in the .hcs file. + .Bind = HimciMmcBind, // See the Bind function. + .Init = HimciMmcInit, // See the Init function. + .Release = HimciMmcRelease, // See the Release function. + .moduleName = "hi3516_mmc_driver", // (Mandatory) The value must be the same as that of moduleName in the .hcs file. }; - HDF_INIT(g_mmcDriverEntry); // Call HDF_INIT to register the driver entry with the HDF. + HDF_INIT(g_mmcDriverEntry); // Call HDF_INIT to register the driver entry with the HDF. ``` -2. Add the **deviceNode** information to the **device_info.hcs** file and configure the device attributes in the **mmc_config.hcs** file. - - The **deviceNode** information is related to registration of the driver entry. The device attribute values are closely related to the default values or value ranges of the **MmcCntlr** members at the core layer. - - If there are multiple devices, you need to add the **deviceNode** information to the **device_info** file and add the device attributes to the **mmc_config** file for each device. - - - **device_info.hcs** configuration example - - - ``` - root { - device_info { - match_attr = "hdf_manager"; - platform :: host { - hostName = "platform_host"; - priority = 50; - device_mmc:: device { - device0 :: deviceNode { - policy = 2; - priority = 10; - permission = 0644; - moduleName = "hi3516_mmc_driver"; // (Mandatory) Driver name, which must be the same as moduleName in the driver entry. - serviceName = "HDF_PLATFORM_MMC_0"; // (Mandatory) Unique name of the service published by the driver. - deviceMatchAttr = "hi3516_mmc_emmc";// (Mandatory) Private data of the controller. The value must be the same as the controller information in mmc_config.hcs. - } - device1 :: deviceNode { - policy = 1; - priority = 20; - permission = 0644; - moduleName = "hi3516_mmc_driver"; - serviceName = "HDF_PLATFORM_MMC_1"; - deviceMatchAttr = "hi3516_mmc_sd"; // The MMC is an SD card. - } - device2 :: deviceNode { - policy = 1; - priority = 30; - permission = 0644; - moduleName = "hi3516_mmc_driver"; - serviceName = "HDF_PLATFORM_MMC_2"; - deviceMatchAttr = "hi3516_mmc_sdio";// The MMC is an SDIO card. - } - } - } - } - } - ``` - - - **mmc_config.hcs** configuration example - - - ``` - root { - platform { - mmc_config { - template mmc_controller { // Template configuration. In the template, you can configure the common parameters shared by device nodes. - match_attr = ""; - voltDef = 0; // 3.3V - freqMin = 50000; // (Mandatory) Minimum frequency - freqMax = 100000000; // (Mandatory) Maximum frequency - freqDef = 400000; // (Mandatory) Default frequency - maxBlkNum = 2048; // (Mandatory) Maximum block number - maxBlkSize = 512; // (Mandatory) Maximum number of blocks - ocrDef = 0x300000; // (Mandatory) Working voltage. - caps2 = 0; // (Mandatory) Attribute register. For details, see MmcCaps2 in mmc_caps.h. - regSize = 0x118; // (Mandatory) Register bit width - hostId = 0; // (Mandatory) Host ID - regBasePhy = 0x10020000;// (Mandatory) Physical base address of the register - irqNum = 63; // (Mandatory) Interrupt number - devType = 2; // (Mandatory) Device type, which can be eMMC, SD, SDIO, or COMBO. - caps = 0x0001e045; // (Mandatory) Attribute register. For details, see MmcCaps in mmc_caps.h. - } - controller_0x10100000 :: mmc_controller { - match_attr = "hi3516_mmc_emmc";// (Mandatory) The value must be the same as that of deviceMatchAttr in device_info.hcs. - hostId = 0; - regBasePhy = 0x10100000; - irqNum = 96; - devType = 0; // The MMC is an eMMC card. - caps = 0xd001e045; - caps2 = 0x60; - } - controller_0x100f0000 :: mmc_controller { - match_attr = "hi3516_mmc_sd"; - hostId = 1; - regBasePhy = 0x100f0000; - irqNum = 62; - devType = 1; // The MMC is an SD card. - caps = 0xd001e005; - } - controller_0x10020000 :: mmc_controller { - match_attr = "hi3516_mmc_sdio"; - hostId = 2; - regBasePhy = 0x10020000; - irqNum = 63; - devType = 2; // The MMC is an SDIO card. - caps = 0x0001e04d; - } - } - } - } - ``` - -3. Initialize the **MmcCntlr** object at the core layer, including defining a custom structure (to pass parameters and data) and implementing the **HdfDriverEntry** member functions (**Bind**, **Init**, and **Release**) to instantiate **MmcCntlrOps** in **MmcCntlr** (so that the underlying driver functions can be called). - - - Defining a custom structure +2. Configure attribute files. - To the driver, the custom structure holds parameters and data. The **DeviceResourceIface** method provided by the HDF reads the values in the **mmc_config.hcs** file to initialize the members in the custom structure and passes important parameters to the **MmcCntlr** object at the core layer. + Add the deviceNode information to the **device_info.hcs** file. The deviceNode information is related to the driver entry registration. The following example uses three MMC controllers as an example. If there are more MMC controllers, add the deviceNode information to the **device_info.hcs** file for each controller. The device attribute values configured in **mmc_config.hcs** are closely related to the default values or value ranges of the **MmcCntlr** members at the core layer. + + - **device_info.hcs** example: + + Add the deviceNode information to the **//vendor/hisilicon/hispark_taurus/hdf_config/device_info/device_info.hcs** file. + + ```c + root { + device_info { + match_attr = "hdf_manager"; + platform :: host { + hostName = "platform_host"; + priority = 50; + device_mmc:: device { + device0 :: deviceNode { // DeviceNode of the driver. + policy = 2; // The value 2 means to publish services for both kernel- and user-mode processes. + priority = 10; // Driver startup priority. + permission = 0644; // Permission for the device node created. + moduleName = "hi3516_mmc_driver"; // (Mandatory) Driver name, which must be the same as moduleName in the driver entry. + serviceName = "HDF_PLATFORM_MMC_0"; // (Mandatory) Unique name of the service published by the driver. + deviceMatchAttr = "hi3516_mmc_emmc"; // (Mandatory) Private data of the controller. The value must be the same as the controller information in mmc_config.hcs. + } + device1 :: deviceNode { + policy = 1; + priority = 20; + permission = 0644; + moduleName = "hi3516_mmc_driver"; + serviceName = "HDF_PLATFORM_MMC_1"; + deviceMatchAttr = "hi3516_mmc_sd"; // The MMC is an SD card. + } + device2 :: deviceNode { + policy = 1; + priority = 30; + permission = 0644; + moduleName = "hi3516_mmc_driver"; + serviceName = "HDF_PLATFORM_MMC_2"; + deviceMatchAttr = "hi3516_mmc_sdio"; // The MMC is an SDIO card. + } + ... + } + } + } + } + ``` - + - **mmc_config.hcs** example + + Configure the device attributes in the **//device/soc/hisilicon/hi3516dv300/sdk_liteos/hdf_config/mmc/mmc_config.hcs** file. The parameters are as follows: + + ```c + root { + platform { + mmc_config { + template mmc_controller { // Template configuration. If the template is used to configure device node information, the default values in the template will be used for the fields that are not declared for the node. + match_attr = ""; + voltDef = 0; // MMC default voltage. The value 0 stands for 3.3 V, 1 for 1.8 V, and 2 for 1.2 V. + freqMin = 50000; // (Mandatory) Minimum frequency. + freqMax = 100000000; // (Mandatory) Maximum frequency. + freqDef = 400000; // (Mandatory) Default frequency. + maxBlkNum = 2048; // (Mandatory) Maximum block number. + maxBlkSize = 512; // (Mandatory) Maximum block size. + ocrDef = 0x300000; // (Mandatory) working voltage. + caps2 = 0; // (Mandatory) Attribute register. For details, see MmcCaps2 in mmc_caps.h. + regSize = 0x118; // (Mandatory) Register size. + hostId = 0; // (Mandatory) Host number. + regBasePhy = 0x10020000; // (Mandatory) Physical base address of the register. + irqNum = 63; // (Mandatory) IRQ number. + devType = 2; // (Mandatory) Device type, which can be eMMC, SD, SDIO, or COMBO. + caps = 0x0001e045; // (Mandatory) Attribute register. For details, see MmcCaps in mmc_caps.h. + } + controller_0x10100000 :: mmc_controller { + match_attr = "hi3516_mmc_emmc"; // (Mandatory) The value must be the same as deviceMatchAttr in device_info.hcs. + hostId = 0; + regBasePhy = 0x10100000; + irqNum = 96; + devType = 0; // The device is an eMMC card. + caps = 0xd001e045; + caps2 = 0x60; + } + controller_0x100f0000 :: mmc_controller { + match_attr = "hi3516_mmc_sd"; + hostId = 1; + regBasePhy = 0x100f0000; + irqNum = 62; + devType = 1; // The device is an SD card. + caps = 0xd001e005; + } + controller_0x10020000 :: mmc_controller { + match_attr = "hi3516_mmc_sdio"; + hostId = 2; + regBasePhy = 0x10020000; + irqNum = 63; + devType = 2; // The device is an SDIO card. + caps = 0x0001e04d; + } + } + } + } + ``` + + After the **mmc_config.hcs** file is configured, include the file in the **hdf.hcs** file. Otherwise, the configuration file cannot take effect. + + ```c + #include "../../../../device/soc/hisilicon/hi3516dv300/sdk_liteos/hdf_config/mmc/mmc_config.hcs" // Relative path of the file. ``` + +3. Instantiate the MMC controller object. + + Initialize the **MmcCntlr** object at the core layer, including defining a custom structure (to pass parameters and data) and implementing the **HdfDriverEntry** member functions (**Bind**, **Init** and **Release**) to instantiate **MmcCntlrOps** in **MmcCntlr** (so that the underlying driver functions can be called). + + - Define a custom structure. + + To the driver, the custom structure holds parameters and data. The **DeviceResourceIface** method provided by the HDF reads the values in the **mmc_config.hcs** file to initialize the members in the custom structure and passes important parameters to the **MmcCntlr** object at the core layer. + + ```c struct HimciHost { - struct MmcCntlr *mmc;// (Mandatory) Core layer structure - struct MmcCmd *cmd; // (Mandatory) Core layer structure used to pass commands. For details about related commands, see MmcCmdCode. - //(Optional) Set parameters based on actual requirements. - void *base; + struct MmcCntlr *mmc; // (Mandatory) Core layer control object. + struct MmcCmd *cmd // (Mandatory) Core layer structure used to pass commands. For details about related commands, see MmcCmdCode. + void *base; // Register base address used for address mapping. enum HimciPowerStatus powerStatus; uint8_t *alignedBuff; uint32_t buffLen; @@ -260,37 +287,39 @@ The following uses **himci.c** as an example to present the information required }; ``` - - Instantiating **MmcCntlrOps** in **MmcCntlr** (other members are initialized by **Bind**) + - Instantiate **MmcCntlrOps** in **MmcCntlr**. - - ``` + ```c static struct MmcCntlrOps g_himciHostOps = { - .request = HimciDoRequest, - .setClock = HimciSetClock, - .setPowerMode = HimciSetPowerMode, - .setBusWidth = HimciSetBusWidth, - .setBusTiming = HimciSetBusTiming, - .setSdioIrq = HimciSetSdioIrq, - .hardwareReset = HimciHardwareReset, - .systemInit = HimciSystemInit, - .setEnhanceStrobe= HimciSetEnhanceStrobe, - .switchVoltage = HimciSwitchVoltage, - .devReadOnly = HimciDevReadOnly, - .devPlugged = HimciCardPlugged, - .devBusy = HimciDevBusy, - .tune = HimciTune, - .rescanSdioDev = HimciRescanSdioDev, + .request = HimciDoRequest, + .setClock = HimciSetClock, + .setPowerMode = HimciSetPowerMode, + .setBusWidth = HimciSetBusWidth, + .setBusTiming = HimciSetBusTiming, + .setSdioIrq = HimciSetSdioIrq, + .hardwareReset = HimciHardwareReset, + .systemInit = HimciSystemInit, + .setEnhanceStrobe = HimciSetEnhanceStrobe, + .switchVoltage = HimciSwitchVoltage, + .devReadOnly = HimciDevReadOnly, + .devPlugged = HimciCardPlugged, + .devBusy = HimciDevBusy, + .tune = HimciTune, + .rescanSdioDev = HimciRescanSdioDev, }; ``` - - **Bind** function + + - Implement the **Bind** function. **Input parameter**: - **HdfDeviceObject**, an interface parameter exposed by the driver, contains the .hcs information. + **HdfDeviceObject**, a device object created by the HDF for each driver, holds device-related private data and service APIs. **Return value**: - **HDF_STATUS**
The table below describes some status. For more information, see **HDF_STATUS** in the **/drivers/framework/include/utils/hdf_base.h** file. + **HDF_STATUS**
The table below describes some status. For more information, see **HDF_STATUS** in the **//drivers/hdf_core/framework/include/utils/hdf_base.h** file. + + **Table 2** HDF_STATUS description | Status| Description| | -------- | -------- | @@ -303,10 +332,9 @@ The following uses **himci.c** as an example to present the information required **Function description**: - Initializes the custom structure **HimciHost** object and **MmcCntlr**, and calls the **MmcCntlrAdd** function at the core layer. **MmcCntlr**, **HimciHost**, and **HdfDeviceObject** assign values with each other so that other functions can be converted successfully. + Initializes the **HimciHost** object and **MmcCntlr**, and calls the **MmcCntlrAdd** function at the core layer to add the MMC controllers. - - ``` + ```c static int32_t HimciMmcBind(struct HdfDeviceObject *obj) { struct MmcCntlr *cntlr = NULL; @@ -315,34 +343,34 @@ The following uses **himci.c** as an example to present the information required cntlr = (struct MmcCntlr *)OsalMemCalloc(sizeof(struct MmcCntlr)); host = (struct HimciHost *)OsalMemCalloc(sizeof(struct HimciHost)); - host->mmc = cntlr; // (Mandatory) Prerequisites for conversion between HimciHost and MmcCntlr. - cntlr->priv = (void *)host; // (Mandatory) Prerequisites for conversion between HimciHost and MmcCntlr. - cntlr->ops = &g_himciHostOps; // (Mandatory) Attach the MmcCntlrOps instance. - cntlr->hdfDevObj = obj; // (Mandatory) Prerequisites for conversion between HdfDeviceObject and MmcCntlr. - obj->service = &cntlr->service; // (Mandatory) Prerequisites for conversion between HdfDeviceObject and MmcCntlr. - ret = MmcCntlrParse(cntlr, obj); // (Mandatory) Initialize MmcCntlr. If the initialization fails, execute goto _ERR. - ... - ret = HimciHostParse(host, obj); // (Mandatory) Initialize HimciHost. If the initialization fails, execute goto _ERR. + host->mmc = cntlr; // (Mandatory) Prerequisites for conversion between HimciHost and MmcCntlr. + cntlr->priv = (void *)host; // (Mandatory) Prerequisites for conversion between HimciHost and MmcCntlr. + cntlr->ops = &g_himciHostOps; // (Mandatory) Attach the MmcCntlrOps instance to MmcCntlr. + cntlr->hdfDevObj = obj; // (Mandatory) Prerequisites for conversion between HdfDeviceObject and MmcCntlr. + obj->service = &cntlr->service; // (Mandatory) Prerequisites for conversion between HdfDeviceObject and MmcCntlr. + ret = MmcCntlrParse(cntlr, obj); // (Mandatory) Initialize MmcCntlr. If the initialization fails, execute goto _ERR. + ... + ret = HimciHostParse(host, obj); // (Mandatory) Initialize HimciHost. If the initialization fails, execute goto _ERR. ... - ret = HimciHostInit(host, cntlr); // Customized initialization. If the initialization fails, execute goto _ERR. + ret = HimciHostInit(host, cntlr); // Customized initizlization. If the initialization fails, goto _ERR. ... - ret = MmcCntlrAdd(cntlr); // Call the functions at the core layer. If the operation fails, execute goto _ERR. + ret = MmcCntlrAdd(cntlr); // Call MmcCntlrAdd at the core layer. If the operation fails, execute goto _ERR. ... - (void)MmcCntlrAddDetectMsgToQueue(cntlr);// Add the card detection message to the queue. + (void)MmcCntlrAddDetectMsgToQueue(cntlr); // Add the card detection message to the queue. HDF_LOGD("HimciMmcBind: success."); return HDF_SUCCESS; - _ERR: + ERR: HimciDeleteHost(host); HDF_LOGD("HimciMmcBind: fail, err = %d.", ret); return ret; } ``` - - **Init** function + - Implement the **Init** function. **Input parameter**: - **HdfDeviceObject**, an interface parameter exposed by the driver, contains the .hcs information. + **HdfDeviceObject**, a device object created by the HDF for each driver, holds device-related private data and service APIs. **Return value**: @@ -352,8 +380,7 @@ The following uses **himci.c** as an example to present the information required Implements **ProcMciInit**. - - ``` + ```c static int32_t HimciMmcInit(struct HdfDeviceObject *obj) { static bool procInit = false; @@ -368,11 +395,12 @@ The following uses **himci.c** as an example to present the information required return HDF_SUCCESS; } ``` - - **Release** function + + - Implement the **Release** function. **Input parameter**: - **HdfDeviceObject**, an interface parameter exposed by the driver, contains the .hcs information. + **HdfDeviceObject**, a device object created by the HDF for each driver, holds device-related private data and service APIs. **Return value**: @@ -380,19 +408,22 @@ The following uses **himci.c** as an example to present the information required **Function description**: - Releases the memory and deletes the controller. This function assigns values to the **Release** function in the driver entry structure. If the HDF fails to call the **Init** function to initialize the driver, the **Release** function can be called to release driver resources. + Releases the memory and deletes the controller. This function assigns values to **Release()** in the driver entry structure. If the HDF fails to call **Init()** to initialize the driver, **Release()** is called to release driver resources. > ![icon-note.gif](public_sys-resources/icon-note.gif) **NOTE**
> All forced conversion operations for obtaining the corresponding object can be successful only when **Init()** has the corresponding value assignment operations. - - static void HimciMmcRelease(struct HdfDeviceObject *obj) + ```c + static void HimciMmcRelease(struct HdfDeviceObject *obj) { struct MmcCntlr *cntlr = NULL; ... - cntlr = (struct MmcCntlr *)obj->service; // Forcibly convert HdfDeviceObject to MmcCntlr by using service. For details about the value assignment, see the Bind function. + cntlr = (struct MmcCntlr *)obj->service; // Forcibly convert HdfDeviceObject to MmcCntlr by using service. For details about the value assignment, see the Bind function. ... - HimciDeleteHost((struct HimciHost *)cntlr->priv);// Customized memory release function. A forced conversion from MmcCntlr to HimciHost is involved in the process. + HimciDeleteHost((struct HimciHost *)cntlr->priv); // Memory release function customized. Forced conversion between MmcCntlr and HimciHost is involved. } + ``` +4. Debug the driver. + (Optional) For new drivers, verify basic functions, for example, check the information returned after the driver is attached and whether data is successfully transmitted. diff --git a/en/device-dev/driver/driver-platform-pin-des.md b/en/device-dev/driver/driver-platform-pin-des.md index 607807085a5334b9b975e840c44406ad3992ad06..fc520123f52559e08bea90ec8659553c21f4976d 100644 --- a/en/device-dev/driver/driver-platform-pin-des.md +++ b/en/device-dev/driver/driver-platform-pin-des.md @@ -1,103 +1,106 @@ -# Pin +# Pin +## Overview -## Overview +### Function -### Pin +The pin module, also called pin controller, manages pin resources of the system on a chip (SoC) and implements the pin multiplexing function. -The pin module, also called pin controller, manages pin resources of system on a chip (SoC) vendors and provides the pin multiplexing function. -The module defines a set of common methods for managing pins, including: - - Obtaining or releasing the pin description handle: The kernel compares the pin name passed in with the pin names of each controller in the linked list. If a match is found, a pin description handle is obtained. After the operation on the pin is complete, the pin description handle will be released. -- Setting or obtaining the pull type of a pin: The pull type can be pull-up, pull-down, or floating. -- Setting or obtaining the pull strength of a pin: You can set the pull strength as required. -- Setting or obtaining the functions of a pin to implement pin multiplexing +The pin module provides a set of APIs for pin management, including: -### Basic Concepts -Pin, as a software concept, provides APIs for uniformly managing the pins from different SoC vendors, providing the pin multiplexing function, and configuring the electrical features of pins. +- Obtaining or releasing a pin description handle +- Setting or obtaining the pull type (pull-up, pull-down, or floating) of a pin +- Setting or obtaining the pull strength of a pin +- Setting or obtaining the function of a pin to implement pin multiplexing + +### Basic Concepts + +Pin is a software concept designed to uniformly manage SoC pins, implement pin multiplexing, and set electrical features of pins. - SoC - An SOC is a chip that integrates microprocessors, analog IP cores, digital IP cores, and memory for specific purposes. + An SoC is a chip that integrates microprocessors, analog IP cores, digital IP cores, and memory for specific purposes. - Pin multiplexing - When the number of pins of a chip cannot handle the increasing connection requests, you can set the software registers to make the pins to work in different states. + When the number of pins of a chip cannot handle the increasing connection requests, you can set software registers to make the pins to work in different states. + +### Working Principles -### Working Principles +In the Hardware Driver Foundation (HDF), the pin module uses the unified service mode for API adaptation. In this mode, a device service is used as the pin manager to handle access requests from the devices of the same type in a unified manner. The unified service mode applies to the scenario where there are many device objects of the same type. If the independent service mode is used in this case, more device nodes need to be configured and more memory resources will be consumed. -In the HDF, the pin module does not support the user mode and therefore does not need to publish services. It uses the service-free mode in interface adaptation. The service-free mode applies to the devices that do not provide user-mode APIs or the OS that does not distinguish the user mode and the kernel mode. The **DevHandle**, a void pointer, directly points to the kernel mode address of the device object. +In the unified service mode, the core layer manages all controllers in a unified manner and publishes a service for the interface layer. That is, the driver does not need to publish a service for each controller. The pin module is divided into the following layers: -- Interface layer: provides APIs for obtaining a pin, setting or obtaining the pull type, pull strength, and functions of a pin, and releasing a pin. -- Core layer: provides the capabilities of matching pin resources and adding, removing, and managing pin controllers. The core layer interacts with the adaptation layer by using hooks. -- Adaptation layer: instantiates hooks to implement specific functions. -**Figure 1** Service-free mode -![](figures/service-free-mode.png "pin service-free mode") +- Interface layer: provides APIs for obtaining a pin, setting or obtaining the pull type, pull strength, and function of a pin, and releasing a pin. +- Core layer: provides the capabilities of matching pin resources, adding and removing a pin controller, and managing pins. The core layer interacts with the adaptation layer through hook functions. +- Adaptation layer: instantiates the hook functions to implement specific features. -### Constraints +**Figure 1** Unified service mode +![Unified service mode](figures/unified-service-mode.png) - Currently, the pin module supports only the kernels (LiteOS) of mini and small systems. +### Constraints - ## Usage Guidelines +The pin module supports only the LiteOS-A kernel of the small system. - ### When to Use +## Usage Guidelines - The pin module is a software concept and is used to manage pin resources. You can set the functions, pull type, and pull strength of pins to implement pin multiplexing. +### When to Use -### Available APIs +The pin module is a software concept used to manage pin resources. You can set the function, pull type, and pull strength of pins to implement pin multiplexing. -The table below describes the APIs of the pin module. For more details, see API Reference. +### Available APIs + +The following table describes the APIs of the pin module. **Table 1** Pin driver APIs - -| **API** | **Description** | +| **API** | **Description** | | ------------------------------------------------------------ | ---------------- | -| DevHandle PinGet(const char *pinName); | Obtains the pin description handle.| -| void PinPut(DevHandle handle); | Releases the pin description handle.| -| int32_t PinSetPull(DevHandle handle, enum PinPullType pullType); | Sets the pull type of a pin.| -| int32_t PinGetPull(DevHandle handle, enum PinPullType *pullType); | Obtains the pull type of a pin.| -| int32_t PinSetStrength(DevHandle handle, uint32_t strength); | Sets the pull strength of a pin.| -| int32_t PinGetStrength(DevHandle handle, uint32_t *strength); | Obtains the pull strength of a pin.| -| int32_t PinSetFunc(DevHandle handle, const char *funcName); | Sets the pin function. | -| int32_t PinGetFunc(DevHandle handle, const char **funcName); | Obtains the pin functions. | +| DevHandle PinGet(const char *pinName) | Obtains a pin description handle.| +| void PinPut(DevHandle handle) | Releases the pin description handle.| +| int32_t PinSetPull(DevHandle handle, enum PinPullType pullType) | Sets the pull type of a pin.| +| int32_t PinGetPull(DevHandle handle, enum PinPullType *pullType) | Obtains the pull type of a pin.| +| int32_t PinSetStrength(DevHandle handle, uint32_t strength) | Sets the pull strength of a pin.| +| int32_t PinGetStrength(DevHandle handle, uint32_t *strength) | Obtains the pull strength of a pin.| +| int32_t PinSetFunc(DevHandle handle, const char *funcName) | Sets the pin function.| +| int32_t PinGetFunc(DevHandle handle, const char **funcName) | Obtains the pin function. | ->![](../public_sys-resources/icon-note.gif) **NOTE**
->All APIs described in this document can be called only in the kernel space. +>![](../public_sys-resources/icon-note.gif) **NOTE** +> +>All the pin APIs described in this document can be used in kernel mode and user mode. -### How to Develop +### How to Develop -The figure below illustrates how to use the APIs. +The following figure illustrates how to use pin driver APIs. - **Figure 2** Using the pin driver APIs - + **Figure 2** Using pin driver APIs
![](figures/using-pin-process.png "Process of using the pin module") -#### Obtaining the Pin Description Handle +#### Obtaining a Pin Description Handle -Before performing an operation on a pin, call **PinGet** to obtain the pin description handle. This API returns the pin description handle that matches the input pin name. +Before performing an operation on a pin, use **PinGet()** to obtain the pin description handle based on the pin name. -``` +```c DevHandle PinGet(const char *pinName); ``` **Table 2** Description of PinGet - - | Parameter | Description | | ---------- | ----------------------- | | pinName | Pointer to the pin name. | -| **Return Value**| **Description** | -| NULL | Failed to obtain the pin description handle.| -| handle | Pin description handle obtained. | +| **Return Value**| **Description** | +| NULL | The operation fails.| +| handle | The operation is successful. The pin description handle obtained is returned. | -Example: Obtain the handle of P18. +Example: Obtain the pin description handle of P18. -``` -DevHandle handle = NULL; /* Pin description handle */ -char pinName = "P18"; /* Pin name. */ +```c +DevHandle handle = NULL; // Pin description handle. + +char pinName = "P18"; // Pin name. handle = PinGet(pinName); if (handle == NULL) { HDF_LOGE("PinGet: get handle failed!\n"); @@ -107,29 +110,28 @@ if (handle == NULL) { #### Setting the Pull Type of a Pin -Call **PinSetPull** to set the pull type of a pin. +Use **PinSetPull()** to set the pull type of a pin. -``` +```c int32_t PinSetPull(DevHandle handle, enum PinPullType pullType); ``` **Table 3** Description of PinSetPull - - -| Parameter | Description | +| Parameter | Description | | ---------- | ----------------------- | | handle | Pin description handle. | | pullType | Pull type to set. | -| **Return Value**| **Description** | +| **Return Value**| **Description** | | 0 | The operation is successful.| -| Negative value | The operation failed.| +| Negative value | The operation fails.| Example: Set the pull type to pull-up. -``` +```c int32_t ret; enum PinPullType pullTypeNum; + /* Set the pull type of a pin. */ pullTypeNum = 1; ret = PinSetPull(handle, pullTypeNum); @@ -141,29 +143,28 @@ if (ret != HDF_SUCCESS) { #### Obtaining the Pull Type of a Pin -Call **PinGetPull** to obtain the pull type of a pin. +Use **PinGetPull()** to obtain the pull type of a pin. -``` +```c int32_t PinGetPull(DevHandle handle, enum PinPullType *pullType); ``` **Table 4** Description of PinGetPull - - | Parameter | Description | | ---------- | ------------------------- | | handle | Pin description handle. | | pullType | Pointer to the pull type obtained.| -| **Return Value**| **Description** | +| **Return Value**| **Description** | | 0 | The operation is successful. | -| Negative value | The operation failed. | +| Negative value | The operation fails. | Example: Obtain the pull type of a pin. -``` +```c int32_t ret; enum PinPullType pullTypeNum; + /* Obtain the pull type of a pin. */ ret = PinGetPull(handle, &pullTypeNum); if (ret != HDF_SUCCESS) { @@ -174,27 +175,25 @@ if (ret != HDF_SUCCESS) { #### Setting the Pull Strength of a Pin -Call **PinSetStrength** to set the pull type of a pin. +Use **PinSetStrength()** to set the pull type of a pin. -``` +```c int32_t PinSetStrength(DevHandle handle, uint32_t strength); ``` **Table 5** Description of PinSetStrength - - | Parameter | Description | | ---------- | ----------------------- | | handle | Pin description handle. | | strength | Pull strength to set. | -| **Return Value**| **Description** | +| **Return Value**| **Description** | | 0 | The operation is successful.| -| Negative value | The operation failed.| +| Negative value | The operation fails.| Example: Set the pull strength of the pin to 2. -``` +```c int32_t ret; uint32_t strengthNum; /* Set the pull strength of the pin. */ @@ -208,29 +207,28 @@ if (ret != HDF_SUCCESS) { #### Obtaining the Pull Strength of a Pin -Call **PinGetStrength** to obtain the pull strength set. +Use **PinGetStrength()** to obtain the pull strength of a pin. -``` +```c int32_t PinGetStrength(DevHandle handle, uint32_t *strength); ``` **Table 6** Description of PinGetStrength - - | Parameter | Description | | ---------- | ------------------------- | | handle | Pin description handle. | | strength | Pointer to the pull strength obtained.| -| **Return Value**| **Description** | +| **Return Value**| **Description** | | 0 | The operation is successful. | -| Negative value | The operation failed. | +| Negative value | The operation fails. | Example: Obtain the pull strength of a pin. -``` +```c int32_t ret; uint32_t strengthNum; + /* Obtain the pull strength of the pin. */ ret = PinGetStrength(handle, &strengthNum); if (ret != HDF_SUCCESS) { @@ -241,31 +239,30 @@ if (ret != HDF_SUCCESS) { #### Setting the Pin Function -The pin function refers to the pin multiplexing function. The function of each pin is different. For details about the pin functions, see [pin_config.hcs](https://gitee.com/openharmony/device_soc_hisilicon/blob/master/hi3516dv300/sdk_liteos/hdf_config/pin/pin_config.hcs). +The pin function refers to the multiplexed pin function. The function of each pin is different. For details about the pin function name, see **//device/soc/hisilicon/hi3516dv300/sdk_liteos/hdf_config/pin/pin_config.hcs**. -Call **PinSetFunc** to set the pin function. +Use **PinSetFunc()** to set the pin function. -``` +```c int32_t PinSetFunc(DevHandle handle, const char *funcName); ``` **Table 7** Description of PinSetFunc - - | Parameter | Description | | ---------- | ------------------- | | handle | Pin description handle. | | funcName | Pointer to the pin function to set. | -| **Return Value**| **Description** | +| **Return Value**| **Description** | | 0 | The operation is successful.| -| Negative value | The operation failed.| +| Negative value | The operation fails.| Example: Set the pin function to LSADC_CH1 (ADC channel 1). -``` +```c int32_t ret; char funcName = "LSADC_CH1"; + /* Sets the pin function. */ ret = PinSetFunc(handle, funcName); if (ret != HDF_SUCCESS) { @@ -276,31 +273,30 @@ if (ret != HDF_SUCCESS) { #### Obtaining the Pin Function -Call **PinGetFunc** to obtain the pin function set. +Use **PinGetFunc()** to obtain the pin function. -``` +```c int32_t PinGetFunc(DevHandle handle, const char **funcName); ``` **Table 8** Description of PinGetFunc - - | Parameter | Description | | ---------- | --------------------- | | handle | Pin description handle. | | funcName | Pointer to the function name obtained.| -| **Return Value**| **Description** | +| **Return Value**| **Description** | | 0 | The operation is successful. | -| Negative value | The operation failed. | +| Negative value | The operation fails. | Example: Obtain the pin function. -``` +```c int32_t ret; -char *funcName; +char *funcName = NULL; + /* Obtain the pin function. */ -ret = PinGetFunc(handle,&funcName); +ret = PinGetFunc(handle, &funcName); if (ret != HDF_SUCCESS) { HDF_LOGE("PinGetFunc: failed, ret %d\n", ret); return ret; @@ -309,16 +305,14 @@ if (ret != HDF_SUCCESS) { #### Releasing a Pin Description Handle -After the operations on a pin are complete, call **PinPut** to release the pin description handle. +After the operations on a pin are complete, use **PinPut()** to release the pin description handle. -``` +```c void PinPut(DevHandle handle); ``` **Table 9** Description of PinPut - - | Parameter | Description | | ---------- | -------------- | | handle | Pin description handle. | @@ -327,13 +321,14 @@ void PinPut(DevHandle handle); Example: Release a pin description handle. -``` +```c PinPut(handle); ``` -## Development Example +## Example + +The following uses the Hi3516D V300 development board as an example. The procedure is as follows: -The procedure is as follows: 1. Pass in the pin name to obtain the pin description handle. 2. Set the pull type of the pin. If the operation fails, release the pin description handle. 3. Obtain the pull type of the pin. If the operation fails, release the pin description handle. @@ -343,9 +338,9 @@ The procedure is as follows: 6. Obtain the pin function. If the operation fails, release the pin description handle. 7. Release the pin description handle if no operation needs to be performed on the pin. -``` -#include "hdf_log.h" /* Header file for log APIs */ -#include "pin_if.h" /* Header file for standard pin APIs */ +```c +#include "hdf_log.h" /* Header file of the HDF log APIs. */ +#include "pin_if.h" /* Header file of standard pin APIs. */ int32_t PinTestSample(void) { @@ -361,7 +356,7 @@ int32_t PinTestSample(void) /* Obtain the pin description handle. */ handle = PinGet(pinName); if (handle == NULL) { - HDF_LOGE("PinGet: failed!\n"); + HDF_LOGE("PinGet: pin get failed!\n"); return; } /* Set the pull type to pull-up for the pin. */ @@ -407,4 +402,5 @@ ERR: /* Release the pin description handle. */ PinPut(handle); return ret; -} \ No newline at end of file +} +``` diff --git a/en/device-dev/driver/driver-platform-pin-develop.md b/en/device-dev/driver/driver-platform-pin-develop.md index 9417dc54ae6ca33ae46941bfe970d87fd7c5c3af..9f34c362f61978f8957e2a7e90d623ac60b3c4a6 100644 --- a/en/device-dev/driver/driver-platform-pin-develop.md +++ b/en/device-dev/driver/driver-platform-pin-develop.md @@ -1,49 +1,54 @@ # Pin - ## Overview -### Pin -The pin, also called pin controller, manages pin resources of system on a chip (SoC) vendors and provides the pin multiplexing function. +### Function + +The pin module, also called pin controller, manages pin resources of the system on a chip (SoC) and implements the pin multiplexing function. ### Basic Concepts -Pin, as a software concept, provides APIs for uniformly managing the pins from different SoC vendors, providing the pin multiplexing function, and configuring the electrical features of pins. +Pin is a software concept designed to uniformly manage SoC pins, implement pin multiplexing, and set electrical features of pins. - SoC - An SOC is a chip that integrates microprocessors, analog IP cores, digital IP cores, and memory for specific purposes. + An SoC is a chip that integrates microprocessors, analog IP cores, digital IP cores, and memory for specific purposes. - Pin multiplexing - When the number of pins of a chip cannot handle the increasing connection requests, you can set the software registers to make the pins to work in different states. + When the number of pins of a chip cannot handle the increasing connection requests, you can set software registers to make the pins to work in different states. ### Working Principles -In the HDF, the pin module does not support the user mode and therefore does not need to publish services. The pin module uses the service-free mode (as shown in Figure 1) in interface adaptation. The service-free mode applies to the devices that do not provide user-mode APIs or the OS that does not distinguish the user mode and the kernel mode. The **DevHandle**, a void pointer, directly points to the kernel mode address of the device object. +In the Hardware Driver Foundation (HDF), the pin module uses the unified service mode for API adaptation. In this mode, a device service is used as the pin manager to handle access requests from the devices of the same type in a unified manner. The unified service mode applies to the scenario where there are many device objects of the same type. If the independent service mode is used in this case, more device nodes need to be configured and more memory resources will be consumed. The following figure shows the unified service mode. + +In the unified service mode, the core layer manages all controllers in a unified manner and publishes a service for the interface layer. That is, the driver does not need to publish a service for each controller. The pin module is divided into the following layers: -- Interface layer: provides APIs for obtaining a pin, setting or obtaining the pull type, pull strength, and functions of a pin, and releasing a pin. -- Core layer: provides the capabilities of matching pin resources and adding, removing, and managing pin controllers. The core layer interacts with the adaptation layer by using hooks. -- Adaptation layer: instantiates hooks to implement specific functions. -**Figure 1** Service-free mode +- Interface layer: provides APIs for obtaining a pin, setting or obtaining the pull type, pull strength, and function of a pin, and releasing a pin. +- Core layer: provides APIs for matching pin resources, adding and removing a pin controller, and managing pins. The core layer interacts with the adaptation layer through hook functions. +- Adaptation layer: instantiates the hook functions to implement specific features. -![](figures/service-free-mode.png) +**Figure 1** Unified service mode + +![](figures/unified-service-mode.png) ### Constraints -Currently, the pin module supports only the kernels (LiteOS) of mini and small systems. +The pin module supports only the LiteOS-A kernel of the small system. ## Development Guidelines ### When to Use -The pin module is used to manage pin resources. When the devices from SoC vendors interconnect with the HDF, the pin driver needs to be adapted. +The pin module is used to manage pin resources. Before using the SoC with the HDF, you need to perform PIN driver adaptation. ### Available APIs -The **PinCntlrMethod** structure defines callbacks to be invoked to call the functions of the pin driver. +To enable the upper layer to successfully operate pins by calling the pin driver APIs, hook functions are defined in **//drivers/hdf_core/framework/support/platform/include/pin/pin_core.h** for the core layer. You need to implement these hook functions at the adaptation layer and hook them to implement the interaction between the interface layer and the core layer. + +**PinCntlrMethod**: ```c struct PinCntlrMethod { @@ -56,385 +61,410 @@ struct PinCntlrMethod { }; ``` -**Table 1** Callbacks in the PinCntlrMethod structure +**Table 1** Hook functions in PinCntlrMethod -| API | Input Parameter | Output Parameter | Return Value| Description| +| Function | Input Parameter | Output Parameter | Return Value| Description| | ------------ | ------------------------------------------- | ------ | ---- | ---- | -| SetPinPull | **cntlr**: structure pointer to the pin controller at the core layer.
**index**: pin index, which is a uint32_t variable.
**pullType**: pull type of the pin. It is an enum constant.| -|HDF_STATUS|Sets the pull type of a pin.| -| GetPinPull | **cntlr**: structure pointer to the pin controller at the core layer.
**index**: pin index, which is a uint32_t variable.| **pullType**: pointer to the pull type of the pin.| HDF_STATUS| Obtains the pull type of a pin.| -| SetPinStrength | **cntlr**: structure pointer to the pin controller at the core layer.
**index**: pin index, which is a uint32_t variable.
**strength**: pull strength of the pin. It is a uint32_t variable.| -| HDF_STATUS| Sets the pull strength of a pin.| -| GetPinStrength | **cntlr**: structure pointer to the pin controller at the core layer.
**index**: pin index, which is a uint32_t variable.| **strength**: pointer to the pull strength of the pin.| HDF_STATUS| Obtains the pull strength of a pin.| -| SetPinFunc | **cntlr**: structure pointer to the pin controller at the core layer.
**index**: pin index, which is a uint32_t variable.
**funcName**: char pointer to the pin function.| -| HDF_STATUS| Sets the pin function.| -| GetPinFunc | **cntlr**: structure pointer to the pin controller at the core layer.
**index**: pin index, which is a uint32_t variable.| **funcName**: char double pointer to the pin function.| HDF_STATUS| Obtains the pin function.| +| SetPinPull | **cntlr**: structure pointer to the pin controller at the core layer.
**index**: pin index, which is a uint32_t variable.
**pullType**: pull type of the pin.| -|HDF_STATUS|Sets the pull type of a pin.| +| GetPinPull | **cntlr**: structure pointer to the pin controller at the core layer.
**index**: pin index, which is a uint32_t variable. | **pullType**: pointer to the pull type obtained.| HDF_STATUS| Obtains the pull type of a pin.| +| SetPinStrength | **cntlr**: structure pointer to the pin controller at the core layer.
**index**: pin index, which is a uint32_t variable.
**strength**: pull type to set, which is a uint32_t variable.| -| HDF_STATUS| Sets the pull strength of a pin.| +| GetPinStrength | **cntlr**: structure pointer to the pin controller at the core layer.
**index**: pin index, which is a uint32_t variable. | **strength**: pointer to the pull strength obtained.| HDF_STATUS| Obtains the pull strength of a pin.| +| SetPinFunc | **cntlr**: structure pointer to the pin controller at the core layer.
**index**: pin index, which is a uint32_t variable.
**funcName**: pointer to the pin function to set. It is a character constant.| -| HDF_STATUS| Sets the pin function.| +| GetPinFunc | **cntlr**: structure pointer to the pin controller at the core layer.
**index**: pin index, which is a uint32_t variable. | **funcName**: double pointer to the PIN function obtained. It is a character constant.| HDF_STATUS| Obtains the pin function.| ### How to Develop The pin module adaptation procedure is as follows: -1. Instantiate the driver entry. -2. Configure attribute files. -3. Instantiate the core layer APIs. -4. Debug the driver. - -### Development Example +- Instantiate the driver entry. +- Configure attribute files. +- Instantiate the pin controller object. +- Debug the driver. -1. Instantiate the driver entry. +### Example - - Instantiate the **HdfDriverEntry** structure. +The following uses the **//device_soc_hisilicon/common/platform/pin/pin_hi35xx.c** driver of the Hi3516D V300 development board as an example to describe the pin driver adaptation. - Instantiate the driver entry. The driver entry must be a global variable of the **HdfDriverEntry** type (defined in **hdf_device_desc.h**), and the value of **moduleName** must be the same as that in **device_info.hcs**. +1. Instantiate the driver entry. - - Call **HDF_INIT** to register the **HdfDriverEntry** instance with the HDF. + The driver entry must be a global variable of the **HdfDriverEntry** type (defined in **hdf_device_desc.h**), and the value of **moduleName** must be the same as that in **device_info.hcs**. In the HDF, the start address of each **HdfDriverEntry** object of all loaded drivers is collected to form a segment address space similar to an array for the upper layer to invoke. + Generally, the HDF calls **Bind()** and then **Init()** to load a driver. If **Init()** fails to be called, the HDF calls **Release()** to release driver resources and exit. - Generally, the HDF calls the **Init** function to load the driver. If **Init** fails to be called, the HDF calls **Release** to release driver resources and exit. + PIN driver entry example: - ```c - static struct HdfDriverEntry g_hi35xxPinDriverEntry = { - .moduleVersion = 1, - .Bind = Hi35xxPinBind, - .Init = Hi35xxPinInit, - .Release = Hi35xxPinRelease, - .moduleName = "hi35xx_pin_driver", // (Mandatory) The value must be the same as that of moduleName in the .hcs file. - }; - HDF_INIT(g_hi35xxPinDriverEntry); // Call HDF_INIT to register the driver entry with the HDF. - ``` + ```c + static struct HdfDriverEntry g_hi35xxPinDriverEntry = { + .moduleVersion = 1, + .Bind = Hi35xxPinBind, + .Init = Hi35xxPinInit, + .Release = Hi35xxPinRelease, + .moduleName = "hi35xx_pin_driver", // (Mandatory) The value must be the same as that of moduleName in the .hcs file. + }; + HDF_INIT(g_hi35xxPinDriverEntry); // Call HDF_INIT to register the driver entry with the HDF. + ``` 2. Configure attribute files. - - Add the device node description to the **vendor/hisilicon/hispark_taurus/hdf_config/device_info/device_info.hcs** file. - - ```c - root { - device_info { - platform :: host { - hostName = "platform_host"; - priority = 50; - device_pin :: device { - device0:: deviceNode { // Set an HDF device node for each pin controller. - policy = 0; // Policy for publishing services. - priority = 10; // Driver startup priority. - permission = 0644; // Permission to create device nodes for the driver. - /* (Mandatory) Driver name, which must be the same as the moduleName in the driver entry. */ - moduleName = "hi35xx_pin_driver"; - /* (Mandatory) Set the controller private data, which must be same as that in pin_config.hcs. */ - deviceMatchAttr = "hisilicon_hi35xx_pin_0"; - } - device1 :: deviceNode { - policy = 0; - priority = 10; - permission = 0644; - moduleName = "hi35xx_pin_driver"; - deviceMatchAttr = "hisilicon_hi35xx_pin_1"; - } - ... - } - } - } - } - ``` - - Add the **pin_config.hcs** file. - - Configure the device attributes in the **device/soc/hisilicon/hi3516dv300/sdk_liteos/hdf_config/pin/pin_config.hcs** file. The parameters are set as follows: - ```c - root { - platform { - pin_config_hi35xx { - template pin_controller { // (Mandatory) Template configuration. In the template, you can configure the common parameters shared by device nodes. - number = 0; // (Mandatory) Controller ID. - regStartBasePhy = 0; // (Mandatory) Start physical base address of the register. - regSize = 0; // (Mandatory) Register bit width. - PinCount = 0; // (Mandatory) Number of pins. - match_attr = ""; - template pin_desc { - pinName = ""; // (Mandatory) Name of the pin. - init = 0; // (Mandatory) Default value of the register. - F0 = ""; // (Mandatory) Function 0. - F1 = ""; // Function 1. - F2 = ""; // Function 2. - F3 = ""; // Function 3. - F4 = ""; // Function 4. - F5 = ""; // Function 5. - } - } - controller_0 :: pin_controller { - number = 0; - regStartBasePhy = 0x10FF0000; - regSize = 0x48; - pinCount = 18; - match_attr = "hisilicon_hi35xx_pin_0"; - T1 :: pin_desc { - pinName = "T1"; - init = 0x0600; - F0 = "EMMC_CLK"; - F1 = "SFC_CLK"; - F2 = "SFC_BOOT_MODE"; - } - ... // Correspond to the pins of the pin controller. Add pins according to actual situation. - } - ... // Each pin controller corresponds to a controller node. If there are multiple pin controllers, add the corresponding controller nodes one by one. - } - } - } - ``` + Add the deviceNode information to the **device_info.hcs** file. The deviceNode information is related to the driver entry registration. The following example uses two pin controllers as an example. If there are more pin controllers, add the deviceNode information to the **device_info.hcs** file for each controller. The device attribute values configured in **pin_config.hcs** are closely related to the driver implementation and default values or value ranges of the **PinCntlr** members at the core layer. + + - **device_info.hcs** example + + Add the deviceNode information to the **//vendor/hisilicon/hispark_taurus/hdf_config/device_info/device_info.hcs** file. + + ```c + root { + device_info { + platform :: host { + hostName = "platform_host"; + priority = 50; + device_pin :: device { + device0 :: deviceNode { // Used to manage pins and release services in a unified manner. + policy = 2; // The value 2 means to publish services for both kernel- and user-mode processes. + priority = 8; // Driver startup priority. + permission = 0644; // Permission for the device node created. + moduleName = "HDF_PLATFORM_PIN_MANAGER"; + serviceName = "HDF_PLATFORM_PIN_MANAGER"; + } + device1:: deviceNode { // Configure an HDF device node for each pin controller. + policy = 0; + priority = 10; // Driver startup priority. + permission = 0644; // Permission for the device node created. + moduleName = "hi35xx_pin_driver"; // (mandatory) Driver name, which must be the same as moduleName in the driver entry. + deviceMatchAttr = "hisilicon_hi35xx_pin_0"; // (Mandatory) Controller private data, which must be the same as that of the controller in pin_config.hcs. + } + device2 :: deviceNode { + policy = 0; + priority = 10; + permission = 0644; + moduleName = "hi35xx_pin_driver"; + deviceMatchAttr = "hisilicon_hi35xx_pin_1"; + } + ... + } + } + } + } + ``` + + - **pin_config.hcs** example: + + Configure the device attributes in the **//device/soc/hisilicon/hi3516dv300/sdk_liteos/hdf_config/pin/pin_config.hcs** file. The parameters are as follows: + + ```c + root { + platform { + pin_config_hi35xx { + template pin_controller { // (Mandatory) Template configuration. If the template is used to configure device node information, the default values in the template will be used for the fields that are not declared for the node. + number = 0; // (Mandatory) PIN controller number. + regStartBasePhy = 0; // (Mandatory) Start address of the physical base address of the register. + regSize = 0; // (Mandatory) Register size. + pinCount = 0; // (Mandatory) Number of pins. + match_attr = ""; + template pin_desc { + pinName = ""; // (Mandatory) Pin name. + init = 0; // (Mandatory) Default value of the register. + F0 = ""; // (Mandatory) Function 0. + F1 = ""; // Function 1. + F2 = ""; // Function 2. + F3 = ""; // Function 3. + F4 = ""; // Function 4. + F5 = ""; // Function 5. + } + } + controller_0 :: pin_controller { + number = 0; + regStartBasePhy = 0x10FF0000; + regSize = 0x48; + pinCount = 18; + match_attr = "hisilicon_hi35xx_pin_0"; + T1 :: pin_desc { + pinName = "T1"; + init = 0x0600; + F0 = "EMMC_CLK"; + F1 = "SFC_CLK"; + F2 = "SFC_BOOT_MODE"; + } + ... // Add each pin under the pin controller. + } + ... // Each pin controller corresponds to a controller node. If there are multiple pin controllers, add the controller nodes one by one. + } + } + } + ``` + + After the **pin_config.hcs** file is configured, include the file in the **hdf.hcs** file. Otherwise, the configuration file cannot take effect. + + ```c + #include "../../../../device/soc/hisilicon/hi3516dv300/sdk_liteos/hdf_config/pin/pin_config.hcs" // Relative path of the file. + ``` 3. Instantiate the pin controller object. - - Initialize the **PinCntlr** object. - - Call **Hi35xxPinCntlrInit** to initialize the **PinCntlr** members. - - ```c - struct Hi35xxPinDesc { - // Pin name. - const char *pinName; - // Initial value. - uint32_t init; - // Index of the pin. - uint32_t index; - // Pull type of the pin. - int32_t pullType; - // Pull strength of the pin. - int32_t strength; - // Array of pin function names. - const char *func[HI35XX_PIN_FUNC_MAX]; - }; - - struct Hi35xxPinCntlr { - // Pin controller. - struct PinCntlr cntlr; - // Pointer to the pin description structure. - struct Hi35xxPinDesc *desc; - // Register mapping address. - volatile unsigned char *regBase; - // ID of the pin controller. - uint16_t number; - // Start address of the register physical base addresses. - uint32_t regStartBasePhy; - // Register bit width. - uint32_t regSize; - // Number of pins. - uint32_t pinCount; - }; - - // PinCntlr is the controller structure at the core layer. The Init function assigns values to PinCntlr. - struct PinCntlr { - struct IDeviceIoService service; - struct HdfDeviceObject *device; - struct PinCntlrMethod *method; - struct DListHead node; // Node in the linked list. - OsalSpinlock spin; // Spinlock. - uint16_t number; // ID of the pin controller. - uint16_t pinCount; // Number of pins. - struct PinDesc *pins; - void *priv; // Private data. - }; - - // Initialize PinCntlr. - static int32_t Hi35xxPinCntlrInit(struct HdfDeviceObject *device, struct Hi35xxPinCntlr *hi35xx) - { - struct DeviceResourceIface *drsOps = NULL; - int32_t ret; - // Read the pin controller attributes from the .hcs file. - drsOps = DeviceResourceGetIfaceInstance(HDF_CONFIG_SOURCE); - if (drsOps == NULL || drsOps->GetUint32 == NULL || drsOps->GetUint16 == NULL) { - HDF_LOGE("%s: invalid drs ops fail!", __func__); - return HDF_FAILURE; - } - ret = drsOps->GetUint16(device->property, "number", &hi35xx->number, 0); - if (ret != HDF_SUCCESS) { - HDF_LOGE("%s: read number failed", __func__); - return ret; - } - - ret = drsOps->GetUint32(device->property, "regStartBasePhy", &hi35xx->regStartBasePhy, 0); - if (ret != HDF_SUCCESS) { - HDF_LOGE("%s: read regStartBasePhy failed", __func__); - return ret; - } - ret = drsOps->GetUint32(device->property, "regSize", &hi35xx->regSize, 0); - if (ret != HDF_SUCCESS) { - HDF_LOGE("%s: read regSize failed", __func__); - return ret; - } - ret = drsOps->GetUint32(device->property, "pinCount", &hi35xx->pinCount, 0); - if (ret != HDF_SUCCESS) { - HDF_LOGE("%s: read pinCount failed", __func__); - return ret; - } - // Assign the values read to the members of the pin controller to initialize the pin controller. - hi35xx->cntlr.pinCount = hi35xx->pinCount; - hi35xx->cntlr.number = hi35xx->number; - hi35xx->regBase = OsalIoRemap(hi35xx->regStartBasePhy, hi35xx->regSize); // Pin controller mapping. - if (hi35xx->regBase == NULL) { - HDF_LOGE("%s: remap Pin base failed", __func__); - return HDF_ERR_IO; - } - hi35xx->desc = (struct Hi35xxPinDesc *)OsalMemCalloc(sizeof(struct Hi35xxPinDesc) * hi35xx->pinCount); - hi35xx->cntlr.pins = (struct PinDesc *)OsalMemCalloc(sizeof(struct PinDesc) * hi35xx->pinCount); - return HDF_SUCCESS; - } - ``` - - - Instantiate the callback structure **PinCntlrMethod** in **PinCntlr**. Other members are initialized by using the **Init** function. - - ```c - // The members of the PinCntlrMethod structure are all callbacks. Vendors need to implement the corresponding functions according to Table 1. - static struct PinCntlrMethod g_method = { - .SetPinPull = Hi35xxPinSetPull, // Set the pull type. - .GetPinPull = Hi35xxPinGetPull, // Obtain the pull type. - .SetPinStrength = Hi35xxPinSetStrength, // Set the pull strength. - .GetPinStrength = Hi35xxPinGetStrength, // Obtain the pull strength. - .SetPinFunc = Hi35xxPinSetFunc, // Set the pin functions. - .GetPinFunc = Hi35xxPinGetFunc, // Obtain the pin functions. - }; - ``` - - - **Init** function - - **Input parameter**: - - **HdfDeviceObject**, an interface parameter exposed by the driver, contains the .hcs information. - - **Return value**: - - **HDF\_STATUS**
The table below describes some status. For more details, see **HDF\_STATUS** in **/drivers/framework/include/utils/hdf\_base.h**. - - | **Status** | **Description** | - | ---------------------- | -------------- | - | HDF_ERR_INVALID_OBJECT | Invalid controller object.| - | HDF_ERR_MALLOC_FAIL | Failed to allocate memory. | - | HDF_ERR_INVALID_PARAM | Invalid parameter. | - | HDF_ERR_IO | I/O error. | - | HDF_SUCCESS | Initialization successful. | - | HDF_FAILURE | Initialization failed. | - - **Function description**: - - Initializes the custom structure object and **PinCntlr** members, and connects to the pin controller by calling the **PinCntlrAdd** function. - - ```c - static int32_t Hi35xxPinReadFunc(struct Hi35xxPinDesc *desc, const struct DeviceResourceNode *node, struct DeviceResourceIface *drsOps) - { - int32_t ret; - uint32_t funcNum = 0; - // Read the pin function names of the pin controller child nodes from the .hcs file. - ret = drsOps->GetString(node, "F0", &desc->func[funcNum], "NULL"); - if (ret != HDF_SUCCESS) { - HDF_LOGE("%s: read F0 failed", __func__); - return ret; - } - - funcNum++; - ret = drsOps->GetString(node, "F1", &desc->func[funcNum], "NULL"); - if (ret != HDF_SUCCESS) { - HDF_LOGE("%s: read F1 failed", __func__); - return ret; - } - - funcNum++; - ... - return HDF_SUCCESS; - } - - static int32_t Hi35xxPinParsePinNode(const struct DeviceResourceNode *node, struct Hi35xxPinCntlr *hi35xx, int32_t index) - { - int32_t ret; - struct DeviceResourceIface *drsOps = NULL; - // Read the pin attributes of the pin controller child nodes from the .hcs file. - drsOps = DeviceResourceGetIfaceInstance(HDF_CONFIG_SOURCE); - if (drsOps == NULL || drsOps->GetUint32 == NULL || drsOps->GetString == NULL) { - HDF_LOGE("%s: invalid drs ops fail!", __func__); - return HDF_FAILURE; - } - ret = drsOps->GetString(node, "pinName", &hi35xx->desc[index].pinName, "NULL"); - if (ret != HDF_SUCCESS) { - HDF_LOGE("%s: read pinName failed", __func__); - return ret; - } - ... - ret = Hi35xxPinReadFunc(&hi35xx->desc[index], node, drsOps); - if (ret != HDF_SUCCESS) { - HDF_LOGE("%s:Pin read Func failed", __func__); - return ret; - } - hi35xx->cntlr.pins[index].pinName = hi35xx->desc[index].pinName; - hi35xx->cntlr.pins[index].priv = (void *)node; - ... - return HDF_SUCCESS; - } - - static int32_t Hi35xxPinInit(struct HdfDeviceObject *device) - { - ... - struct Hi35xxPinCntlr *hi35xx = NULL; - ... - ret = Hi35xxPinCntlrInit(device, hi35xx); // Initialize the pin controller. - ... - DEV_RES_NODE_FOR_EACH_CHILD_NODE(device->property, childNode) { // Traverses each child node of the pin controller. - ret = Hi35xxPinParsePinNode(childNode, hi35xx, index); // Parse child nodes. - ... - } - - hi35xx->cntlr.method = &g_method; // Instantiate method. - ret = PinCntlrAdd(&hi35xx->cntlr); // Connect to the controller. - if (ret != HDF_SUCCESS) { - HDF_LOGE("%s: add Pin cntlr: failed", __func__); - ret = HDF_FAILURE; - } - return HDF_SUCCESS; - } - ``` - - - **Release** function - - **Input parameter**: - - **HdfDeviceObject**, an interface parameter exposed by the driver, contains the .hcs information. - - **Return value**: - - No value is returned. - - **Function description**: - - Releases the memory and deletes the controller. This function assigns values to the **Release** function in the driver entry structure. If the HDF fails to call the **Init** function to initialize the driver, **Release** can be called to release driver resources. - - ```c - static void Hi35xxPinRelease(struct HdfDeviceObject *device) - { - int32_t ret; - uint16_t number; - struct PinCntlr *cntlr = NULL; - struct Hi35xxPinCntlr *hi35xx = NULL; - struct DeviceResourceIface *drsOps = NULL; - - if (device == NULL || device->property == NULL) { - HDF_LOGE("%s: device or property is null", __func__); - return; - } - // Read the pin controller ID from the .hcs file. - drsOps = DeviceResourceGetIfaceInstance(HDF_CONFIG_SOURCE); - if (drsOps == NULL || drsOps->GetUint32 == NULL || drsOps->GetString == NULL) { - HDF_LOGE("%s: invalid drs ops", __func__); - return; - } - ret = drsOps->GetUint16(device->property, "number", &number, 0); - if (ret != HDF_SUCCESS) { - HDF_LOGE("%s: read cntlr number failed", __func__); - return; - } - - cntlr = PinCntlrGetByNumber(number); // Obtain the pin controller based on the controller ID. - PinCntlrRemove(cntlr); - hi35xx = (struct Hi35xxPinCntlr *)cntlr; - if (hi35xx != NULL) { - if (hi35xx->regBase != NULL) { - OsalIoUnmap((void *)hi35xx->regBase); - } - OsalMemFree(hi35xx); - } - } - ``` + Initialize the **PinCntlr** object at the core layer, including defining a custom structure (to pass parameters and data) and implementing the **HdfDriverEntry** member functions (**Bind**, **Init** and **Release**) to instantiate **PinCntlrMethod** in **PinCntlr** (so that the underlying driver functions can be called). + + - Define a custom structure. + + To the driver, the custom structure holds parameters and data. The **DeviceResourceIface** method provided by the HDF reads the values in the **pin_config.hcs** file to initialize the members in the custom structure and passes important parameters to the object at the core layer. + + Initialize **PinCntlr** in **Hi35xxPinCntlrInit**. + + ```c + // Custom pin description structure. + struct Hi35xxPinDesc { + const char *pinName; // Pin name. + uint32_t init; // Initial value. + uint32_t index; // Pin index. + int32_t pullType; // Pull type of the pin. + int32_t strength; // Pull strength of the pin. + const char *func[HI35XX_PIN_FUNC_MAX]; // Array of pin function names. + }; + + /* Custom structure. + struct Hi35xxPinCntlr { + struct PinCntlr cntlr // Core layer control object. + struct Hi35xxPinDesc *desc; // Pointer to the pin description structure. + volatile unsigned char *regBase; // Register base address used for address mapping. + uint16_t number; // Pin controller number. + uint32_t regStartBasePhy; // Start address of the register physical base address. + uint32_t regSize; // Register size. + uint32_t pinCount; // Number of pins. + }; + + // PinCntlr is the controller structure at the core layer. The Init function assigns values to the members of PinCntlr. + struct PinCntlr { + struct IDeviceIoService service; // Driver service. + struct HdfDeviceObject *device; // Driver device object. + struct PinCntlrMethod *method; // Hook functions. + struct DListHead node; // Linked list node. + OsalSpinlock spin; // Spinlock. + uint16_t number; // Pin controller number. + uint16_t pinCount; // Number of pins. + struct PinDesc *pins; // Pin resources. + void *priv; // Private data. + }; + + // Initialize the pin controller. + static int32_t Hi35xxPinCntlrInit(struct HdfDeviceObject *device, struct Hi35xxPinCntlr *hi35xx) + { + struct DeviceResourceIface *drsOps = NULL; + int32_t ret; + // Read the pin controller attributes from the .hcs file. + drsOps = DeviceResourceGetIfaceInstance(HDF_CONFIG_SOURCE); + if (drsOps == NULL || drsOps->GetUint32 == NULL || drsOps->GetUint16 == NULL) { + HDF_LOGE("%s: invalid drs ops fail!", __func__); + return HDF_FAILURE; + } + ret = drsOps->GetUint16(device->property, "number", &hi35xx->number, 0); + if (ret != HDF_SUCCESS) { + HDF_LOGE("%s: read number failed", __func__); + return ret; + } + + if (hi35xx->number > HI35XX_PIN_MAX_NUMBER) { + HDF_LOGE("%s: invalid number:%u", __func__, hi35xx->number); + return HDF_ERR_INVALID_PARAM; + } + ret = drsOps->GetUint32(device->property, "regStartBasePhy", &hi35xx->regStartBasePhy, 0); + if (ret != HDF_SUCCESS) { + HDF_LOGE("%s: read regStartBasePhy failed", __func__); + return ret; + } + ret = drsOps->GetUint32(device->property, "regSize", &hi35xx->regSize, 0); + if (ret != HDF_SUCCESS) { + HDF_LOGE("%s: read regSize failed", __func__); + return ret; + } + ret = drsOps->GetUint32(device->property, "pinCount", &hi35xx->pinCount, 0); + if (ret != HDF_SUCCESS) { + HDF_LOGE("%s: read pinCount failed", __func__); + return ret; + } + if (hi35xx->pinCount > PIN_MAX_CNT_PER_CNTLR) { + HDF_LOGE("%s: invalid number:%u", __func__, hi35xx->number); + return HDF_ERR_INVALID_PARAM; + } + // Assign the values read to the members of the pin controller to initialize the pin controller. + hi35xx->cntlr.pinCount = hi35xx->pinCount; + hi35xx->cntlr.number = hi35xx->number; + hi35xx->regBase = OsalIoRemap(hi35xx->regStartBasePhy, hi35xx->regSize); // Pin controller mapping. + if (hi35xx->regBase == NULL) { + HDF_LOGE("%s: remap Pin base failed", __func__); + return HDF_ERR_IO; + } + hi35xx->desc = (struct Hi35xxPinDesc *)OsalMemCalloc(sizeof(struct Hi35xxPinDesc) * hi35xx->pinCount); + if (hi35xx->desc == NULL) { + HDF_LOGE("%s: memcalloc hi35xx desc failed", __func__); + return HDF_ERR_MALLOC_FAIL; + } + hi35xx->cntlr.pins = (struct PinDesc *)OsalMemCalloc(sizeof(struct PinDesc) * hi35xx->pinCount); + if (hi35xx->desc == NULL) { + HDF_LOGE("%s: memcalloc hi35xx cntlr pins failed", __func__); + return HDF_ERR_MALLOC_FAIL; + } + return HDF_SUCCESS; + } + ``` + + - Instantiate the **PinCntlrMethod** structure in **PinCntlr**. + + ```c + static struct PinCntlrMethod g_method = { + .SetPinPull = Hi35xxPinSetPull, // Set the pull type. + .GetPinPull = Hi35xxPinGetPull, // Obtain the pull type. + .SetPinStrength = Hi35xxPinSetStrength, // Set the pull strength. + .GetPinStrength = Hi35xxPinGetStrength, // Obtain the pull strength. + .SetPinFunc = Hi35xxPinSetFunc, // Set the pin functions. + .GetPinFunc = Hi35xxPinGetFunc, // Obtain the pin functions. + }; + ``` + + - Implement the **Init** function. + + Input Parameter + + **HdfDeviceObject**, a device object created by the HDF for each driver, holds device-related private data and service APIs. + + Return value: + + **HDF_STATUS**
The table below describes some status. For more information, see **HDF_STATUS** in the **//drivers/hdf_core/framework/include/utils/hdf_base.h** file. + + | **State** | **Description** | + | ---------------------- | -------------- | + | HDF_ERR_INVALID_OBJECT | Invalid controller object.| + | HDF_ERR_MALLOC_FAIL | Failed to allocate memory. | + | HDF_ERR_INVALID_PARAM | Invalid parameter. | + | HDF_ERR_IO | I/O error. | + | HDF_SUCCESS | Initialization successful. | + | HDF_FAILURE | Initialization failed. | + + Function description: + + Initializes the custom structure object and **PinCntlr** members, and calls **PinCntlrAdd()** to add the pin controller to the core layer. + + ```c + static int32_t Hi35xxPinReadFunc(struct Hi35xxPinDesc *desc, const struct DeviceResourceNode *node, struct DeviceResourceIface *drsOps) + { + int32_t ret; + uint32_t funcNum = 0; + // Read the pin function names of the pin controller child nodes from the .hcs file. + ret = drsOps->GetString(node, "F0", &desc->func[funcNum], "NULL"); + if (ret != HDF_SUCCESS) { + HDF_LOGE("%s: read F0 failed", __func__); + return ret; + } + + funcNum++; + ret = drsOps->GetString(node, "F1", &desc->func[funcNum], "NULL"); + if (ret != HDF_SUCCESS) { + HDF_LOGE("%s: read F1 failed", __func__); + return ret; + } + + funcNum++; + ... + return HDF_SUCCESS; + } + + static int32_t Hi35xxPinParsePinNode(const struct DeviceResourceNode *node, struct Hi35xxPinCntlr *hi35xx, int32_t index) + { + int32_t ret; + struct DeviceResourceIface *drsOps = NULL; + // Read the pin attributes of the pin controller child nodes from the .hcs file. + drsOps = DeviceResourceGetIfaceInstance(HDF_CONFIG_SOURCE); + if (drsOps == NULL || drsOps->GetUint32 == NULL || drsOps->GetString == NULL) { + HDF_LOGE("%s: invalid drs ops fail!", __func__); + return HDF_FAILURE; + } + ret = drsOps->GetString(node, "pinName", &hi35xx->desc[index].pinName, "NULL"); + if (ret != HDF_SUCCESS) { + HDF_LOGE("%s: read pinName failed", __func__); + return ret; + } + ... + ret = Hi35xxPinReadFunc(&hi35xx->desc[index], node, drsOps); + if (ret != HDF_SUCCESS) { + HDF_LOGE("%s:Pin read Func failed", __func__); + return ret; + } + hi35xx->cntlr.pins[index].pinName = hi35xx->desc[index].pinName; + hi35xx->cntlr.pins[index].priv = (void *)node; + ... + return HDF_SUCCESS; + } + + static int32_t Hi35xxPinInit(struct HdfDeviceObject *device) + { + ... + struct Hi35xxPinCntlr *hi35xx = NULL; + ... + ret = Hi35xxPinCntlrInit(device, hi35xx); // Initialize the pin controller. + ... + DEV_RES_NODE_FOR_EACH_CHILD_NODE(device->property, childNode) { // Traverse each child node of the pin controller. + ret = Hi35xxPinParsePinNode(childNode, hi35xx, index); // Parse child nodes. + ... + } + + hi35xx->cntlr.method = &g_method; // Attach the PinCntlrMethod instance. + ret = PinCntlrAdd(&hi35xx->cntlr); // Add the controller. + if (ret != HDF_SUCCESS) { + HDF_LOGE("%s: add Pin cntlr: failed", __func__); + ret = HDF_FAILURE; + } + return HDF_SUCCESS; + } + ``` + + - Implement the **Release** function. + + Input parameters: + + **HdfDeviceObject**, a device object created by the HDF for each driver, holds device-related private data and service APIs. + + Return value: + + No value is returned. + + Function description: + + Releases the memory and deletes the controller. This function needs to assign values to **Release()** in the driver entry structure. If the HDF fails to call **Init()** to initialize the driver, **Release()** is called to release driver resources. + + ```c + static void Hi35xxPinRelease(struct HdfDeviceObject *device) + { + int32_t ret; + uint16_t number; + struct PinCntlr *cntlr = NULL; + struct Hi35xxPinCntlr *hi35xx = NULL; + struct DeviceResourceIface *drsOps = NULL; + + if (device == NULL || device->property == NULL) { + HDF_LOGE("%s: device or property is null", __func__); + return; + } + // Read the pin controller number from the .hcs file. + drsOps = DeviceResourceGetIfaceInstance(HDF_CONFIG_SOURCE); + if (drsOps == NULL || drsOps->GetUint32 == NULL || drsOps->GetString == NULL) { + HDF_LOGE("%s: invalid drs ops", __func__); + return; + } + ret = drsOps->GetUint16(device->property, "number", &number, 0); + if (ret != HDF_SUCCESS) { + HDF_LOGE("%s: read cntlr number failed", __func__); + return; + } + + cntlr = PinCntlrGetByNumber(number); // Obtain the pin controller based on the controller number. + PinCntlrRemove(cntlr); + hi35xx = (struct Hi35xxPinCntlr *)cntlr; + if (hi35xx != NULL) { + if (hi35xx->regBase != NULL) { + OsalIoUnmap((void *)hi35xx->regBase); + } + OsalMemFree(hi35xx); + } + } + ``` + 4. Debug the driver. (Optional) Verify basic functionalities of new drivers. For example, verify the information returned when the driver is loaded and whether data is successfully transmitted. diff --git a/en/device-dev/driver/driver-platform-pwm-des.md b/en/device-dev/driver/driver-platform-pwm-des.md index 9ea0bedfd64114be9e5d646a52857f8d013406ed..0814751514e04ea888575106a6178a1edb5aa836 100644 --- a/en/device-dev/driver/driver-platform-pwm-des.md +++ b/en/device-dev/driver/driver-platform-pwm-des.md @@ -1,417 +1,409 @@ # PWM - ## Overview -Pulse width modulation (PWM) is a technology that digitally encodes analog signal levels and converts them into pulses. It can be used for motor control and backlight brightness adjustment. - -The PWM APIs provide a set of functions for operating a PWM device, including those for: -- Opening or closing a PWM device handle +### Function -- Setting the PWM period, signal ON-state time, and polarity +Pulse width modulation (PWM) is a technology that digitally encodes analog signal levels and converts them into pulses. -- Enabling and disabling a PWM device +The PWM module provides a set of APIs for operating a PWM device, including: +- Opening or closing a PWM device +- Setting the PWM period, signal ON-state time, and polarity +- Enabling or disabling a PWM device - Obtaining and setting PWM parameters +### Basic Concepts -### PwmConfig Structure +A pulse (electrical pulse) is a burst of current or voltage, characterized by sudden change and discontinuity. There are many types of pulses. Common pulses include triangular, sharp, rectangular, square, trapezoidal, and zigzag pulses. Main pulse parameters include the repetition period **T** (**T** = 1/**F**, where **F** is the pulse repetition frequency), pulse amplitude **U**, rise time **ts** at the leading edge, fall time **t** at the trailing edge, and pulse width **tk**. - **Table 1** PwmConfig structure +### Working Principles -| Parameter| Description| -| -------- | -------- | -| duty | Time that a signal is in the ON state, in ns.| -| period | Time for a signal to complete an on-and-off cycle, in ns.| -| number | Number of square waves to generate.
- Positive value: indicates the number of square waves to generate.
- **0**: indicates that square waves are generated continuously.| -| polarity | PWM signal polarity, which can be positive or reverse.| -| status | PWM device status, which can be enabled or disabled.| +In the Hardware Driver Foundation (HDF), the PWM uses the independent service mode (see Figure 1) for API adaptation. In this mode, each device independently publishes a service to process external access requests. When receiving an access request, the HDF DeviceManager extracts parameters from the request to call the internal APIs of the target device. In the independent service mode, the HDF DeviceManager provides service management capabilities. However, you need to configure a node for each device, which increases memory usage. +In the independent service mode, the core layer does not publish a service for the upper layer. Therefore, a service must be published for each controller. To achieve this purpose: -## Available APIs +- You need to implement the **Bind()** function in **HdfDriverEntry** to bind services. +- The **policy** field of **deviceNode** in the **device_info.hcs** file can be **1** or **2**, but not **0**. - **Table 2** PWM driver APIs +The PWM module is divided into the following layers: -| Category| Description| -| -------- | -------- | -| Operating PWM handles| - **PwmOpen**: opens the device handle of a PWM device.
- **PwmClose**: closes the device handle of a PWM device.| -| Enabling or disabling PWM| - **PwmEnable**: enables a PWM device.
- **PwmDisable**: disables a PWM device.| -| Setting PWM| - **PwmSetPeriod**: sets the PWM period.
- **PwmSetDuty**: sets the signal ON-state time.
- **PwmSetPolarity**: sets the PWM signal polarity.| -| Setting or obtaining PWM configuration| - **PwmSetConfig**: sets PWM device parameters.
- **PwmGetConfig**: obtains PWM device parameters.| +- Interface layer: provides APIs for opening or closing a PWM device, setting the PWM period, signal ON-state time, PWM device polarity, or PWM device parameters, obtaining PWM device parameters, and enabling or disabling a PWM device +- Core layer: provides the capabilities of adding or removing a PWM controller and managing PWM devices. The core layer interacts with the adaptation layer through hook functions. +- Adaptation layer: instantiates the hook functions to implement specific features. -> ![icon-note.gif](../public_sys-resources/icon-note.gif) **NOTE**
-> All APIs described in this document can be called only in the kernel space. +**Figure 1** Independent service mode +![image1](figures/independent-service-mode.png "PWM independent service mode") ## Usage Guidelines +### When to Use + +The PWM module is used for controlling vibrators and adjusting backlight brightness in smart devices. -### How to Use +### Available APIs -The figure below illustrates how to use the APIs. +**Table 1** describes the **PwmConfig** structure, which defines the PWM device attributes. **Table 2** describes the APIs provided by the PWM module. -**Figure 1** Using the PWM driver APIs +**Table 1** PwmConfig structure -![](figures/using-PWM-process.png) +| Parameter| Description| +| -------- | -------- | +| duty | Time that a signal is in the ON state, in ns.| +| period | Time for a signal to complete an on-and-off cycle, in ns.| +| number | Number of square waves to generate.
- Positive value: indicates the number of square waves to generate.
- **0**: indicates that square waves are generated continuously.| +| polarity | PWM signal polarity, which can be normal or reverted.
A signal with normal polarity starts high for the duration of the duty cycle and goes low for the remaining of the period.
A signal with inverted polarity starts low for the duration of the duty cycle and goes high for the remaining of the period.| +| status | PWM device status, which can be enabled or disabled.| +**Table 2** PWM driver APIs -### Opening a PWM Device Handle +| API | | +| ------------------------------------------------------------ | ------------------- | +| DevHandle PwmOpen(uint32_t num) | Opens a PWM device. | +| void PwmClose(DevHandle handle) | Closes a PWM device. | +| int32_t PwmSetPeriod(DevHandle handle, uint32_t period) | Sets the PWM period. | +| int32_t PwmSetDuty(DevHandle handle, uint32_t duty) | Sets the signal ON-state time.| +| int32_t PwmSetPolarity(DevHandle handle, uint8_t polarity) | Sets the PWM signal polarity. | +| int32_t PwmEnable(DevHandle handle) | Enables a PWM device. | +| int32_t PwmDisable(DevHandle handle) | Disables a PWM device. | +| int32_t PwmSetConfig(DevHandle handle, struct PwmConfig *config) | Sets PWM device parameters. | +| int32_t PwmGetConfig(DevHandle handle, struct PwmConfig *config) | Obtains PWM device parameters. | -Before performing operations on a PWM device, call **PwmOpen** to open the device handle. +> ![icon-note.gif](public_sys-resources/icon-note.gif) **NOTE** +> +> All the PWM APIs described in this document can be used in kernel mode and user mode. +### How to Develop -``` +The following figure shows how to use PWM APIs. + +**Figure 2** Using PWM APIs + +![image2](figures/using-PWM-process.png) + +#### Opening a PWM Device + +Before performing operations on a PWM device, use **PwmOpen()** to obtain the device handle. + +```c DevHandle PwmOpen(uint32_t num); ``` - **Table 3** Description of PwmOpen +**Table 3** Description of PwmOpen | **Parameter**| **Description**| | -------- | -------- | | num | PWM device number. | | **Return Value** | **Description** | -| handle | The operation is successful. The handle of the PWM device obtained is returned.| -| NULL | The operation failed. | - -Example: Open the device handle of PWM device 0. +| handle | The operation is successful. The PWM device handle is returned.| +| NULL | The operation fails. | +Example: Open PWM device 0. -``` -uint32_t num = 0; /* PWM device number. */ +```c +uint32_t num = 0; // PWM device number. DevHandle handle = NULL; -/* Obtain the PWM device handle. */ -handle = PwmOpen(num); +handle = PwmOpen(num); // Open PWM device 0 and obtain the device handle. if (handle == NULL) { - /* Error handling. */ + HDF_LOGE("PwmOpen: open pwm_%u failed.\n", num); + return; } ``` +#### Closing a PWM Device -### Closing a PWM Device Handle - -Call **PwmClose()** to close a PWM device handle to release resources. +Use **PwmClose()** to close a PWM device to release resources. - -``` -voidPwmClose(DevHandle handle); +```c +void PwmClose(DevHandle handle); ``` - **Table 4** Description of PwmClose +**Table 4** Description of PwmClose | **Parameter**| **Description**| | -------- | -------- | -| handle | PWM device handle to close. | +| handle | Handle of the PWM device to close. | - -``` -/* Close a PWM device handle. */ -PwmClose(handle); +```c +PwmClose(handle); // Close the PWM device and destroy the PWM device handle. ``` +#### Enabling a PWM Device -### Enabling a PWM Device - -Call **PwmEnable()** to enable a PWM device. - - -``` +```c int32_t PwmEnable(DevHandle handle); ``` - **Table 5** Description of PwmEnable +**Table 5** Description of PwmEnable | **Parameter**| **Description**| | -------- | -------- | | handle | PWM device handle. | -| **Return Value** | **Description** | -| 0 | The operation is successful. | -| Negative number | The operation failed. | +| **Return Value** | **Description**| +| HDF_SUCCESS | The operation is successful. | +| Negative number | The operation fails. | - -``` +```c int32_t ret; -/* Enable a PWM device. */ -ret = PwmEnable(handle); -if (ret != 0) { - /* Error handling. */ +ret = PwmEnable(handle); // Enable the PWM device. +if (ret != HDF_SUCCESS) { + HDF_LOGE("PwmEnable: enable pwm failed, ret:%d\n", ret); + return ret; } ``` +#### Disabling a PWM Device -### Disabling a PWM Device - -Call **PwmDisable()** to disable a PWM device. - - -``` +```c int32_t PwmDisable(DevHandle handle); ``` - **Table 6** Description of PwmDisable +**Table 6** Description of PwmDisable | **Parameter**| **Description**| | -------- | -------- | | handle | PWM device handle. | -| **Return Value** | **Description** | -| 0 | The operation is successful. | -| Negative number | The operation failed. | +| **Return Value** | **Description**| +| HDF_SUCCESS | The operation is successful. | +| Negative number | The operation fails. | - -``` +```c int32_t ret; -/* Disable a PWM device. */ -ret = PwmDisable(handle); -if (ret != 0) { - /* Error handling. */ +ret = PwmDisable(handle); // Disable the PWM device. +if (ret != HDF_SUCCESS) { + HDF_LOGE("PwmDisable: disable pwm failed, ret:%d\n", ret); + return ret; } ``` +#### Setting the PWM Period -### Setting the PWM Period - -Call **PwmSetPeriod()** to set the PWM period. - - -``` +```c int32_t PwmSetPeriod(DevHandle handle, uint32_t period); ``` - **Table 7** Description of PwmSetPeriod +**Table 7** Description of PwmSetPeriod | **Parameter**| **Description**| | -------- | -------- | | handle | PWM device handle. | | period | PWM period to set, in ns.| -| **Return Value**| **Description** | -| 0 | The operation is successful. | +| **Return Value**| **Description** | +| HDF_SUCCESS | The operation is successful. | | Negative number | The operation fails. | - -``` +```c int32_t ret; -/* Set the PWM period to 50000000 ns. */ -ret = PwmSetPeriod(handle, 50000000); -if (ret != 0) { - /* Error handling. */ +ret = PwmSetPeriod(handle, 50000000); // Set the PWM period to 50,000,000 ns. +if (ret != HDF_SUCCESS) { + HDF_LOGE("PwmSetPeriod: pwm set period failed, ret:%d\n", ret); + return ret; } ``` +#### Setting the Signal ON-State Time -### Setting the PWM Signal ON-State Time - -Call **PwmSetDuty()** to set the time that the PWM signal is in the ON state. - - -``` +```c int32_t PwmSetDuty(DevHandle handle, uint32_t duty); ``` - **Table 8** Description of PwmSetDuty +**Table 8** Description of PwmSetDuty | **Parameter**| **Description**| | -------- | -------- | | handle | PWM device handle. | -| duty | Time that the signal is in the ON state, in ns.| -| **Return Value**| **Description** | -| 0 | The operation is successful. | -| Negative number | The operation failed. | +| duty | Time that a signal is in the ON state, in ns.| +| **Return Value**| **Description** | +| HDF_SUCCESS | The operation is successful. | +| Negative number | The operation fails. | -``` +```c int32_t ret; -/* Set the signal ON-state time to 25000000 ns. */ -ret = PwmSetDuty(handle, 25000000); -if (ret != 0) { - /* Error handling. */ +ret = PwmSetDuty(handle, 25000000); // Set the signal ON-state time to 25,000,000 ns. +if (ret != HDF_SUCCESS) { + HDF_LOGE("PwmSetDuty: pwm set duty failed, ret:%d\n", ret); + return ret; } ``` +#### Setting the PWM Signal Polarity -### Setting the PWM Signal Polarity - -Call **PwmSetPolarity()** to set the signal polarity for a PWM device. - - -``` +```c int32_t PwmSetPolarity(DevHandle handle, uint8_t polarity); ``` - **Table 9** Description of PwmSetPolarity +**Table 9** Description of PwmSetPolarity | **Parameter**| **Description**| | -------- | -------- | | handle | PWM device handle. | | polarity | Polarity to set, which can be **PWM\_NORMAL\_POLARITY** or **PWM\_INVERTED\_POLARITY**.| -| **Return Value**| **Description** | -| 0 | The operation is successful. | -| Negative number | The operation failed. | +| **Return Value**| **Description** | +| HDF_SUCCESS | The operation is successful. | +| Negative number | The operation fails. | -``` +```c int32_t ret; -/* Set the PWM polarity to PWM_INVERTED_POLARITY. */ -ret = PwmSetPolarity(handle, PWM_INVERTED_POLARITY); -if (ret != 0) { - /* Error handling. */ +ret = PwmSetPolarity(handle, PWM_INVERTED_POLARITY); // Set the PWM signal polarity to inverted. +if (ret != HDF_SUCCESS) { + HDF_LOGE("PwmSetPolarity: pwm set polarity failed, ret:%d\n", ret); + return ret; } ``` +#### Setting PWM Device Parameters -### Setting PWM Device Parameters - -Call **PwmSetConfig()** to set PWM device parameters. - - -``` +```c int32_t PwmSetConfig(DevHandle handle, struct PwmConfig *config); ``` - **Table 10** Description of PwmSetConfig +**Table 10** Description of PwmSetConfig | **Parameter**| **Description**| | -------- | -------- | -| handle | PWM device handle to close. | -| \*config | Pointer to PWM parameters. | -| **Return Value**| **Description** | -| 0 | The operation is successful. | -| Negative number | The operation failed. | - +| handle | PWM device handle. | +| \*config | Pointer to the PWM parameters to set. | +| **Return Value**| **Description**| +| HDF_SUCCESS | The operation is successful. | +| Negative number | The operation fails. | -``` +```c int32_t ret; struct PwmConfig pcfg; -pcfg.duty = 25000000; /* Set the signal ON-state time to 25000000 ns. */ -pcfg.period = 50000000; /* Set the PWM period to 50000000 ns. */ -pcfg.number = 0; /* Generate square waves continuously. */ -pcfg.polarity = PWM_INVERTED_POLARITY; /* Set the PWM polarity to PWM_INVERTED_POLARITY. */ -pcfg.status = PWM_ENABLE_STATUS; /* Set the running status to Enabled. */ - -/* Set PWM device parameters. */ -ret = PwmSetConfig(handle, &pcfg); -if (ret != 0) { - /* Error handling. */ -} -``` +The pcfg.duty = 25000000; // Set the signal ON-state time to 25,000,000 ns. +pcfg.period = 50000000; // Set the PWM period to 50,000,000 ns. +pcfg.number = 0; // Generate square waves continuously. +pcfg.polarity = PWM_INVERTED_POLARITY; // Set the PWM signal polarity to inverted. +pcfg.status = PWM_ENABLE_STATUS; // Enable PWM. -### Obtaining PWM Device Parameters - -Call **PwmGetConfig()** to obtain PWM device parameters. +ret = PwmSetConfig(handle, &pcfg); // Set PWM device parameters. +if (ret != HDF_SUCCESS) { + HDF_LOGE("PwmSetConfig: pwm set config failed, ret:%d\n", ret); + return ret; +} +``` +#### Obtaining PWM Device Parameters -``` +```c int32_t PwmGetConfig(DevHandle handle, struct PwmConfig *config); ``` - **Table 11** Description of PwmGetConfig +**Table 11** Description of PwmGetConfig | **Parameter**| **Description**| | -------- | -------- | -| handle | PWM device handle to close. | -| \*config | Pointer to PWM parameters. | -| **Return Value**| **Description** | -| 0 | The operation is successful. | -| Negative number | The operation failed. | - +| handle | PWM device handle. | +| \*config | Pointer to the PWM parameters obtained. | +| **Return Value**| **Description**| +| HDF_SUCCESS | The operation is successful. | +| Negative number | The operation fails. | -``` +```c int32_t ret; struct PwmConfig pcfg; -/* Obtain PWM device parameters. */ -ret = PwmGetConfig(handle, &pcfg); -if (ret != 0) { - /* Error handling. */ +ret = PwmGetConfig(handle, &pcfg); // Obtain PWM device parameters. +if (ret != HDF_SUCCESS) { + HDF_LOGE("PwmGetConfig: pwm get config failed, ret:%d\n", ret); + return ret; } ``` +## Example -## Development Example +The following uses the Hi3516D V300 development board as an example to describe how to use the PWM. The procedure is as follows: -The following example shows how to use the APIs to implement a PWM driver and manage the PWM device. +1. Open a PWM device and obtain the PWM device handle. +2. Set the PWM device period. +3. Set the signal ON-state time for the PWM device. +4. Set the signal polarity for the PWM device. +5. Obtain the PWM device parameters. +6. Enable the PWM device. +7. Set the PWM device parameters. +8. Disable the PWM device. +9. Close the PWM device. +```c +#include "pwm_if.h" // Header file of PWM standard APIs. +#include "hdf_log.h" // Header file of the HDF log APIs. -``` -void PwmTestSample(void) +static int32_t PwmTestSample(void) { int32_t ret; uint32_t num; + uint32_t period DevHandle handle = NULL; struct PwmConfig pcfg; - pcfg.duty = 20000000; /* Set the signal ON-state time to 20000000 ns. */ - pcfg.period = 40000000; /* Set the PWM period to 40000000 ns. */ - pcfg.number = 100; /* Generate 100 square waves. */ - pcfg.polarity = PWM_NORMAL_POLARITY; /* Set the polarity to PWM_NORMAL_POLARITY. */ - pcfg.status = PWM_ENABLE_STATUS; /* Set the running status to Enabled. */ + pcfg.duty = 20000000; // Set the signal ON-state time to 20,000,000 ns. + pcfg.period = 40000000; // Set the PWM period to 40,000,000 ns. + pcfg.number = 100; // Generate 100 square waves continuously. + pcfg.polarity = PWM_NORMAL_POLARITY; // Set the PWM signal polarity to normal. + pcfg.status = PWM_ENABLE_STATUS; // Enable the PWM device. - /* Enter the PWM device number. */ - num = 1; + num = 1; // PWM device number. - /* Open the PWM device handle. */ - handle = PwmOpen(num); + handle = PwmOpen(num); // Open a PWM device. if (handle == NULL) { - HDF_LOGE("PwmOpen: failed!\n"); + HDF_LOGE("PwmOpen: open pwm_%u failed!\n", num); return; } - /* Set the PWM period to 50000000 ns.*/ - ret = PwmSetPeriod(handle, 50000000); - if (ret != 0) { - HDF_LOGE("PwmSetPeriod: failed, ret %d\n", ret); - goto _ERR; + ret = PwmSetPeriod(handle, 50000000); // Set the PWM period to 50,000,000 ns. + if (ret != HDF_SUCCESS) { + HDF_LOGE("PwmSetPeriod: pwm set period failed, ret %d\n", ret); + goto ERR; } - /* Set the signal ON-state time to 25000000 ns. */ - ret = PwmSetDuty(handle, 25000000); - if (ret != 0) { - HDF_LOGE("PwmSetDuty: failed, ret %d\n", ret); - goto _ERR; + ret = PwmSetDuty(handle, 25000000); // Set the signal ON-state time to 25,000,000 ns. + if (ret != HDF_SUCCESS) { + HDF_LOGE("PwmSetDuty: pwm set duty failed, ret %d\n", ret); + goto ERR; } - /* Set the PWM polarity to PWM_INVERTED_POLARITY. */ - ret = PwmSetPolarity(handle, PWM_INVERTED_POLARITY); - if (ret != 0) { - HDF_LOGE("PwmSetPolarity: failed, ret %d\n", ret); - goto _ERR; + ret = PwmSetPolarity(handle, PWM_INVERTED_POLARITY); // Set the PWM signal polarity to inverted. + if (ret != HDF_SUCCESS) { + HDF_LOGE("PwmSetPolarity: pwm set polarity failed, ret %d\n", ret); + goto ERR; } - /* Obtain PWM device parameters. */ - ret = PwmGetConfig(handle, &pcfg); - if (ret != 0) { - HDF_LOGE("PwmGetConfig: failed, ret %d\n", ret); - goto _ERR; + ret = PwmGetConfig(handle, &pcfg); // Obtain PWM device parameters. + if (ret != HDF_SUCCESS) { + HDF_LOGE("PwmGetConfig: get pwm config failed, ret %d\n", ret); + goto ERR; } - /* Enable the PWM device. */ - ret = PwmEnable(handle); - if (ret != 0) { - HDF_LOGE("PwmEnable: failed, ret %d\n", ret); - goto _ERR; + ret = PwmEnable(handle); // Enable the PWM device. + if (ret != HDF_SUCCESS) { + HDF_LOGE("PwmEnable: enable pwm failed, ret %d\n", ret); + goto ERR; } - /* Set PWM device parameters. */ - ret = PwmSetConfig(handle, &pcfg); - if (ret != 0) { - HDF_LOGE("PwmSetConfig: failed, ret %d\n", ret); - goto _ERR; + ret = PwmSetConfig(handle, &pcfg); // Set PWM device parameters. + if (ret != HDF_SUCCESS) { + HDF_LOGE("PwmSetConfig: set pwm config failed, ret %d\n", ret); + goto ERR; } - /* Disable the PWM device. */ - ret = PwmDisable(handle); - if (ret != 0) { - HDF_LOGE("PwmDisable: failed, ret %d\n", ret); - goto _ERR; + ret = PwmDisable(handle); // Disable the PWM device. + if (ret != HDF_SUCCESS) { + HDF_LOGE("PwmDisable: disable pwm failed, ret %d\n", ret); + goto ERR; } -_ERR: - /* Close the PWM device handle. */ - PwmClose(handle); +ERR: + PwmClose(handle); // Close the PWM device. + return ret; } ``` diff --git a/en/device-dev/driver/driver-platform-pwm-develop.md b/en/device-dev/driver/driver-platform-pwm-develop.md index 73fe496394b614bf4dc8390dfaa8cc879c4ddb0d..c2d2fe59ab49dda27d2ed5093af80b7311e15e26 100644 --- a/en/device-dev/driver/driver-platform-pwm-develop.md +++ b/en/device-dev/driver/driver-platform-pwm-develop.md @@ -1,208 +1,224 @@ # PWM - ## Overview -In the Hardware Driver Foundation (HDF), the Pulse Width Modulator (PWM) uses the independent service mode for API adaptation. In this mode, each device independently publishes a service to process external access requests. When receiving an access request, the HDF DeviceManager extracts parameters from the request to call the internal APIs of the target device. In the independent service mode, the HDF DeviceManager provides service management capabilities. However, you need to configure a node for each device, which increases memory usage. +### Function - **Figure 1** Independent service mode +Pulse Width Modulation (PWM) is a technology that performs digital coding on analog signal levels and converts them into pulses. It is widely used in fields, such as measurement, communication, and power control and conversion. The PWM module is used for controlling vibrators and adjusting backlight brightness in smart devices. - ![image](figures/independent-service-mode.png "PWM independent service mode") +### Basic Concepts +A pulse (electrical pulse) is a burst of current or voltage, characterized by sudden change and discontinuity. There are many types of pulses. Common pulses include triangular, sharp, rectangular, square, trapezoidal, and zigzag pulses. Main pulse parameters include the repetition period **T** (**T** = 1/**F**, where **F** is the pulse repetition frequency), pulse amplitude **U**, rise time **ts** at the leading edge, fall time **t** at the trailing edge, and pulse width **tk**. -## Available APIs +### Working Principles -**PwmMethod**: +In the Hardware Driver Foundation (HDF), the PWM uses the independent service mode (see Figure 1) for API adaptation. In this mode, each device independently publishes a service to process external access requests. When receiving an access request, the HDF DeviceManager extracts parameters from the request to call the internal APIs of the target device. In the independent service mode, the HDF DeviceManager provides service management capabilities. However, you need to configure a node for each device, which increases memory usage. +In the independent service mode, the core layer does not publish a service for the upper layer. Therefore, a service must be published for each controller. To achieve this purpose: -``` +- You need to implement the **Bind()** function in **HdfDriverEntry** to bind services. +- The **policy** field of **deviceNode** in the **device_info.hcs** file can be **1** or **2**, but not **0**. + +The PWM module is divided into the following layers: + +- Interface layer: provides APIs for opening or closing a PWM device, setting the PWM period, signal ON-state time, PWM device polarity, or PWM device parameters, obtaining PWM device parameters, and enabling or disabling a PWM device +- Core layer: provides the capabilities of adding or removing a PWM controller and managing PWM devices. The core layer interacts with the adaptation layer through hook functions. +- Adaptation layer: instantiates the hook functions to implement specific features. + +**Figure 1** Independent service mode + +![image](figures/independent-service-mode.png "PWM independent service mode") + +## Development Guidelines + +### When to Use + +Before using your PWM device with OpenHarmony, you need to perform PWM driver adaptation. + +### Available APIs + +To enable the upper layer to successfully operate the PWM controller by calling the PWM APIs, hook functions are defined in **//drivers/hdf_core/framework/support/platform/include/pwm/pwm_core.h** for the core layer. You need to implement these hook functions at the adaptation layer and hook them to implement the interaction between the interface layer and the core layer. + +**PwmMethod**: + +```c struct PwmMethod { - int32_t (*setConfig)(struct PwmDev *pwm, struct PwmConfig *config); - int32_t (*open)(struct PwmDev *pwm); - int32_t (*close)(struct PwmDev *pwm); + int32_t (*setConfig)(struct PwmDev *pwm, struct PwmConfig *config); + int32_t (*open)(struct PwmDev *pwm); + int32_t (*close)(struct PwmDev *pwm); }; ``` - **Table 1** Description of callback functions in PwmMethod +**Table 1** Hook functions in **PwmMethod** | Function| Input Parameter| Return Value| Description| | -------- | -------- | -------- | -------- | -| setConfig | **pwm**: structure pointer to the PWM controller at the core layer.
**config**: structure pointer to the attributes to set.| HDF_STATUS| Sets attributes.| -| open | **pwm**: structure pointer to the PWM controller at the core layer.| HDF_STATUS| Opens a device.| -| close | **pwm**: structure pointer to the PWM controller at the core layer.| HDF_STATUS| Closes a device.| - +| setConfig | **pwm**: structure pointer to the PWM controller at the core layer.
**config**: structure pointer to the device attributes to set.| HDF_STATUS| Sets device attributes.| +| open | **pwm**: structure pointer to the PWM controller at the core layer.| HDF_STATUS| Opens a PWM device.| +| close | **pwm**: structure pointer to the PWM controller at the core layer.| HDF_STATUS| Closes a PWM device.| -## How to Develop +### How to Develop -The PWM module adaptation involves the following steps: +The PWM module adaptation procedure is as follows: 1. Instantiate the driver entry. - - Instantiate the **HdfDriverEntry** structure. - - Call **HDF_INIT** to register the **HdfDriverEntry** instance with the HDF. - 2. Configure attribute files. - - Add the **deviceNode** description to the **device_info.hcs** file. - - (Optional) Add the **pwm_config.hcs** file. - 3. Instantiate the PWM controller object. - - Initialize **PwmDev**. - - Instantiate **PwmMethod** in the **PwmDev** object. - > ![icon-note.gif](public_sys-resources/icon-note.gif) **NOTE**
- > For details about the functions in **PwmMethod**, see [Available APIs](#available-apis). - 4. Debug the driver. - (Optional) For new drivers, verify the basic functions, such as the PWM status control and response to interrupts. - +### Example -## Development Example - -The following uses **pwm_hi35xx.c** as an example to present the information required for implementing device functions. +The following uses the **//device_soc_hisilicon/common/platform/pwm/pwm_hi35xx.c** driver of the Hi3516D V300 development board as an example to describe the PWM driver adaptation. 1. Instantiate the driver entry. - The driver entry must be a global variable of the **HdfDriverEntry** type (defined in **hdf_device_desc.h**), and the value of **moduleName** must be the same as that in **device_info.hcs**. - - In the HDF, the start address of each **HdfDriverEntry** object of all loaded drivers is collected to form a segment address space similar to an array for the upper layer to invoke. - - Generally, the HDF calls the **Bind** function and then the **Init** function to load a driver. If **Init** fails to be called, the HDF calls **Release** to release driver resources and exit. + The driver entry must be a global variable of the **HdfDriverEntry** type (defined in **hdf_device_desc.h**), and the value of **moduleName** must be the same as that in **device_info.hcs**. In the HDF, the start address of each **HdfDriverEntry** object of all loaded drivers is collected to form a segment address space similar to an array for the upper layer to invoke. + Generally, the HDF calls **Bind()** and then **Init()** to load a driver. If **Init()** fails to be called, the HDF calls **Release()** to release driver resources and exit. PWM driver entry example: - - ``` + + ```c struct HdfDriverEntry g_hdfPwm = { .moduleVersion = 1, - .moduleName = "HDF_PLATFORM_PWM",// (Mandatory) The value must be the same as that of moduleName in the .hcs file. - .Bind = HdfPwmBind, - .Init = HdfPwmInit, - .Release = HdfPwmRelease, + .moduleName = "HDF_PLATFORM_PWM", // (Mandatory) The value must be the same as that of moduleName in the .hcs file. + .Bind = HdfPwmBind, // See the Bind function. + .Init = HdfPwmInit, // See the Init function. + .Release = HdfPwmRelease, // See the Release function. }; - // Call HDF_INIT to register the driver entry with the HDF. - HDF_INIT(g_hdfPwm); + HDF_INIT(g_hdfPwm); // Call HDF_INIT to register the driver entry with the HDF. ``` -2. Add the **deviceNode** information to the **device_info.hcs** file and configure the device attributes in the **pwm_config.hcs** file. +2. Configure attribute files. + + Add the deviceNode information to the **device_info.hcs** file. The deviceNode information is related to the driver entry registration. The following example uses two PWM controllers as an example. If there are more PWM controllers, add the deviceNode information to the **device_info.hcs** file for each controller. The device attribute values configured in **pwm_config.hcs** are closely related to default values or value ranges of the **PwmDev** members at the core layer. - The **deviceNode** information is related to registration of the driver entry. The device attribute values are closely related to the default values or value ranges of the **PwmDev** members at the core layer. If there are multiple devices, you need to add the **deviceNode** information to the **device_info** file and add the device attributes to the **pwm_config** file for each device. + - **device_info.hcs** example - - **device_info.hcs** configuration example + Add the deviceNode information to the **//vendor/hisilicon/hispark_taurus/hdf_config/device_info/device_info.hcs** file. - - ``` + ```c root { - device_info { - platform :: host { - hostName = "platform_host"; - priority = 50; - device_pwm :: device { // Configure an HDF device node for each PWM controller. - device0 :: deviceNode { - policy = 1; // Publish services for kernel-mode processes. - priority = 80; // Driver startup priority. - permission = 0644; // Permission for the driver to create a device node. - moduleName = "HDF_PLATFORM_PWM"; // (Mandatory) Driver name, which must be the same as moduleName in the driver entry. - serviceName = "HDF_PLATFORM_PWM_0"; // (Mandatory) Unique name of the service published by the driver. - deviceMatchAttr = "hisilicon_hi35xx_pwm_0";// (Mandatory) Used to configure the private data of the controller. - // The value must be the same as the controller information in pwm_config.hcs. + device_info { + platform :: host { + hostName = "platform_host"; + priority = 50; + device_pwm ::device { // Configure an HDF device node for each PWM controller. + device0 :: deviceNode { + policy = 1; // The value 1 means to publish services only to the kernel-mode processes. + priority = 80; // Driver startup priority. + permission = 0644; // Permission for the device node created. + moduleName = "HDF_PLATFORM_PWM"; // (Mandatory) Driver name, which must be the same as moduleName in the driver entry. + serviceName = "HDF_PLATFORM_PWM_0"; // (Mandatory) Unique name of the service published by the driver. + deviceMatchAttr = "hisilicon_hi35xx_pwm_0"; // Controller private data, which must be the same as that of the controller in pwm_config.hcs. + } + device1 :: deviceNode { + policy = 1; + priority = 80; + permission = 0644; + moduleName = "HDF_PLATFORM_PWM"; + serviceName = "HDF_PLATFORM_PWM_1"; + deviceMatchAttr = "hisilicon_hi35xx_pwm_1"; + } + ... + } } - device1 :: deviceNode { - policy = 1; - priority = 80; - permission = 0644; - moduleName = "HDF_PLATFORM_PWM"; - serviceName = "HDF_PLATFORM_PWM_1"; - deviceMatchAttr = "hisilicon_hi35xx_pwm_1"; - } - } } - } } ``` - - **pwm_config.hcs** configuration example - - ``` + - **pwm_config.hcs** example + + Configure the device attributes in the **//device/soc/hisilicon/hi3516dv300/sdk_liteos/hdf_config/pwm/pwm_config.hcs** file. The parameters are as follows: + + ```c root { - platform { - pwm_config { - template pwm_device { // (Mandatory) Template configuration. In the template, you can configure the common parameters shared by service nodes. - serviceName = ""; - match_attr = ""; - num = 0; // (Mandatory) Device number - base = 0x12070000; // (Mandatory) Used for address mapping - } - device_0x12070000 :: pwm_device { // Add the HDF node and device node information for each device. - match_attr = "hisilicon_hi35xx_pwm_0";// (Mandatory) The value must be the same as that of deviceMatchAttr in device_info.hcs. - } - device_0x12070020 :: pwm_device { - match_attr = "hisilicon_hi35xx_pwm_1"; - num = 1; - base = 0x12070020; // (Mandatory) Used for address mapping - } + platform { + pwm_config { + template pwm_device { // (Mandatory) Template configuration. If the template is used to configure device node information, the default values in the template will be used for the fields that are not declared for the node. + serviceName = ""; + match_attr = ""; + num = 0; // (Mandatory) Device number. + base = 0x12070000; // (Mandatory) Base address used for address mapping. + } + device_0x12070000 :: pwm_device { // Add the HDF node and device node information for each device. + match_attr = "hisilicon_hi35xx_pwm_0"; // (Mandatory) The value must be the same as that of deviceMatchAttr in device_info.hcs. + } + device_0x12070020 :: pwm_device { + match_attr = "hisilicon_hi35xx_pwm_1"; + num = 1; + base = 0x12070020; // (Mandatory) Base address used for address mapping. + } + } } - } } ``` -3. Initialize the **wmDev** object at the core layer, including defining a custom structure (to pass parameters and data) and implementing the **HdfDriverEntry** member functions (**Bind**, **Init**, and **Release**) to instantiate **PwmMethod** in **PwmDev** (so that the underlying driver functions can be called). + After the **pwm_config.hcs** file is configured, include the file in the **hdf.hcs** file. Otherwise, the configuration file cannot take effect. - - Defining a custom structure + ```c + #include "../../../../device/soc/hisilicon/hi3516dv300/sdk_liteos/hdf_config/pwm/pwm_config.hcs" // Relative path of the file. + ``` - To the driver, the custom structure holds parameters and data. The **DeviceResourceIface** method provided by the HDF reads the values in the **pwm_config.hcs** file to initialize the members in the custom structure and passes important parameters, such as the device number, to the object at the core layer. +3. Instantiate the PWM controller object. - - ``` + Initialize the **PwmDev** object at the core layer, including defining a custom structure (to pass parameters and data) and implementing the **HdfDriverEntry** member functions (**Bind**, **Init** and **Release**) to instantiate **PwmMethod** in **PwmDev** (so that the underlying driver functions can be called). + + - Define a custom structure. + + To the driver, the custom structure holds parameters and data. The **DeviceResourceIface** method provided by the HDF reads the values in the **pwm_config.hcs** file to initialize the members in the custom structure and passes important parameters, such as the PWM device number, to the object at the core layer. + + ```c struct HiPwm { - struct PwmDev dev; // (Mandatory) Core layer structure - volatile unsigned char *base; - struct HiPwmRegs *reg; // Device attribute structure, which can be customized. - bool supportPolarity; + struct PwmDev dev; // (Mandatory) Control object at the core layer. + volatile unsigned char *base; // (Mandatory) Register base address used for address mapping. + struct HiPwmRegs *reg; // Device attribute structure, which can be customized. + bool supportPolarity; // Whether polarity is supported. }; - - // PwmDev is the controller structure at the core layer. The Init function assigns values to the members of PwmDev. - struct PwmDev { - struct IDeviceIoService service; - struct HdfDeviceObject *device; - struct PwmConfig cfg; // Attribute structure. For details, see the description of PwmConfig. - struct PwmMethod *method; // Hook function template - bool busy; - uint32_t num; // Device number - OsalSpinlock lock; - void *priv; // Private data. Generally, the start address of the custom structure is stored to facilitate invoking of the structure. + + struct PwmDev { // PwmDev is the core layer controller structure. The Bind function assigns values to the members of PwmDev. + struct IDeviceIoService service; // Driver service. + struct HdfDeviceObject *device; // Driver device object. + struct PwmConfig cfg; // Device attribute structure. For details, see the following definition. + struct PwmMethod *method; // Hook functions. + bool busy; // Whether the device is busy. + uint32_t num; // Device number. + OsalSpinlock lock; // Spinlock. + void *priv; // Private data. }; - struct PwmConfig { - uint32_t duty // Time that a signal is in the ON state, in ns. - uint32_t period; // Time for a signal to complete an on-and-off cycle, in ns. - uint32_t number; // Number of square waves to generate. - uint8_t polarity; // Polarity - // ------------------- | -------------- - // PWM_NORMAL_POLARITY | Normal polarity - // PWM_INVERTED_POLARITY | Inverted polarity - // - uint8_t status; // Running status - // ------------------ | ----------------- - // PWM_DISABLE_STATUS | Disabled - // PWM_ENABLE_STATUS | Enabled + + struct PwmConfig { // PWM device attributes. + uint32_t duty; // Time that a signal is in the ON state, in ns. + uint32_t period; // Time for a signal to complete an on-and-off cycle, in ns. + uint32_t number; // Number of square waves to generate. + uint8_t polarity; // Polarity + // ------------------- | -------------- + // PWM_NORMAL_POLARITY | Normal polarity + // PWM_INVERTED_POLARITY | Inverted polarity + // + uint8_t status; // Running status. + // ------------------ | ----------------- + // PWM_DISABLE_STATUS | Disabled + // PWM_ENABLE_STATUS | Enabled }; ``` - - Instantiating **PwmMethod** in **PwmDev** (other members are initialized by **Init**) + - Instantiate the **PwmMethod** structure in **PwmDev**. - - ``` - // The following uses pwm_hi35xx.c as an example. Fill the hook function. - struct PwmMethod g_pwmOps = { - .setConfig = HiPwmSetConfig,// Set attributes. + ```c + struct PwmMethod g_pwmOps = { // Instantiate the hook functions in pwm_hi35xx.c. + .setConfig = HiPwmSetConfig, // Set device attributes. }; ``` - - **Init** function + + - Implement the **Init** function. **Input parameter**: - **HdfDeviceObject**, an interface parameter exposed by the driver, contains the .hcs information. + **HdfDeviceObject**, a device object created by the HDF for each driver, holds device-related private data and service APIs. **Return value**: - **HDF_STATUS**
The table below describes some status. For more information, see **HDF_STATUS** in the **/drivers/framework/include/utils/hdf_base.h** file. + **HDF_STATUS**
The table below describes some status. For more information, see **HDF_STATUS** in the **//drivers/hdf_core/framework/include/utils/hdf_base.h** file. | Status| Description| | -------- | -------- | @@ -215,58 +231,58 @@ The following uses **pwm_hi35xx.c** as an example to present the information req **Function description**: - Initializes the custom structure object and **PwmDev**, and calls the **PwmDeviceAdd** function at the core layer. + Initializes the custom structure object and **PwmDev** members, and calls **PwmDeviceAdd()** to add the PWM controller to the core layer. - - ``` - // The Bind function is empty. It can be combined with the Init function or implement related operations based on service requirements. + ```c + // In this example, Bind() is an empty function. You can add operations as required or implement related features in Init(). static int32_t HdfPwmBind(struct HdfDeviceObject *obj) { - (void)obj; - return HDF_SUCCESS; + (void)obj; + return HDF_SUCCESS; } - + static int32_t HdfPwmInit(struct HdfDeviceObject *obj) { - int ret; - struct HiPwm *hp = NULL; - ... - hp = (struct HiPwm *)OsalMemCalloc(sizeof(*hp)); - ... - ret = HiPwmProbe(hp, obj); // (Mandatory) The implementation is as follows: - ... - return ret; + int ret; + struct HiPwm *hp = NULL; + ... + hp = (struct HiPwm *)OsalMemCalloc(sizeof(*hp)); + ... + ret = HiPwmProbe(hp, obj); // (Mandatory) The implementation is as follows. + ... + return ret; } - + static int32_t HiPwmProbe(struct HiPwm *hp, struct HdfDeviceObject *obj) { uint32_t tmp; struct DeviceResourceIface *iface = NULL; - iface = DeviceResourceGetIfaceInstance(HDF_CONFIG_SOURCE);// Initialize the custom structure HiPwm. + iface = DeviceResourceGetIfaceInstance(HDF_CONFIG_SOURCE); // Initialize the custom structure HiPwm. ... - hp->reg = (struct HiPwmRegs *)hp->base; // Initialize the custom structure HiPwm. - hp->supportPolarity = false; // Initialize the custom structure HiPwm. - hp->dev.method = &g_pwmOps; // Attach the PwmMethod instance. - hp->dev.cfg.duty = PWM_DEFAULT_DUTY_CYCLE; // Initialize PwmDev. - hp->dev.cfg.period = PWM_DEFAULT_PERIOD; // Initialize PwmDev. - hp->dev.cfg.polarity = PWM_DEFAULT_POLARITY; // Initialize PwmDev. - hp->dev.cfg.status = PWM_DISABLE_STATUS; // Initialize PwmDev. - hp->dev.cfg.number = 0; // Initialize PwmDev. - hp->dev.busy = false; // Initialize PwmDev. - if (PwmDeviceAdd(obj, &(hp->dev)) ) != HDF_SUCCESS) { // Call the core layer function to initialize devices and services. + hp->reg = (struct HiPwmRegs *)hp->base; // Initialize the custom structure HiPwm. + hp->supportPolarity = false; // Initialize the custom structure HiPwm. + hp->dev.method = &g_pwmOps; // Attach the PwmMethod instance. + hp->dev.cfg.duty = PWM_DEFAULT_DUTY_CYCLE; // Initialize PwmDev. + hp->dev.cfg.period = PWM_DEFAULT_PERIOD; // Initialize PwmDev. + hp->dev.cfg.polarity = PWM_DEFAULT_POLARITY; // Initialize PwmDev. + hp->dev.cfg.status = PWM_DISABLE_STATUS; // Initialize PwmDev. + hp->dev.cfg.number = 0; // Initialize PwmDev. + hp->dev.busy = false; // Initialize PwmDev. + if (PwmDeviceAdd(obj, &(hp->dev)) ) != HDF_SUCCESS) { // Call the core layer function to initialize devices and services. OsalIoUnmap((void *)hp->base); return HDF_FAILURE; } return HDF_SUCCESS; } ``` - - **Release** function + + - Implement the **Release** function. **Input parameter**: - **HdfDeviceObject**, an interface parameter exposed by the driver, contains the .hcs information. + **HdfDeviceObject**, a device object created by the HDF for each driver, holds device-related private data and service APIs. **Return value**: @@ -274,17 +290,20 @@ The following uses **pwm_hi35xx.c** as an example to present the information req **Function description**: - Releases the memory and deletes the controller. This function assigns values to the **Release** function in the driver entry structure. If the HDF fails to call the **Init** function to initialize the driver, the **Release** function can be called to release driver resources. + Releases the memory and deletes the controller. This function assigns values to **Release()** in the driver entry structure. If the HDF fails to call **Init()** to initialize the driver, **Release()** is called to release driver resources. - - ``` + ```c static void HdfPwmRelease(struct HdfDeviceObject *obj) { struct HiPwm *hp = NULL; ... - hp = (struct HiPwm *)obj->service;// A forced conversion from HdfDeviceObject to HiPwm is involved. - ... - PwmDeviceRemove(obj, &(hp->dev));// (Mandatory) Call the core layer functions to release PwmDev devices and services. A forced conversion from HiPwm to PwmDev is involved in the process. - HiPwmRemove(hp); // Release HiPwm. + hp = (struct HiPwm *)obj->service; // A forced conversion from HdfDeviceObject to HiPwm is involved. + ... + PwmDeviceRemove(obj, &(hp->dev)); // (Mandatory) Call the core layer functions to release PwmDev devices and services. A forced conversion from HiPwm to PwmDev is involved in the process. + HiPwmRemove(hp); // Release HiPwm. } ``` + +4. Debug the driver. + + (Optional) For new drivers, verify the basic functions, such as the PWM status control and response to interrupts. diff --git a/en/device-dev/driver/driver-platform-uart-des.md b/en/device-dev/driver/driver-platform-uart-des.md index 3ddb92b532569df4718931c9c3ca32b7e7e010c1..c73c19d71a903d9f73539b3704975fb15c374a94 100644 --- a/en/device-dev/driver/driver-platform-uart-des.md +++ b/en/device-dev/driver/driver-platform-uart-des.md @@ -1,328 +1,353 @@ # UART - ## Overview -The Universal Asynchronous Receiver/Transmitter (UART) is a universal serial data bus used for asynchronous communication. It enables bi-directional communication between devices in full-duplex mode. +### Function -UART is widely used to print information for debugging or to connect to various external modules such as GPS and Bluetooth. +The Universal Asynchronous Receiver/Transmitter (UART) is a universal serial data bus used for asynchronous communication. It enables bi-directional communication between devices in full-duplex mode. A UART is connected to other modules through two wires (as shown in Figure 1) or four wires (as shown in Figure 2). - - TX: TX pin of the transmitting UART. It is connected to the RX pin of the peer UART. - - RX: RX pin of the receiving UART. It is connected to the TX pin of the peer UART. - - RTS: Request to Send signal pin. It is connected to the CTS pin of the peer UART and is used to indicate whether the local UART is ready to receive data. - - CTS: Clear to Send signal pin. It is connected to the RTS pin of the peer UART and is used to indicate whether the local UART is allowed to send data to the peer end. - **Figure 1** Two-wire UART communication + - TX: UART transmitter. It is connected to the RX of the peer UART. + - RX: UART receiver. It is connected to the TX of the peer UART. + - RTS: Request to Send signal, indicating whether the local UART is ready to receive data. It is connected to the CTS of the peer UART. + - CTS: Clear to Send signal, indicating whether the local UART is allowed to send data to the peer end. It is connected to the RTS of the peer UART. - ![](figures/2-wire-uart-communication.png "2-wire-uart-communication") +**Figure 1** Two-wire UART communication - **Figure 2** Four-wire UART communication +![image1](figures/2-wire-uart-communication.png "2-wire-uart-communication") - ![](figures/4-wire-uart-communication.png "4-wire-uart-communication") +**Figure 2** Four-wire UART communication -- The transmitting and receiving UARTs must ensure that they have the same settings on particular attributes such as the baud rate and data format (start bit, data bit, parity bit, and stop bit) before they start to communicate. During data transmission, a UART sends data to the peer end over the TX pin and receives data from the peer end over the RX pin. When the size of the buffer used by a UART for storing received data reaches the preset threshold, the RTS signal of the UART changes to **1** (data cannot be received), and the peer UART stops sending data to it because its CTS signal does not allow it to send data. + ![image2](figures/4-wire-uart-communication.png "4-wire-uart-communication") -- The UART interface defines a set of common functions for operating a UART port, including obtaining and releasing device handles, reading and writing data of a specified length, and obtaining and setting the baud rate, as well as the device attributes. +The UART transmitter and receiver must have the same settings on particular attributes, such as the baud rate and data format (start bit, data bits, parity bit, and stop bit) before they start to communicate. A UART sends data to the peer end over the TX and receives data from the peer end over the RX. When the size of the buffer used by a UART for storing received data reaches the preset threshold, the RTS signal of the UART changes to **1** (data cannot be received), and the peer UART stops sending data to it because its CTS signal does not allow it to send data. +The UART module provides APIs for operating UART ports, including: -## Available APIs +- Opening or closing a UART device +- Reading or writing data +- Setting or obtaining the baud rate of a UART device +- Setting or obtaining UART device attributes - **Table 1** UART driver APIs +### Basic Concepts -| API| Description| -| -------- | -------- | -| UartOpen | Obtains a UART device handle.| -| UartClose | Releases a UART device handle.| -| UartRead | Reads data of the specified length from a UART device.| -| UartWrite | Writes data of the specified length to a UART device.| -| UartGetBaud | Obtains the UART baud rate.| -| UartSetBaud | Sets the UART baud rate.| -| UartGetAttribute | Obtains UART device attributes.| -| UartSetAttribute | Sets UART device attributes.| -| UartSetTransMode | Sets the UART transmission mode.| +- Asynchronous communication + + In asynchronous communication, data is transmitted in frames of characters or bytes. Frames are sent and received one by one through the transmission line. The transmitter and receiver have their own clocks to control data sending and receiving. The two clock sources are independent and not synchronized with each other. + + When data is sent one character at a time, the time interval between two characters is not fixed, but the time interval between two adjacent bits in a character frame is fixed. + +- Full-duplex transmission + + A duplex communication mode allows data to be transmitted in both directions at the same time. A duplex communication channel is equivalent to two simplex communication channels operating in opposite directions at the same time. In full-duplex mode, signals can be transmitted bidirectionally at the same time. + +### Working Principles -> ![icon-note.gif](public_sys-resources/icon-note.gif) **NOTE**
-> All APIs described in this document can be called only in kernel mode. +In the Hardware Driver Foundation (HDF), the UART uses the independent service mode (see Figure 3) for API adaptation. In this mode, each device independently publishes a service to process external access requests. When receiving an access request, the HDF DeviceManager extracts parameters from the request to call the internal APIs of the target device. In the independent service mode, the HDF DeviceManager provides service management capabilities. However, you need to configure a node for each device, which increases memory usage. +In the independent service mode, the core layer does not publish a service for the upper layer. Therefore, a service must be published for each controller. To achieve this purpose: + +- You need to implement the **Bind()** function in **HdfDriverEntry** to bind services. +- The **policy** field of **deviceNode** in the **device_info.hcs** file can be **1** or **2**, but not **0**. + +The UART module is divided into the following layers: + +- Interface layer: provides APIs for opening or closing a UART device, reading or writing data of the specified length, setting or obtaining the baud rate or attributes of a UART device, and setting the transmission mode. +- Core layer: provides the capabilities of adding or removing a UART controller, and managing UART devices. The core layer interacts with the adaptation layer through hook functions. +- Adaptation layer: instantiates the hook functions to implement specific features. + +**Figure 3** Independent service mode + +![image3](figures/independent-service-mode.png) ## Usage Guidelines +### When to Use -### How to Use +The UART module is widely used to implement low-speed serial communication between devices, for example, output the printing information. It can also connect to a variety of external GPS and Bluetooth devices. -The figure below illustrates how to use the APIs. +### Available APIs - **Figure 3** Using UART driver APIs +**Table 1** UART driver APIs - ![](figures/using-UART-process.png) +| API| Description| +| -------- | -------- | +| DevHandle UartOpen(uint32_t port) | Opens a UART device.| +| void UartClose(DevHandle handle) | Closes a UART device.| +| int32_t UartRead(DevHandle handle, uint8_t *data, uint32_t size) | Reads data of the specified length from a UART device.| +| int32_t UartWrite(DevHandle handle, uint8_t *data, uint32_t size) | Writes data of the specified length to a UART device.| +| int32_t UartGetBaud(DevHandle handle, uint32_t *baudRate) | Obtains the UART baud rate.| +| int32_t UartSetBaud(DevHandle handle, uint32_t baudRate) | Sets the UART baud rate.| +| int32_t UartGetAttribute(DevHandle handle, struct UartAttribute *attribute) | Obtains UART device attributes.| +| int32_t UartSetAttribute(DevHandle handle, struct UartAttribute *attribute) | Sets UART device attributes.| +| int32_t UartSetTransMode(DevHandle handle, enum UartTransMode mode) | Sets the UART transmission mode.| +> ![icon-note.gif](public_sys-resources/icon-note.gif) **NOTE** +> +> All the UART APIs described in this document can be used in kernel mode and user mode. -### Opening a UART Device Handle +### How to Develop -Before performing UART communication, call **UartOpen** to obtain a UART device handle. This function returns the pointer to the UART device handle with the specified port number. +The following figure illustrates how to use the UART APIs. - -``` +**Figure 4** Using UART driver APIs + +![image4](figures/using-UART-process.png) + + +#### Opening a UART Device + +Before performing UART communication, use **UartOpen()** to obtain a UART device handle based on the port number. + +```c DevHandle UartOpen(uint32_t port); ``` - **Table 2** Description of UartOpen +**Table 2** Description of UartOpen -| Parameter| Description| +| Parameter| Description| | -------- | -------- | -| port | UART port number.| -| **Return Value**| **Description**| -| NULL | The operation failed.| -| Device handle| The operation is successful. The obtained UART device handle is returned.| +| port | UART port number.| +| **Return Value**| **Description**| +| NULL | The operation fails.| +| Device handle| The operation is successful. The obtained UART device handle is returned.| + +Example: Obtain the device handle of UART port 1. + +```c +DevHandle handle = NULL; // UART device handle. +uint32_t port = 1; // UART device port number. - Example: Obtain the device handle of UART port 3. - -``` -DevHandle handle = NULL; /* UART device handle */ -uint32_t port = 3; /* UART port number */ handle = UartOpen(port); if (handle == NULL) { - HDF_LOGE("UartOpen: failed!\n"); + HDF_LOGE("UartOpen: open uart_%u failed!\n", port); return; } ``` +#### Setting the UART Baud Rate -### Setting the UART Baud Rate - -Call **UartSetBaud()** to set the UART baud rate. +Use **UartSetBaud()** to set the UART baud rate. - -``` +```c int32_t UartSetBaud(DevHandle handle, uint32_t baudRate); ``` - **Table 3** Description of UartSetBaud +**Table 3** Description of UartSetBaud -| Parameter| Description| +| Parameter| Description| | -------- | -------- | -| handle | UART device handle.| -| baudRate | Baud rate to set.| -| **Return Value**| **Description**| -| 0 | The operation is successful.| -| Negative value| The operation failed.| +| handle | UART device handle.| +| baudRate | Baud rate to set.| +| **Return Value**| **Description**| +| HDF_SUCCESS | The operation is successful.| +| Negative value| The operation fails.| Example: Set the UART baud rate to **9600**. - -``` +```c int32_t ret; -/* Set the UART baud rate to 9600. */ -ret = UartSetBaud(handle, 9600); -if (ret != 0) { + +ret = UartSetBaud(handle, 9600); // Set the UART baud rate. +if (ret != HDF_SUCCESS) { HDF_LOGE("UartSetBaud: failed, ret %d\n", ret); + return ret; } ``` +#### Obtaining the UART Baud Rate -### Obtaining the UART Baud Rate - -Call **UartGetBaud()** to obtain the UART baud rate. +Use **UartGetBaud()** to obtain the UART baud rate. - -``` +```c int32_t UartGetBaud(DevHandle handle, uint32_t *baudRate); ``` - **Table 4** Description of UartGetBaud +**Table 4** Description of UartGetBaud -| Parameter| Description| +| Parameter| Description| | -------- | -------- | -| handle | UART device handle.| -| baudRate | Pointer to the UART baud rate obtained.| -| **Return Value**| **Description**| -| 0 | The operation is successful.| -| Negative value| The operation failed.| +| handle | UART device handle.| +| baudRate | Pointer to the UART baud rate obtained.| +| **Return Value**| **Description**| +| HDF_SUCCESS | The operation is successful.| +| Negative value| The operation fails.| Example: Obtain the UART baud rate. - -``` +```c int32_t ret; uint32_t baudRate; -/* Obtain the UART baud rate. */ -ret = UartGetBaud(handle, &baudRate); -if (ret != 0) { + +ret = UartGetBaud(handle, &baudRate); // Obtain the UART baud rate. +if (ret != HDF_SUCCESS) { HDF_LOGE("UartGetBaud: failed, ret %d\n", ret); + return ret; } ``` +#### Setting UART Device Attributes -### Setting UART Device Attributes +Use **UartSetAttribute()** to set UART device attributes. -Call **UartSetAttribute()** to set UART device attributes. - - -``` +```c int32_t UartSetAttribute(DevHandle handle, struct UartAttribute *attribute); ``` - **Table 5** Description of UartSetAttribute +**Table 5** Description of UartSetAttribute -| Parameter| Description| +| Parameter| Description| | -------- | -------- | -| handle | UART device handle.| -| attribute | Pointer to the UART device attributes to set.| -| **Return Value**| **Description**| -| 0 | The operation is successful.| -| Negative value| The operation failed.| +| handle | UART device handle.| +| attribute | Pointer to the UART device attributes to set.| +| **Return Value**| **Description**| +| HDF_SUCCESS | The operation is successful.| +| Negative value| The operation fails.| Example: Set UART device attributes. - -``` +```c int32_t ret; struct UartAttribute attribute; -attribute.dataBits = UART_ATTR_DATABIT_7; /* Enable 7 bits to be transferred each time. */ -attribute.parity = UART_ATTR_PARITY_NONE; /* Disable parity check. */ -attribute.stopBits = UART_ATTR_STOPBIT_1; /* Set the stop bit to 1. */ -attribute.rts = UART_ATTR_RTS_DIS; /* Disable the RTS signal. */ -attribute.cts = UART_ATTR_CTS_DIS; /* Disable the CTS signal. */ -attribute.fifoRxEn = UART_ATTR_RX_FIFO_EN; /* Enable RX FIFO. */ -attribute.fifoTxEn = UART_ATTR_TX_FIFO_EN; /* Enable TX FIFO. */ -/* Set UART device attributes. */ -ret = UartSetAttribute(handle, &attribute); -if (ret != 0) { + +attribute.dataBits = UART_ATTR_DATABIT_7; // Transfer 7 bits each time. +attribute.parity = UART_ATTR_PARITY_NONE; // Disable parity check for the data to transfer. +attribute.stopBits = UART_ATTR_STOPBIT_1; // Set the stop bit to 1. +attribute.rts = UART_ATTR_RTS_DIS; // Disable RTS. +attribute.cts = UART_ATTR_CTS_DIS; // Disable CTS. +attribute.fifoRxEn = UART_ATTR_RX_FIFO_EN; // Enable RX FIFO. +attribute.fifoTxEn = UART_ATTR_TX_FIFO_EN; // Enable TX FIFO. + +ret = UartSetAttribute(handle, &attribute); // Set UART device attributes. +if (ret != HDF_SUCCESS) { HDF_LOGE("UartSetAttribute: failed, ret %d\n", ret); +turn ret; } ``` +#### Obtaining UART Device Attributes -### Obtaining UART Device Attributes - -Call **UartGetAttribute()** to obtain the current UART device attributes. +Use **UartGetAttribute()** to obtain the UART device attributes. - -``` +```c int32_t UartGetAttribute(DevHandle handle, struct UartAttribute *attribute); ``` - **Table 6** Description of UartGetAttribute +**Table 6** Description of UartGetAttribute -| Parameter| Description| +| Parameter| Description| | -------- | -------- | -| handle | UART device handle.| -| attribute | Pointer to the UART device attributes obtained.| -| **Return Value**| **Description**| -| 0 | The operation is successful.| -| Negative value| The operation failed.| +| handle | UART device handle.| +| attribute | Pointer to the UART device attributes obtained.| +| **Return Value**| **Description**| +| HDF_SUCCESS | The operation is successful.| +| Negative value| The operation fails.| Example: Obtain UART device attributes. - -``` +```c int32_t ret; struct UartAttribute attribute; -/* Obtain UART device attributes. */ -ret = UartGetAttribute(handle, &attribute); -if (ret != 0) { + +ret = UartGetAttribute(handle, &attribute); // Obtain the attributes of the UART device. +if (ret != HDF_SUCCESS) { HDF_LOGE("UartGetAttribute: failed, ret %d\n", ret); + return ret; } ``` +#### Setting the UART Transmission Mode -### Setting the UART Transmission Mode +Use **UartSetTransMode()** to set the UART transmission mode. -Call **UartSetTransMode()** to set the UART transmission mode. - - -``` +```c int32_t UartSetTransMode(DevHandle handle, enum UartTransMode mode); ``` - **Table 7** Description of UartSetTransMode +**Table 7** Description of UartSetTransMode -| Parameter| Description| +| Parameter| Description| | -------- | -------- | -| handle | UART device handle.| -| mode | UART transmission mode to set.| -| **Return Value**| **Description**| -| 0 | The operation is successful.| -| Negative value| The operation failed.| +| handle | UART device handle.| +| mode | UART transmission mode to set.| +| **Return Value**| **Description**| +| HDF_SUCCESS | The operation is successful.| +| Negative value| The operation fails.| Example: Set the UART transmission mode to **UART_MODE_RD_BLOCK**. - -``` +```c int32_t ret; -/* Set the UART transmission mode. */ -ret = UartSetTransMode(handle, UART_MODE_RD_BLOCK); -if (ret != 0) { + +ret = UartSetTransMode(handle, UART_MODE_RD_BLOCK); // Sets the UART transmission mode. +if (ret != HDF_SUCCESS) { HDF_LOGE("UartSetTransMode: failed, ret %d\n", ret); + return ret; } ``` +#### Writing Data to a UART Device -### Writing Data to a UART Device +Use **UartWrite()** to write data of the specified length to a UART device. -Call **UartWrite()** to write data of the specified length to a UART device. - - -``` +```c int32_t UartWrite(DevHandle handle, uint8_t *data, uint32_t size); ``` - **Table 8** Description of UartWrite +**Table 8** Description of UartWrite -| Parameter| Description| +| Parameter| Description| | -------- | -------- | -| handle | UART device handle.| -| data | Pointer to the data to write.| -| size | Length of the data to write.| -| **Return Value**| **Description**| -| 0 | The operation is successful.| -| Negative value| The operation failed.| +| handle | UART device handle.| +| data | Pointer to the data to write.| +| size | Length of the data to write.| +| **Return Value**| **Description**| +| HDF_SUCCESS | The operation is successful.| +| Negative value| The operation fails.| Example: Write data to a UART device. - -``` +```c int32_t ret; uint8_t wbuff[5] = {1, 2, 3, 4, 5}; -/* Write 5-byte data to the UART device. */ -ret = UartWrite(handle, wbuff, 5); -if (ret != 0) { + +ret = UartWrite(handle, wbuff, 5); // Write data of the specified length to the UART device. +if (ret != HDF_SUCCESS) { HDF_LOGE("UartWrite: failed, ret %d\n", ret); + return ret; } ``` +#### Reading Data from a UART Device -### Reading Data from a UART Device - -Call **UartRead()** to read data of the specified length from a UART device. +Use **UartRead()** to read data of the specified length from a UART device. - -``` +```c int32_t UartRead(DevHandle handle, uint8_t *data, uint32_t size); ``` - **Table 9** Description of UartRead +**Table 9** Description of UartRead -| Parameter| Description| +| Parameter| Description| | -------- | -------- | -| handle | UART device handle.| -| data | Pointer to the buffer for receiving the data.| -| size | Length of the data to read.| -| **Return Value**| **Description**| -| Non-negative value| The operation is successful. The length of the data read is returned.| -| Negative value| The operation failed.| +| handle | UART device handle.| +| data | Pointer to the buffer for receiving the data.| +| size | Length of the data to read.| +| **Return Value**| **Description**| +| Non-negative value| The operation is successful. The length of the data read is returned.| +| Negative value| The operation fails.| Example: Read data of the specified length from a UART device. - -``` +```c int32_t ret; uint8_t rbuff[5] = {0}; -/* Read 5-byte data from the UART device. */ -ret = UartRead(handle, rbuff, 5); + +ret = UartRead(handle, rbuff, 5); // Read data of the specified length from the UART device. if (ret < 0) { HDF_LOGE("UartRead: failed, ret %d\n", ret); + return ret; } ``` @@ -330,94 +355,115 @@ if (ret < 0) { > Data is successfully read from the UART device if a non-negative value is returned. If **0** is returned, no valid data can be read from the UART device. A value greater than **0** indicates the length of the data read from the UART device. The data length must be less than or equal to the value of **size** and cannot exceed the maximum length of the data to read at a time specified by the UART controller in use. -### Closing a UART Device Handle +#### Closing a UART Device -Call **UartClose()** to close a UART device handle. +Use **UartClose()** to close a UART device. - -``` +```c void UartClose(DevHandle handle); ``` This function releases the resources requested by **UartOpen**. - **Table 10** Description of UartClose +**Table 10** Description of UartClose -| Parameter| Description| +| Parameter| Description| | -------- | -------- | -| handle | UART device handle to close.| +| handle | UART device handle to close.| -Example: Close a UART device handle. +Example: Close a UART device. - +```c +UartClose(handle); // Close a UART device to release resources. ``` -UartClose(handle); /* Close the UART device handle. */ -``` - ## Example - The following example shows how to open a UART device handle, set the baud rate, device attributes, and transmission mode, read data from or write data into the UART device, and then close the UART device handle. - -``` +The following uses the Hi3516D V300 development board as an example to describe how to manage the UART device. The procedure is as follows: + +1. Open a UART device based on the port number. The handle of the UART device opened is returned. +2. Set the baud rate of the UART device. +3. Obtain the baud rate of the UART device. +4. Set the attributes of the UART device. +5. Obtain the attributes of the UART device. +6. Set the transmission mode of the UART device. +7. Transfer data of the specified length. +8. Receive data of the specified length. +9. Closes the UART device. + +```c #include "hdf_log.h" #include "uart_if.h" void UartTestSample(void) { int32_t ret; - uint32_t port; + uint32_t port; + uint32_t baud; DevHandle handle = NULL; uint8_t wbuff[5] = { 1, 2, 3, 4, 5 }; uint8_t rbuff[5] = { 0 }; struct UartAttribute attribute; - attribute.dataBits = UART_ATTR_DATABIT_7; /* Enable 7 bits to be transferred each time. */ - attribute.parity = UART_ATTR_PARITY_NONE; /* Disable parity check. */ - attribute.stopBits = UART_ATTR_STOPBIT_1; /* Set the stop bit to 1. */ - attribute.rts = UART_ATTR_RTS_DIS; /* Disable the RTS signal. */ - attribute.cts = UART_ATTR_CTS_DIS; /* Disable the CTS signal. */ - attribute.fifoRxEn = UART_ATTR_RX_FIFO_EN; /* Enable RX FIFO. */ - attribute.fifoTxEn = UART_ATTR_TX_FIFO_EN; /* Enable TX FIFO. */ - /* Enter the UART port number. */ - port = 1; - /* Open the UART device handle based on the port number. */ - handle = UartOpen(port); + + attribute.dataBits = UART_ATTR_DATABIT_7; // Transfer 7 bits each time. + attribute.parity = UART_ATTR_PARITY_NONE; // Disable parity check. + attribute.stopBits = UART_ATTR_STOPBIT_1; // Set the stop bit to 1. + attribute.rts = UART_ATTR_RTS_DIS; // Disable RTS. + attribute.cts = UART_ATTR_CTS_DIS; // Disable CTS. + attribute.fifoRxEn = UART_ATTR_RX_FIFO_EN; // Enable RX FIFO. + attribute.fifoTxEn = UART_ATTR_TX_FIFO_EN; // Enable TX FIFO. + + port = 1; // UART device port number. + + handle = UartOpen(port); // Open a UART device. if (handle == NULL) { - HDF_LOGE("UartOpen: failed!\n"); + HDF_LOGE("UartOpen: open uart_%u failed!\n", port); return; } - /* Set the UART baud rate to 9600. */ - ret = UartSetBaud(handle, 9600); - if (ret != 0) { - HDF_LOGE("UartSetBaud: failed, ret %d\n", ret); - goto _ERR; + + ret = UartSetBaud(handle, 9600); // Set the UART baud rate to 9600. + if (ret != HDF_SUCCESS) { + HDF_LOGE("UartSetBaud: set baud failed, ret %d\n", ret); + goto ERR; } - /* Set UART device attributes. */ - ret = UartSetAttribute(handle, &attribute); - if (ret != 0) { - HDF_LOGE("UartSetAttribute: failed, ret %d\n", ret); - goto _ERR; + + ret = UartGetBaud(handle, &baud); // Obtain the UART baud rate. + if (ret != HDF_SUCCESS) { + HDF_LOGE("UartGetBaud: get baud failed, ret %d\n", ret); + goto ERR; + } + + ret = UartSetAttribute(handle, &attribute); // Set the attributes of the UART device. + if (ret != HDF_SUCCESS) { + HDF_LOGE("UartSetAttribute: set attribute failed, ret %d\n", ret); + goto ERR; + } + + ret = UartGetAttribute(handle, &attribute); // Obtain the attributes of the UART device. + if (ret != HDF_SUCCESS) { + HDF_LOGE("UartGetAttribute: get attribute failed, ret %d\n", ret); + goto ERR; } - /* Set the UART transmission mode to non-blocking mode. */ - ret = UartSetTransMode(handle, UART_MODE_RD_NONBLOCK); - if (ret != 0) { - HDF_LOGE("UartSetTransMode: failed, ret %d\n", ret); - goto _ERR; + + ret = UartSetTransMode(handle, UART_MODE_RD_NONBLOCK); // Set the UART transmission mode to non-block mode. + if (ret != HDF_SUCCESS) { + HDF_LOGE("UartSetTransMode: set trans mode failed, ret %d\n", ret); + goto ERR; } - /* Write 5-byte data to the UART device. */ - ret = UartWrite(handle, wbuff, 5); - if (ret != 0) { - HDF_LOGE("UartWrite: failed, ret %d\n", ret); - goto _ERR; + + ret = UartWrite(handle, wbuff, 5); // Write 5-byte data to the UART device. + if (ret != HDF_SUCCESS) { + HDF_LOGE("UartWrite: write data failed, ret %d\n", ret); + goto ERR; } - /* Read 5-byte data from the UART device. */ - ret = UartRead(handle, rbuff, 5); + + ret = UartRead(handle, rbuff, 5); // Read 5-byte data from the UART device. if (ret < 0) { - HDF_LOGE("UartRead: failed, ret %d\n", ret); - goto _ERR; + HDF_LOGE("UartRead: read data failed, ret %d\n", ret); + goto ERR; } -_ERR: - /* Close the UART device handle. */ - UartClose(handle); +ERR: + UartClose(handle); // Close the UART device. + return ret; } ``` diff --git a/en/device-dev/driver/driver-platform-uart-develop.md b/en/device-dev/driver/driver-platform-uart-develop.md index cf3393d01505ceccb7c34b643a9ce218f148ce6c..eb08e87a54b68cae652c70e9018e9c0e73d26704 100644 --- a/en/device-dev/driver/driver-platform-uart-develop.md +++ b/en/device-dev/driver/driver-platform-uart-develop.md @@ -1,256 +1,293 @@ # UART - ## Overview -In the Hardware Driver Foundation (HDF), the Universal Asynchronous Receiver/Transmitter (UART) uses the independent service mode for API adaptation. In this mode, each device independently publishes a service to process external access requests. When receiving an access request, the HDF DeviceManager extracts parameters from the request to call the internal APIs of the target device. In the independent service mode, the HDF DeviceManager provides service management capabilities. However, you need to configure a node for each device, which increases memory usage. +### Function - **Figure 1** Independent service mode +The Universal Asynchronous Receiver/Transmitter (UART) is a universal serial data bus used for asynchronous communication. It enables bi-directional communication between devices in full-duplex mode. - ![image](figures/independent-service-mode.png) +A UART is connected to other modules through two wires (as shown in Figure 1) or four wires (as shown in Figure 2). + - TX: UART transmitter. It is connected to the RX of the peer UART. + - RX: UART receiver. It is connected to the TX of the peer UART. + - RTS: Request to Send signal, indicating whether the local UART is ready to receive data. It is connected to the CTS of the peer UART. + - CTS: Clear to Send signal, indicating whether the local UART is allowed to send data to the peer end. It is connected to the RTS of the peer UART. -## Available APIs +**Figure 1** Two-wire UART communication -**UartHostMethod**: +![image1](figures/2-wire-uart-communication.png "2-wire-uart-communication") +**Figure 2** Four-wire UART communication -``` + ![image2](figures/4-wire-uart-communication.png "4-wire-uart-communication") + +The UART transmitter and receiver must have the same settings on particular attributes, such as the baud rate and data format (start bit, data bits, parity bit, and stop bit) before they start to communicate. A UART sends data to the peer end over the TX and receives data from the peer end over the RX. When the size of the buffer used by a UART for storing received data reaches the preset threshold, the RTS signal of the UART changes to **1** (data cannot be received), and the peer UART stops sending data to it because its CTS signal does not allow it to send data. + +### Basic Concepts + +- Asynchronous communication + + In asynchronous communication, data is transmitted in frames of characters or bytes. Frames are sent and received one by one through the transmission line. The transmitter and receiver have their own clocks to control data sending and receiving. The two clock sources are independent and not synchronized with each other. + + When data is sent one character at a time, the time interval between two characters is not fixed, but the time interval between two adjacent bits in a character frame is fixed. + +- Full-duplex transmission + + A duplex communication mode allows data to be transmitted in both directions at the same time. A duplex communication channel is equivalent to two simplex communication channels operating in opposite directions at the same time. In full-duplex mode, signals can be transmitted bidirectionally at the same time. + +### Working Principles + +In the Hardware Driver Foundation (HDF), the UART uses the independent service mode (see Figure 3) for API adaptation. In this mode, each device independently publishes a service to process external access requests. When receiving an access request, the HDF DeviceManager extracts parameters from the request to call the internal APIs of the target device. In the independent service mode, the HDF DeviceManager provides service management capabilities. However, you need to configure a node for each device, which increases memory usage. + +In the independent service mode, the core layer does not publish a service for the upper layer. Therefore, a service must be published for each controller. To achieve this purpose: + +- You need to implement the **Bind()** function in **HdfDriverEntry** to bind services. +- The **policy** field of **deviceNode** in the **device_info.hcs** file can be **1** or **2**, but not **0**. + +The UART module is divided into the following layers: + +- Interface layer: provides APIs for opening or closing a UART device, reading or writing data of the specified length, setting or obtaining the baud rate or attributes of a UART device, and setting the transmission mode. +- Core layer: provides the capabilities of adding or removing a UART controller, and managing UART devices. The core layer interacts with the adaptation layer through hook functions. +- Adaptation layer: instantiates the hook functions to implement specific features. + +**Figure 3** Independent service mode + +![image3](figures/independent-service-mode.png) + +## Development Guidelines + +### When to Use + +The UART module is widely used to implement low-speed serial communication between devices, for example, output the printing information. It can also connect to a variety of external GPS and Bluetooth devices. Before using your UART devices with OpenHarmony, you need to perform UART driver adaptation. + +### Available APIs + +To enable the upper layer to successfully operate the PWM controller by calling the UART APIs, hook functions are defined in **//drivers/hdf_core/framework/support/platform/include/uart/uart_core.h** for the core layer. You need to implement these hook functions at the adaptation layer and hook them to implement the interaction between the interface layer and the core layer. + +**UartHostMethod**: + +```c struct UartHostMethod { - int32_t (*Init)(struct UartHost *host); - int32_t (*Deinit)(struct UartHost *host); - int32_t (*Read)(struct UartHost *host, uint8_t *data, uint32_t size); - int32_t (*Write)(struct UartHost *host, uint8_t *data, uint32_t size); - int32_t (*GetBaud)(struct UartHost *host, uint32_t *baudRate); - int32_t (*SetBaud)(struct UartHost *host, uint32_t baudRate); - int32_t (*GetAttribute)(struct UartHost *host, struct UartAttribute *attribute); - int32_t (*SetAttribute)(struct UartHost *host, struct UartAttribute *attribute); - int32_t (*SetTransMode)(struct UartHost *host, enum UartTransMode mode); - int32_t (*pollEvent)(struct UartHost *host, void *filep, void *table); + int32_t (*Init)(struct UartHost *host); + int32_t (*Deinit)(struct UartHost *host); + int32_t (*Read)(struct UartHost *host, uint8_t *data, uint32_t size); + int32_t (*Write)(struct UartHost *host, uint8_t *data, uint32_t size); + int32_t (*GetBaud)(struct UartHost *host, uint32_t *baudRate); + int32_t (*SetBaud)(struct UartHost *host, uint32_t baudRate); + int32_t (*GetAttribute)(struct UartHost *host, struct UartAttribute *attribute); + int32_t (*SetAttribute)(struct UartHost *host, struct UartAttribute *attribute); + int32_t (*SetTransMode)(struct UartHost *host, enum UartTransMode mode); + int32_t (*pollEvent)(struct UartHost *host, void *filep, void *table); }; ``` - **Table 1** Description of the callback functions in UartHostMethod +**Table 1** Hook functions in UartHostMethod | Function| Input Parameter| Output Parameter| Return Value| Description| | -------- | -------- | -------- | -------- | -------- | | Init | **host**: structure pointer to the UART controller at the core layer.| –| HDF_STATUS| Initializes a UART device.| | Deinit | **host**: structure pointer to the UART controller at the core layer.| –| HDF_STATUS| Deinitializes a UART device.| -| Read | **host**: structure pointer to the UART controller at the core layer.
**size**: data size, which is of the uint32_t type.| **data**: pointer to the data read. The value is of the uint8_t type. | HDF_STATUS| Reads data.| -| Write | **host**: structure pointer to the UART controller at the core layer.
**data**: pointer to the data to write. The value is of the uint8_t type.
**size**: data size, which is of the uint32_t type. | –| HDF_STATUS| Writes data.| -| SetBaud | **host**: structure pointer to the UART controller at the core layer.
**baudRate**: pointer to the baud rate to set. The value is of the uint32_t type. | –| HDF_STATUS| Sets the baud rate.| -| GetBaud | **host**: structure pointer to the UART controller at the core layer.| **baudRate**: pointer to the baud rate obtained. The value is of the uint32_t type. | HDF_STATUS| Obtains the current baud rate.| -| GetAttribute | **host**: structure pointer to the UART controller at the core layer.| **attribute**: structure pointer to the attribute obtained. For details, see **UartAttribute** in **uart_if.h**. | HDF_STATUS| Obtains UART attributes.| -| SetAttribute | **host**: structure pointer to the UART controller at the core layer.
**attribute**: structure pointer to the attribute to set. | –| HDF_STATUS| Sets UART attributes.| -| SetTransMode | **host**: structure pointer to the UART controller at the core layer.
**mode**: transfer mode to set. For details, see **UartTransMode** in **uart_if.h**.| –| HDF_STATUS| Sets the UART transfer mode.| -| PollEvent | **host**: structure pointer to the UART controller at the core layer.
**filep**: void pointer to a file.
**table**: void pointer to poll_table.| –| HDF_STATUS| Polls for pending events.| - +| Read | **host**: structure pointer to the UART controller at the core layer.
**size**: size of the data to read, which is of the uint32_t type.| **data**: pointer to the data read, which is of the uint8_t type. | HDF_STATUS| Reads data.| +| Write | **host**: structure pointer to the UART controller at the core layer.
**data**: pointer to the data to write, which is of the uint8_t type.
**size**: size of the data to write, which is of the uint32_t type.| –| HDF_STATUS| Writes data.| +| SetBaud | **host**: structure pointer to the UART controller at the core layer.
**baudRate**: baud rate to set, which is of the uint32_t type.| –| HDF_STATUS| Sets the baud rate.| +| GetBaud | **host**: structure pointer to the UART controller at the core layer.| **baudRate**: pointer to the baud rate obtained, which is of the uint32_t type.| HDF_STATUS| Obtains the baud rate.| +| GetAttribute | **host**: structure pointer to the UART controller at the core layer.| **attribute**: structure pointer to the attributes obtained. For details, see **UartAttribute** in **uart_if.h**.| HDF_STATUS| Obtains UART attributes.| +| SetAttribute | **host**: structure pointer to the UART controller at the core layer.
**attribute**: structure pointer to the attributes to set.| –| HDF_STATUS| Sets UART attributes.| +| SetTransMode | **host**: structure pointer to the UART controller at the core layer.
**mode**: transmission mode to set. For details, see **UartTransMode** in **uart_if.h**.| –| HDF_STATUS| Sets the UART transmission mode.| +| PollEvent | **host**: structure pointer to the UART controller at the core layer.
**filep**: void pointer to a file.
**table**: void pointer to the poll_table.| –| HDF_STATUS| Polls for the pending events.| -## How to Develop +### How to Develop -The UART module adaptation involves the following steps: +The UART module adaptation procedure is as follows: 1. Instantiate the driver entry. - - Instantiate the **HdfDriverEntry** structure. - - Call **HDF_INIT** to register the **HdfDriverEntry** instance with the HDF. - 2. Configure attribute files. - - Add the **deviceNode** information to the **device_info.hcs** file. - - (Optional) Add the **uart_config.hcs** file. - 3. Instantiate the UART controller object. - - Initialize **UartHost**. - - Instantiate **UartHostMethod** in the **UartHost** object. - > ![icon-note.gif](public_sys-resources/icon-note.gif) **NOTE**
- > For details about the functions in **UartHostMethod**, see [Available APIs](#available-apis). - 4. Debug the driver. - (Optional) For new drivers, verify the basic functions, such as the UART status control and response to interrupts. +### Example - -## Development Example - -The following uses **uart_hi35xx.c** as an example to present the information required for implementing device functions. +The following uses the **//device_soc_hisilicon/common/platform/uart/uart_hi35xx.c** driver of the Hi3516D V300 development board as an example to describe the UART driver adaptation. 1. Instantiate the driver entry. - The driver entry must be a global variable of the **HdfDriverEntry** type (defined in **hdf_device_desc.h**), and the value of **moduleName** must be the same as that in **device_info.hcs**. - - In the HDF, the start address of each **HdfDriverEntry** object of all loaded drivers is collected to form a segment address space similar to an array for the upper layer to invoke. - - Generally, the HDF calls the **Bind** function and then the **Init** function to load a driver. If **Init** fails to be called, the HDF calls **Release** to release driver resources and exit. + The driver entry must be a global variable of the **HdfDriverEntry** type (defined in **hdf_device_desc.h**), and the value of **moduleName** must be the same as that in **device_info.hcs**. In the HDF, the start address of each **HdfDriverEntry** object of all loaded drivers is collected to form a segment address space similar to an array for the upper layer to invoke. + Generally, the HDF calls **Bind()** and then **Init()** to load a driver. If **Init()** fails to be called, the HDF calls **Release()** to release driver resources and exit. UART driver entry example: - - ``` + + ```c struct HdfDriverEntry g_hdfUartDevice = { .moduleVersion = 1, - .moduleName = "HDF_PLATFORM_UART", // (Mandatory) The value must be the same as that in the .hcs file. - .Bind = HdfUartDeviceBind, // See the Bind function. - .Init = HdfUartDeviceInit, // See the Init function. - .Release = HdfUartDeviceRelease, //See the Release function. + .moduleName = "HDF_PLATFORM_UART", // (Mandatory) The value must be the same as that of moduleName in the .hcs file. + .Bind = HdfUartDeviceBind, // See the Bind function. + .Init = HdfUartDeviceInit, // See the Init function. + .Release = HdfUartDeviceRelease, // See the Release function. }; - // Call HDF_INIT to register the driver entry with the HDF. - HDF_INIT(g_hdfUartDevice); + HDF_INIT(g_hdfUartDevice); // Call HDF_INIT to register the driver entry with the HDF. ``` -2. Add the **deviceNode** information to the **device_info.hcs** file and configure the device attributes in the **uart_config.hcs** file. - - The **deviceNode** information is related to registration of the driver entry. The device attribute values are closely related to the default values or value ranges of the **UartHost** members at the core layer. - - In this example, there is only one UART controller. If there are multiple UART controllers, you need to add the **deviceNode** information to the **device_info** file and add the corresponding device attributes to the **uart_config** file for each controller. - - - **device_info.hcs** configuration example: - - - ``` - root { - device_info { - match_attr = "hdf_manager"; - platform :: host { - hostName = "platform_host"; - priority = 50; - device_uart :: device { - device0 :: deviceNode { - policy = 1; // The driver publishes services only for kernel-mode processes. - priority = 40; // Driver startup priority. - permission = 0644; // Permission for the driver to create a device node. - moduleName = "HDF_PLATFORM_UART"; // Driver name, which must be the same as moduleName in the HdfDriverEntry structure. - serviceName = "HDF_PLATFORM_UART_0"; // Unique name of the service published by the driver. The name is in the HDF_PLATFORM_UART_X format. X indicates the UART controller number. - deviceMatchAttr = "hisilicon_hi35xx_uart_0"; // Keyword for matching the private data of the driver. The value must be the same as that of match_attr in the private data configuration table of the driver. - } - device1 :: deviceNode { - policy = 2; // The driver publishes services for both kernel- and user-mode processes. - permission = 0644; - priority = 40; - moduleName = "HDF_PLATFORM_UART"; - serviceName = "HDF_PLATFORM_UART_1"; - deviceMatchAttr = "hisilicon_hi35xx_uart_1"; - } - ... - } - } - } - } - ``` - - - **uart_config.hcs** configuration example - - - ``` - root { - platform { - template uart_controller { // Template configuration. In the template, you can configure the common parameters shared by device nodes. - match_attr = ""; - num = 0; // (Mandatory) Device number. - baudrate = 115200; // (Mandatory) Baud rate. Set the value based on service requirements. - fifoRxEn = 1; // (Mandatory) Enable FIFOs to be received. - fifoTxEn = 1; // (Mandatory) Enable FIFOs to be transferred. - flags = 4; // (Mandatory) Flag signal. - regPbase = 0x120a0000; // (Mandatory) Used for address mapping. - interrupt = 38; // (Mandatory) Interrupt number. - iomemCount = 0x48; // (Mandatory) Used for address mapping. - } - controller_0x120a0000 :: uart_controller { - match_attr = "hisilicon_hi35xx_uart_0";// (Mandatory) The value must be the same as that of deviceMatchAttr of the corresponding device in device_info.hcs. - } - controller_0x120a1000 :: uart_controller { - num = 1; - baudrate = 9600; - regPbase = 0x120a1000; - interrupt = 39; - match_attr = "hisilicon_hi35xx_uart_1"; - } - ... - //(Optional) Add more controller data. The node information must have been added in the device_info.hcs file. - } - } - ``` - -3. Initialize the **UartHost** object at the core layer, including defining a custom structure (to pass parameters and data) and implementing the **HdfDriverEntry** member functions (**Bind**, **Init**, and **Release**) to instantiate **UartHostMethod** in **UartHost** (so that the underlying driver functions can be called). - - - Defining a custom structure - - To the driver, the custom structure holds parameters and data. The **DeviceResourceIface** method provided by the HDF reads the values in the **uart_config.hcs** file to initialize the members in the custom structure and passes important parameters, such as the device number, to the **UartHost** object at the core layer. +2. Configure attribute files. - + Add the deviceNode information to the **device_info.hcs** file. The deviceNode information is related to the driver entry registration. The following example uses two UART controllers as an example. If there are more UART controllers, add the deviceNode information to the **device_info.hcs** file for each controller. The device attribute values configured in **uart_config.hcs** are closely related to default values or value ranges of the **UartHost** members at the core layer. + + - **device_info.hcs** example: + + Add the deviceNode information to the **//vendor/hisilicon/hispark_taurus/hdf_config/device_info/device_info.hcs** file. + + ```c + root { + device_info { + match_attr = "hdf_manager"; + platform :: host { + hostName = "platform_host"; + priority = 50; + device_uart :: device { + device0 :: deviceNode { + policy = 1; // The value 1 means to publish services only to the kernel-mode processes. + priority = 40; // Driver startup priority. + permission = 0644; // Permission for the device node created. + moduleName = "HDF_PLATFORM_UART"; // Driver name, which must be the same as moduleName in the HdfDriverEntry structure. + serviceName = "HDF_PLATFORM_UART_0"; // Unique name of the service published by the driver. The name is in the HDF_PLATFORM_UART_X format. X indicates the UART controller number. + deviceMatchAttr = "hisilicon_hi35xx_uart_0";// Keyword for matching the private data of the driver. The value must be the same as that of match_attr in the private data configuration table of the driver. + } + device1 :: deviceNode { + policy = 2; // The value 2 means to publish services for both kernel- and user-mode processes. + permission = 0644; + priority = 40; + moduleName = "HDF_PLATFORM_UART"; + serviceName = "HDF_PLATFORM_UART_1"; + deviceMatchAttr = "hisilicon_hi35xx_uart_1"; + } + ... + } + } + } + } + ``` + + - **uart_config.hcs** example + + Configure the device attributes in the **//device/soc/hisilicon/hi3516dv300/sdk_liteos/hdf_config/uart/uart_config.hcs** file. The parameters are as follows: + + ```c + root { + platform { + template uart_controller { // Template configuration. If the template is used to configure device node information, the default values in the template will be used for the fields that are not declared for the node. + match_attr = ""; + num = 0; // (Mandatory) Port number. + baudrate = 115200; // (Mandatory) Baud rate. + fifoRxEn = 1; // (Mandatory) Enable RX FIFO. + fifoTxEn = 1; // (Mandatory) Enable TX FIFO. + flags = 4; // (Mandatory) flag signal. + regPbase = 0x120a0000; // (Mandatory) Register physical base address used for address mapping. + interrupt = 38; // (Mandatory) Interrupt number. + iomemCount = 0x48; // (Mandatory) Used for address mapping. + } + controller_0x120a0000 :: uart_controller { + match_attr = "hisilicon_hi35xx_uart_0"; // (Mandatory) The value must be the same as that of deviceMatchAttr of the device in device_info.hcs. + } + controller_0x120a1000 :: uart_controller { + num = 1; + baudrate = 9600; + regPbase = 0x120a1000; + interrupt = 39; + match_attr = "hisilicon_hi35xx_uart_1"; + } + ... // Add node information for more UART devices. + } + } + ``` + + After the **uart_config.hcs** file is configured, include the file in the **hdf.hcs** file. Otherwise, the configuration file cannot take effect. + + ```c + #include "../../../../device/soc/hisilicon/hi3516dv300/sdk_liteos/hdf_config/uart/uart_config.hcs" // Relative path of the file. ``` - struct UartPl011Port { // Interface structure - int32_t enable; - unsigned long physBase; // Physical address - uint32_t irqNum; // Interrupt number - uint32_t defaultBaudrate; // Default baud rate - uint32_t flags; // Flags related to the following three macros + +3. Instantiate the UART controller object. + + Initialize the **UartHost** object at the core layer, including defining a custom structure (to pass parameters and data) and implementing the **HdfDriverEntry** member functions (**Bind**, **Init** and **Release**) to instantiate **UartHostMethod** in **UartHost** (so that the underlying driver functions can be called). + + - Define a custom structure. + + To the driver, the custom structure holds parameters and data. The **DeviceResourceIface** method provided by the HDF reads the values in the **uart_config.hcs** file to initialize the members in the custom structure and passes important parameters, such as the UART port number, to the object at the core layer. + + ```c + struct UartPl011Port { // Pin description structure customized. + int32_t enable; + unsigned long physBase; // Physical base address. + uint32_t irqNum; // IRQ number. + uint32_t defaultBaudrate; // Default baud rate. + uint32_t flags; // Flag signals related to the following three macros. #define PL011_FLG_IRQ_REQUESTED (1 << 0) #define PL011_FLG_DMA_RX_REQUESTED (1 << 1) #define PL011_FLG_DMA_TX_REQUESTED (1 << 2) - struct UartDmaTransfer *rxUdt; // DMA transfer - struct UartDriverData *udd; // The data structure is defined as follows: + struct UartDmaTransfer *rxUdt; // DMA transfer. + struct UartDriverData *udd; }; - struct UartDriverData { // Structure related to data transfer - uint32_t num; - uint32_t baudrate; // Baud rate (configurable) - struct UartAttribute attr; // Attributes, such as the data bit and stop bit, related to data transfer. - struct UartTransfer *rxTransfer; // Buffer (FIFO structure) - wait_queue_head_t wait; // Queuing signal related to conditional variables - int32_t count; // Data count - int32_t state; // UART controller state + struct UartDriverData { // Structure related to data transfer + uint32_t num; // Port number. + uint32_t baudrate; // Baud rate (configurable). + struct UartAttribute attr; // Attributes, such as the data bits and stop bit of the data to transfer. + struct UartTransfer *rxTransfer; // Buffer structure (FIFO structure) + wait_queue_head_t wait; // Queuing signal related to conditional variables + int32_t count; // Data count. + int32_t state; // UART controller state. #define UART_STATE_NOT_OPENED 0 #define UART_STATE_OPENING 1 #define UART_STATE_USEABLE 2 #define UART_STATE_SUSPENDED 3 - uint32_t flags; // Status flags + uint32_t flags; // Status flags. #define UART_FLG_DMA_RX (1 << 0) #define UART_FLG_DMA_TX (1 << 1) #define UART_FLG_RD_BLOCK (1 << 2) - RecvNotify recv; // Pointer to the function that receives serial port data. - struct UartOps *ops; // Custom function pointer structure. For details, see device/hisilicon/drivers/uart/uart_pl011.c. - void *private; // It stores the pointer to the start address of UartPl011Port for easy invocation. + RecvNotify recv; // Pointer to the function that receives serial port data. + struct UartOps *ops; // Custom function pointer structure. + void *private; // Private data. }; // UartHost is the controller structure at the core layer. The Init function assigns values to the members of UartHost. struct UartHost { - struct IDeviceIoService service; - struct HdfDeviceObject *device; - uint32_t num; - OsalAtomic atom; - void *priv; // It stores the pointer to the start address of the vendor's custom structure for easy invocation. - struct UartHostMethod *method; // Hook at the core layer. You need to implement and instantiate its member functions. + struct IDeviceIoService service; // Driver service. + struct HdfDeviceObject *device; // Driver device object. + uint32_t num; // Port number. + OsalAtomic atom; // Atomic quantity. + void *priv; // Private data. + struct UartHostMethod *method; // Callback functions. }; ``` - - Instantiating **UartHostMethod** in **UartHost** (other members are initialized by **Bind**) + - Instantiate **UartHostMethod** in **UartHost**. - - ``` - // Example in uart_hi35xx.c: instantiate the hook. + ```c + // Instantiate the hook functions in uart_hi35xx.c. struct UartHostMethod g_uartHostMethod = { - .Init = Hi35xxInit, - .Deinit = Hi35xxDeinit, - .Read = Hi35xxRead, - .Write = Hi35xxWrite, - .SetBaud = Hi35xxSetBaud, - .GetBaud = Hi35xxGetBaud, - .SetAttribute = Hi35xxSetAttribute, - .GetAttribute = Hi35xxGetAttribute, - .SetTransMode = Hi35xxSetTransMode, - .pollEvent = Hi35xxPollEvent, + .Init = Hi35xxInit, // Initialize the device. + .Deinit = Hi35xxDeinit, // Deinitialize the device. + .Read = Hi35xxRead, // Receive data. + .Write = Hi35xxWrite, // Write data. + .SetBaud = Hi35xxSetBaud, // Set the baud rate. + .GetBaud = Hi35xxGetBaud, // Obtain the baud rate. + .SetAttribute = Hi35xxSetAttribute, // Set device attributes. + .GetAttribute = Hi35xxGetAttribute, //Obtain device attributes. + .SetTransMode = Hi35xxSetTransMode, // Set the transmission mode. + .pollEvent = Hi35xxPollEvent, // Polling for pending events. }; ``` - - **Bind** function + - Implement the **Bind** function. - **Input parameter**: + Input parameter: - **HdfDeviceObject**, an interface parameter exposed by the driver, contains the .hcs information. + **HdfDeviceObject**, a device object created by the HDF for each driver, holds device-related private data and service APIs. - **Return value**: + Return value: - **HDF_STATUS**
The table below describes some status. For more information, see **HDF_STATUS** in the **/drivers/framework/include/utils/hdf_base.h** file. + **HDF_STATUS**
The table below describes some status. For more information, see **HDF_STATUS** in the **//drivers/hdf_core/framework/include/utils/hdf_base.h** file. + + **Table 2** HDF_STATUS - **Table 2** Description of HDF_STATUS - | Status| Description| | -------- | -------- | | HDF_ERR_INVALID_OBJECT | Invalid controller object.| @@ -260,89 +297,85 @@ The following uses **uart_hi35xx.c** as an example to present the information re | HDF_SUCCESS | Initialization successful.| | HDF_FAILURE | Initialization failed.| - **Function description**: + Function description: Initializes the custom structure object and **UartHost**. - - ``` + ```c //uart_hi35xx.c static int32_t HdfUartDeviceBind(struct HdfDeviceObject *device) { ... - return (UartHostCreate(device) == NULL)? HDF_FAILURE: HDF_SUCCESS;// (Mandatory) Call UartHostCreate. + return (UartHostCreate(device) == NULL) ? HDF_FAILURE : HDF_SUCCESS; // (Mandatory) Call UartHostCreate. } + // Description of UartHostCreate() in uart_core.c struct UartHost *UartHostCreate(struct HdfDeviceObject *device) { - struct UartHost *host = NULL; // Create UartHost. - ... - host = (struct UartHost *)OsalMemCalloc(sizeof(*host));// Allocate memory. + struct UartHost *host = NULL // Create UartHost. + ... + host = (struct UartHost *)OsalMemCalloc(sizeof(*host)); // Allocate memory. ... - host->device = device; // (Mandatory) Prerequisites for conversion between HdfDeviceObject and UartHost. - device->service = &(host->service; // (Mandatory) Prerequisites for conversion between HdfDeviceObject and UartHost. - host->device->service->Dispatch = UartIoDispatch; // Assign values to Dispatch of service. - OsalAtomicSet(&host->atom, 0); // Initialize or set the atomic services. + host->device = device; // (Mandatory) Prerequisites for conversion between HdfDeviceObject and UartHost. + device->service = &(host->service); // (Mandatory) Prerequisites for conversion between HdfDeviceObject and UartHost. + host->device->service->Dispatch = UartIoDispatch; // Assign values to Dispatch() of service. + OsalAtomicSet(&host->atom, 0); // Initialize or set the atomic service. host->priv = NULL; host->method = NULL; return host; } ``` - - **Init** function + - Implement the **Init** function. - **Input parameter**: + Input parameter: - **HdfDeviceObject**, an interface parameter exposed by the driver, contains the .hcs information. + **HdfDeviceObject**, a device object created by the HDF for each driver, holds device-related private data and service APIs. - **Return value**: + Return value: - **HDF_STATUS** + HDF_STATUS - **Function description**: + Function description: - Initializes the custom structure object and **UartHost**, calls the **artAddDev** function at the core layer, and connects to the VFS. + Initialize the custom structure and **UartHost**, calls **UartAddDev()** at the core layer to add the UART controller, and accesses the VFS. - - ``` + ```c int32_t HdfUartDeviceInit(struct HdfDeviceObject *device) { int32_t ret; struct UartHost *host = NULL; HDF_LOGI("%s: entry", __func__); ... - host = UartHostFromDevice(device);// Forcibly convert to UartHost by using service. The values are assigned by Bind(). - ... - ret = Hi35xxAttach(host, device); // Initialize the UartHost object. - ... - host->method = &g_uartHostMethod; // Attach the UartHostMethod instance. + host = UartHostFromDevice(device); // Forcibly convert to UartHost by using service. The values are assigned by Bind(). + ... + ret = Hi35xxAttach(host, device); // Initialize the UartHost object. + ... + host->method = &g_uartHostMethod; // Attach the UartHostMethod instance. return ret; } // Initialize UartHost. static int32_t Hi35xxAttach(struct UartHost *host, struct HdfDeviceObject *device) { int32_t ret; - // udd and port are customized structure objects. Implement the related functions as required. - struct UartDriverData *udd = NULL; + struct UartDriverData *udd = NULL; // udd and port are custom structure objects. You can implement features as required. struct UartPl011Port *port = NULL; ... - // Steps 1 to 7 instantiate and assign values to the udd object, and then assign values to UartHost. - udd = (struct UartDriverData *)OsalMemCalloc(sizeof(*udd));// Step 1 + // Steps 1 to 7 assign values to the udd object and then UartHost. + udd = (struct UartDriverData *)OsalMemCalloc(sizeof(*udd)); // Step 1 ... - port = (struct UartPl011Port *)OsalMemCalloc(sizeof(struct UartPl011Port));// Step 2 + port = (struct UartPl011Port *)OsalMemCalloc(sizeof(struct UartPl011Port)); // Step 2 ... - udd->ops = Pl011GetOps(); // Step 3 Hook the functions for starting or stopping a device, setting attributes, and sending data. - udd->recv = PL011UartRecvNotify;// Step 4 Hook the data receiving notification function (conditional lock mechanism). - udd->count = 0; // Step 5 - port->udd = udd; // Step 6 Enable conversion between UartPl011Port and UartDriverData. - ret = UartGetConfigFromHcs(port, device->property);// Pass the attributes of HdfDeviceObject to the custom structure. - // The sample code is as follows: + udd->ops = Pl011GetOps(); // Step 3 Hook the functions for opening or closing a device, setting device attributes, and sending data. + udd->recv = PL011UartRecvNotify; // Step 4 Hook the data receiving notification function (conditional lock mechanism). + udd->count = 0; // Step 5. + port->udd = udd; // Step 6 Prerequisites for conversion between UartPl011Port and UartDriverData. + ret = UartGetConfigFromHcs(port, device->property); // Pass the attributes of HdfDeviceObject to the custom structure to perform related operations. The sample code is as follows: ... - udd->private = port; // Step 7 - - host->priv = udd; // (Mandatory) Enable conversion between UartHost and UartDriverData. - host->num = udd->num; // (Mandatory) UART device number - UartAddDev(host); // (Mandatory) Function (in uart_dev.c) used to register a character device node to the VFS so that the UART can be accessed through the virtual file node in user mode. + udd->private = port; // Step 7 + host->priv = udd; // (Mandatory) Prerequisites for conversion between UartHost and UartDriverData. + host->num = udd->num; // (Mandatory) UART device number. + UartAddDev(host); // (Mandatory) Function in uart_dev.c at the core layer used to register a character device node to the VFS so that the UART can be accessed through the virtual file node in user mode. return HDF_SUCCESS; } @@ -352,7 +385,7 @@ The following uses **uart_hi35xx.c** as an example to present the information re struct UartDriverData *udd = port->udd; struct DeviceResourceIface *iface = DeviceResourceGetIfaceInstance(HDF_CONFIG_SOURCE); ... - // Extract the values based on the request and assign the values to the custom structure. + // Extract the values based on the request and assign the values to the custom structures. if (iface->GetUint32(node, "num", &udd->num, 0) != HDF_SUCCESS) { HDF_LOGE("%s: read busNum fail", __func__); return HDF_FAILURE; @@ -361,36 +394,35 @@ The following uses **uart_hi35xx.c** as an example to present the information re return 0; } ``` - - **Release** function - **Input parameter**: + - Implement the **Release** function. - **HdfDeviceObject**, an interface parameter exposed by the driver, contains the .hcs information. + Input parameter: - **Return value**: + **HdfDeviceObject**, a device object created by the HDF for each driver, holds device-related private data and service APIs. + + Return value: No value is returned. - **Function description**: + Function description: - Releases the memory and deletes the controller. This function assigns values to the **Release** API in the driver entry structure. When the HDF fails to call the **Init** function to initialize the driver, the **Release** function can be called to release driver resources. + Releases the memory and deletes the controller. This function assigns values to **Release()** in the driver entry structure. When the HDF fails to call **Init()** to initialize the driver, **Release()** is called to release driver resources. > ![icon-note.gif](public_sys-resources/icon-note.gif) **NOTE** - > > All forced conversion operations for obtaining the corresponding object can be successful only when **Init()** has the corresponding value assignment operations. - - ``` + ```c void HdfUartDeviceRelease(struct HdfDeviceObject *device) { struct UartHost *host = NULL; ... - host = UartHostFromDevice(device); // Forcibly convert HdfDeviceObject to UartHost by using service. For details about the value assignment, see the Bind function. - ... - if (host->priv != NULL) { - Hi35xxDetach(host); // Customized memory release function. - } - UartHostDestroy(host); // Call the function of the core layer to release the host. + host = UartHostFromDevice(device); // Forcible conversion from HdfDeviceObject to UartHost through the service member. For details about the value assignment, see the Bind function. + ... + if (host->priv != NULL) { + Hi35xxDetach(host); // Customized memory release function. For details, see the following. + } + UartHostDestroy(host); // Call the core layer function to release the host. } static void Hi35xxDetach(struct UartHost *host) @@ -398,13 +430,13 @@ The following uses **uart_hi35xx.c** as an example to present the information re struct UartDriverData *udd = NULL; struct UartPl011Port *port = NULL; ... - udd = host->priv; // The conversion from UartHost to UartDriverData is involved. - ... - UartRemoveDev (host); // Remove the VFS. - port = udd->private; // The conversion from UartDriverData to UartPl011Port is involved. - if (port != NULL) { - if (port->physBase != 0) { - OsalIoUnmap((void *)port->physBase);// Unmap addresses. + udd = host->priv; // The conversion from UartHost to UartDriverData is involved. + ... + UartRemoveDev (host); // Remove the VFS. + port = udd->private; // The conversion from UartDriverData to UartPl011Port is involved. + if (port != NULL) { + if (port->physBase != 0) { + OsalIoUnmap((void *)port->physBase); // Unmap addresses. } OsalMemFree(port); udd->private = NULL; @@ -413,3 +445,7 @@ The following uses **uart_hi35xx.c** as an example to present the information re host->priv = NULL; } ``` + +4. Debug the driver. + + (Optional) For new drivers, verify basic functions, for example, check the information returned after the driver is attached and whether data is successfully transmitted. diff --git a/en/device-dev/driver/driver-platform-watchdog-des.md b/en/device-dev/driver/driver-platform-watchdog-des.md index aa00d54869576d625e05eead5cbe237a81d560e8..5482f8542dbf0a40e317f77f13c7ebee913c456a 100644 --- a/en/device-dev/driver/driver-platform-watchdog-des.md +++ b/en/device-dev/driver/driver-platform-watchdog-des.md @@ -1,49 +1,83 @@ # Watchdog +## Overview -## **Overview** +### Function -A watchdog, also called a watchdog timer, is a hardware timing device used to facilitate automatic correction of temporary hardware faults or recover from system malfunctions. If an error occurs in the main program of the system and the watchdog timer is not cleared in time, the watchdog timer sends a reset signal to restore the system to the normal state. +A watchdog, also called a watchdog timer, is a hardware timing device used to facilitate automatic correction of temporary hardware faults or recover from system malfunctions. Generally, it has an input to feed the watchdog and an output to the reset pin of the system. If an error occurs in the main program of the system and the watchdog timer is not cleared in time, the watchdog timer sends a reset signal to restore the system to the normal state. +The watchdog module provides APIs for watchdog operations, including: -## Available APIs +- Opening or closing a watchdog +- Starting or stopping a watchdog +- Setting or obtaining the watchdog timeout period +- Obtaining the watchdog status +- Feeding a watchdog -**Table 1** Watchdog APIs +### Basic Concepts -| API| Description| -| -------- | -------- | -| WatchdogOpen | Opens a watchdog.| -| WatchdogClose | Closes a watchdog.| -| WatchdogStart | Starts a watchdog.| -| WatchdogStop | Stops a watchdog.| -| WatchdogSetTimeout | Sets the watchdog timeout duration.| -| WatchdogGetTimeout | Obtains the watchdog timeout duration.| -| WatchdogGetStatus | Obtains the watchdog status.| -| WatchdogFeed | Feeds a watchdog or resets a watchdog timer.| - -> ![](../public_sys-resources/icon-note.gif) **NOTE** -> -> All watchdog APIs provided in this document can be called only in kernel mode. +When the system works properly, a signal is output to the watchdog to prevent it from timing out. This operation is called watchdog feeding. If the watchdog is not fed within the specified time, the watchdog times out and a reset signal is sent to the system to reset the system. + +### Working Principles + +In the Hardware Driver Foundation (HDF), the PWM uses the independent service mode (see Figure 1) for API adaptation. In this mode, each device independently publishes a service to process external access requests. When receiving an access request, the HDF DeviceManager extracts parameters from the request to call the internal APIs of the target device. In the independent service mode, the HDF DeviceManager provides service management capabilities. However, you need to configure a node for each device, which increases memory usage. + +In the independent service mode, the core layer does not publish a service for the upper layer. Therefore, a service must be published for each controller. To achieve this purpose: + +- You need to implement the **Bind()** function in **HdfDriverEntry** to bind services. +- The **policy** field of **deviceNode** in the **device_info.hcs** file can be **1** or **2**, but not **0**. + +The watchdog module is divided into the following layers: + +- Interface layer: provides APIs for opening or closing a watchdog, starting or stopping a watchdog, setting or obtaining the watchdog timeout period, and feeding a watchdog +- Core layer: provides the capabilities of adding or removing a watchdog controller and managing watchdog devices. The core layer interacts with the adaptation layer through hook functions. +- Adaptation layer: instantiates the hook functions to implement specific features. + +**Figure 1** Independent service mode +![image1](figures/independent-service-mode.png "Watchdog independent service mode") ## Usage Guidelines +### When to Use -### How to Use +Watchdogs are used to automatically detect the software exceptions that cannot be directly observed and reset the system when an exception is detected. -The figure below shows how to use the watchdog APIs. +### Available APIs -Figure 1 Using watchdog APIs +The following table describes the APIs provided by the watchdog module. -![image](figures/using-watchdog-process.png) +**Table 1** Watchdog APIs +| API| Description| +| -------- | -------- | +| int32_t WatchdogOpen(int16_t wdtId, DevHandle *handle) | Opens a watchdog.| +| void WatchdogClose(DevHandle handle) | Closes a watchdog.| +| int32_t WatchdogStart(DevHandle handle) | Starts a watchdog.| +| int32_t WatchdogStop(DevHandle handle) | Stops a watchdog.| +| int32_t WatchdogSetTimeout(DevHandle handle, uint32_t seconds) | Sets the watchdog timeout duration.| +| int32_t WatchdogGetTimeout(DevHandle handle, uint32_t *seconds) | Obtains the watchdog timeout duration.| +| int32_t WatchdogGetStatus(DevHandle handle, int32_t *status) | Obtains the watchdog status.| +| int32_t WatchdogFeed(DevHandle handle) | Feeds a watchdog or resets a watchdog timer.| + +> ![icon-note.gif](public_sys-resources/icon-note.gif) **NOTE** +> +> All watchdog APIs described in this document can be used in kernel mode and user mode. -### Opening a Watchdog +### How to Develop -Use **WatchdogOpen()** to open a watchdog. A system may have multiple watchdogs. You need to specify the ID of the watchdog to open. +The following figure shows how to use the watchdog driver APIs. -``` -DevHandle WatchdogOpen(int16_t wdtId); +**Figure 2** Using watchdog driver APIs + +![image2](figures/using-watchdog-process.png) + +#### Opening a Watchdog + +Before operating a watchdog, you need to use **WatchdogOpen()** to open a watchdog. A system may have multiple watchdogs. You need to specify the ID of the watchdog to open. + +```c +DevHandle WatchdogOpen(int16_t wdtId, DevHandle *handle); ``` **Table 2** Description of WatchdogOpen @@ -51,24 +85,26 @@ DevHandle WatchdogOpen(int16_t wdtId); | **Parameter**| **Description**| | -------- | -------- | | wdtId | Watchdog ID.| +| handle | Pointer to the watchdog device handle obtained.| | **Return Value**| **Description**| -| NULL | The operation failed.| -| **DevHandle** pointer| The operation is successful. The pointer to the watchdog device handle is returned.| +| HDF_SUCCESS | The operation is successful.| +| Negative value| The operation fails.| +```c +int16_t wdtId = 0; +int32_t ret; +DevHandle *handle = NULL; -``` -DevHandle handle = NULL; -handle = WatchdogOpen(0); /* Open watchdog 0.*/ -if (handle == NULL) { - HDF_LOGE("WatchdogOpen: failed, ret %d\n", ret); - return; +ret = WatchdogOpen(wdtId, handle); // Open watchdog 0. +if (ret != HDF_SUCCESS) { + HDF_LOGE("WatchdogOpen: open watchdog_%hd failed, ret:%d\n", wdtId, ret); + return ret; } ``` +#### Obtaining the Watchdog Status -### Obtaining the Watchdog Status - -``` +```c int32_t WatchdogGetStatus(DevHandle handle, int32_t *status); ``` @@ -79,25 +115,24 @@ int32_t WatchdogGetStatus(DevHandle handle, int32_t *status); | handle | Watchdog device handle.| | status | Pointer to the watchdog status obtained.| | **Return Value**| **Description**| -| 0 | The operation is successful.| -| Negative value| The operation failed.| +| HDF_SUCCESS | The operation is successful.| +| Negative value| The operation fails.| - -``` +```c int32_t ret; int32_t status; -/* Obtain the watchdog status. */ -ret = WatchdogGetStatus(handle, &status); -if (ret != 0) { - HDF_LOGE("WatchdogGetStatus: failed, ret %d\n", ret); - return; + +ret = WatchdogGetStatus(handle, &status); // Obtain the watchdog status. +if (ret != HDF_SUCCESS) { + HDF_LOGE("WatchdogGetStatus: watchdog get status failed, ret:%d\n", ret); + return ret; } ``` +#### Setting the Timeout Duration -### Setting the Timeout Duration -``` +```c int32_t WatchdogSetTimeout(DevHandle *handle, uint32_t seconds); ``` @@ -108,25 +143,22 @@ int32_t WatchdogSetTimeout(DevHandle *handle, uint32_t seconds); | handle | Pointer to the watchdog device handle.| | seconds | Timeout duration to set, in seconds.| | **Return Value**| **Description**| -| 0 | The operation is successful.| -| Negative value| The operation failed.| - +| HDF_SUCCESS | The operation is successful.| +| Negative value| The operation fails.| -``` +```c int32_t ret; -uint32_t timeOut = 60; -/* Set the timeout duration to 60 seconds. */ -ret = WatchdogSetTimeout(handle, timeOut); -if (ret != 0) { - HDF_LOGE("WatchdogSetTimeout: failed, ret %d\n", ret); - return; + +ret = WatchdogSetTimeout(handle, 2); // Set the timeout duration to 2 seconds. +if (ret != HDF_SUCCESS) { + HDF_LOGE("WatchdogSetTimeout: watchdog set timeOut failed, ret:%d\n", ret); + return ret; } ``` +#### Obtaining the Timeout Duration -### Obtaining the Timeout Duration - -``` +```c int32_t WatchdogGetTimeout(DevHandle *handle, uint32_t *seconds); ``` @@ -135,27 +167,25 @@ int32_t WatchdogGetTimeout(DevHandle *handle, uint32_t *seconds); | **Parameter**| **Description**| | -------- | -------- | | handle | Pointer to the watchdog device handle.| -| seconds | Pointer to the timeout duration, in seconds.| +| seconds | Pointer to the watchdog timeout duration obtained.| | **Return Value**| **Description**| -| 0 | The operation is successful.| -| Negative value| The operation failed.| +| HDF_SUCCESS | The operation is successful.| +| Negative value| The operation fails.| +```c + int32_t ret; + uint32_t timeOut; -``` -int32_t ret; -uint32_t timeOut; -/* Obtain the timeout duration, in seconds. */ -ret = WatchdogGetTimeout(handle, &timeOut); -if (ret != 0) { - HDF_LOGE("WatchdogGetTimeout: failed, ret %d\n", ret); - return; -} + ret = WatchdogGetTimeout(handle, &timeOut); // Obtain the watchdog timeout duration. + if (ret != HDF_SUCCESS) { + HDF_LOGE("WatchdogGetTimeout: watchdog get timeOut failed, ret:%d\n", ret); + return ret; + } ``` +#### Starting a Watchdog -### Starting a Watchdog - -``` +```c int32_t WatchdogStart(DevHandle handle); ``` @@ -165,24 +195,22 @@ int32_t WatchdogStart(DevHandle handle); | -------- | -------- | | handle | Watchdog device handle.| | **Return Value**| **Description**| -| 0 | The operation is successful.| -| Negative value| The operation failed.| - +| HDF_SUCCESS | The operation is successful.| +| Negative value| The operation fails.| -``` +```c int32_t ret; -/* Start the watchdog. */ -ret = WatchdogStart(handle); -if (ret != 0) { - HDF_LOGE("WatchdogStart: failed, ret %d\n", ret); - return; + +ret = WatchdogStart(handle); // Start a watchdog. +if (ret != HDF_SUCCESS) { + HDF_LOGE("WatchdogStart: start watchdog failed, ret:%d\n", ret); + return ret; } ``` +#### Feeding a Watchdog -### Feeding a Watchdog - -``` +```c int32_t WatchdogFeed(DevHandle handle); ``` @@ -192,24 +220,22 @@ int32_t WatchdogFeed(DevHandle handle); | -------- | -------- | | handle | Watchdog device handle.| | **Return Value**| **Description**| -| 0 | The operation is successful.| -| Negative value| The operation failed.| - +| HDF_SUCCESS | The operation is successful.| +| Negative value| The operation fails.| -``` +```c int32_t ret; -/* Feed the watchdog. */ -ret = WatchdogFeed(handle); -if (ret != 0) { - HDF_LOGE("WatchdogFeed: failed, ret %d\n", ret); - return; + +ret = WatchdogFeed (handle); // Feed a watchdog. +if (ret != HDF_SUCCESS) { + HDF_LOGE("WatchdogFeed: feed watchdog failed, ret:%d\n", ret); + return ret; } ``` +#### Stopping a Watchdog -### Stopping a Watchdog - -``` +```c int32_t WatchdogStop(DevHandle handle); ``` @@ -219,26 +245,24 @@ int32_t WatchdogStop(DevHandle handle); | -------- | -------- | | handle | Watchdog device handle.| | **Return Value**| **Description**| -| 0 | The operation is successful.| -| Negative value| The operation failed.| - +| HDF_SUCCESS | The operation is successful.| +| Negative value| The operation fails.| -``` +```c int32_t ret; -/* Stop the watchdog. */ -ret = WatchdogStop(handle); -if (ret != 0) { - HDF_LOGE("WatchdogStop: failed, ret %d\n", ret); - return; + +ret = WatchdogStop(handle); // Stop a watchdog. +if (ret != HDF_SUCCESS) { + HDF_LOGE("WatchdogStop: stop watchdog failed, ret:%d\n", ret); + return ret; } ``` +#### Closing a Watchdog -### Closing a Watchdog +After all operations are complete, use **WatchdogClose()** to close the watchdog. -If a watchdog is no longer required, call **WatchdogClose()** to close it. - -``` +```c void WatchdogClose(DevHandle handle); ``` @@ -248,29 +272,26 @@ void WatchdogClose(DevHandle handle); | -------- | -------- | | handle | Watchdog device handle.| - +```c +WatchdogClose(handle); // Close a watchdog. ``` -/* Close the watchdog. */ -ret = WatchdogClose(handle); -``` - ## Example -The following example provides the complete development process. +The following uses the Hi3516D V300 development board as an example to describe how to operate the watchdog. The procedure is as follows: -1. Open a watchdog, set the timeout duration, and start the watchdog. +1. Open a watchdog. You need to pass in the watchdog ID. The device handle of the watchdog opened is returned. +2. Set the timeout duration for the watchdog. +3. Obtain the timeout duration of the watchdog. +4. Start the watchdog. +5. Feed the watchdog. +6. Stop the watchdog. +7. Close the watchdog. -2. Feed the watchdog periodically to ensure that the system is not reset due to timer expiry. -3. Stop feeding the watchdog and check whether the system is reset after the timer expires. - -Sample code: - -``` -#include "watchdog_if.h" -#include "hdf_log.h" -#include "osal_irq.h" -#include "osal_time.h" +```c +#include "watchdog_if.h" /* Header file of the standard watchdog APIs. */ +#include "hdf_log.h" /* Header file of the HDF log APIs. */ +#include "osal_time.h" /* Header file of the delay and sleep APIs. */ #define WATCHDOG_TEST_TIMEOUT 2 #define WATCHDOG_TEST_FEED_TIME 6 @@ -279,14 +300,16 @@ static int32_t TestCaseWatchdog(void) { int32_t i; int32_t ret; + int16_t wdtId = 0; + int32_t status; uint32_t timeout; - DevHandle handle = NULL; + DevHandle *handle = NULL; /* Open watchdog 0. */ - handle = WatchdogOpen(0); - if (handle == NULL) { - HDF_LOGE("Open watchdog failed!"); - return -1; + ret = WatchdogOpen(wdtId, handle); + if (ret != HDF_SUCCESS) { + HDF_LOGE("WatchdogOpen: open watchdog_%hd failed, ret:%d\n", wdtId, ret); + return ret; } /* Set the timeout duration. */ @@ -297,13 +320,19 @@ static int32_t TestCaseWatchdog(void) return ret; } - /* Obtain the timeout duration. */ + /* Obtain the timeout duration. */ ret = WatchdogGetTimeout(handle, &timeout); if (ret != HDF_SUCCESS) { HDF_LOGE("%s: get timeout fail! ret:%d\n", __func__, ret); WatchdogClose(handle); return ret; } + /* Check whether the timeout duration obtained is the same as the timeout duration set. */ + if (timeout != WATCHDOG_TEST_TIMEOUT) { + HDF_LOGE("%s: set:%u, but get:%u", __func__, WATCHDOG_TEST_TIMEOUT, timeout); + WatchdogClose(handle); + return HDF_FAILURE; + } HDF_LOGI("%s: read timeout back:%u\n", __func__, timeout); /* Start the watchdog. The timer starts. */ @@ -313,6 +342,19 @@ static int32_t TestCaseWatchdog(void) WatchdogClose(handle); return ret; } + /* Obtain the watchdog status and determine whether to start the watchdog. */ + status = WATCHDOG_STOP; + ret = WatchdogGetStatus(handle, &status); + if (ret != HDF_SUCCESS) { + HDF_LOGE("%s: get status fail! ret:%d", __func__, ret); + WatchdogClose(handle); + return ret; + } + if (status != WATCHDOG_START) { + HDF_LOGE("%s: status is:%d after start", __func__, status); + WatchdogClose(handle); + return HDF_FAILURE; + } /* Feed the watchdog every other second. */ for (i = 0; i < WATCHDOG_TEST_FEED_TIME; i++) { @@ -328,15 +370,26 @@ static int32_t TestCaseWatchdog(void) /* Because the interval for feeding the watchdog is shorter than the timeout duration, the system does not reset, and logs can be printed normally. */ HDF_LOGI("%s: no reset ... feeding test OK!!!\n", __func__); - /* Stop feeding the watchdog to make the timer expire. */ - for (i = 0; i < WATCHDOG_TEST_FEED_TIME; i++) { - HDF_LOGI("%s: waiting dog buck %d times... \n", __func__, i); - OsalSleep(1); + ret = WatchdogStop(handle); + if (ret != HDF_SUCCESS) { + HDF_LOGE("%s: stop fail! ret:%d", __func__, ret); + WatchdogClose(handle); + return ret; + } + /* Obtain the watchdog status and determine whether to close the watchdog. */ + status = WATCHDOG_START; + ret = WatchdogGetStatus(handle, &status); + if (ret != HDF_SUCCESS) { + HDF_LOGE("%s: get status fail! ret:%d", __func__, ret); + WatchdogClose(handle); + return ret; + } + if (status != WATCHDOG_STOP) { + HDF_LOGE("%s: status is:%d after stop", __func__, status); + WatchdogClose(handle); + return HDF_FAILURE; } - - /* The system resets when the timer expires. Theoretically, this log is not displayed. */ - HDF_LOGI("%s: dog hasn't back!!! \n", __func__, i); WatchdogClose(handle); - return -1; + return HDF_SUCCESS; } ``` diff --git a/en/device-dev/driver/driver-platform-watchdog-develop.md b/en/device-dev/driver/driver-platform-watchdog-develop.md index 67095216d8d78c6743e199b540fe38c9b5b18957..72691eb5d0cc0f728c60d9040dd4c212466a77a7 100644 --- a/en/device-dev/driver/driver-platform-watchdog-develop.md +++ b/en/device-dev/driver/driver-platform-watchdog-develop.md @@ -1,187 +1,198 @@ # Watchdog - ## Overview -A watchdog, also called a watchdog timer, is a hardware timing device used to facilitate automatic correction of temporary hardware faults or recover from system malfunctions. +### Function -In the Hardware Driver Foundation (HDF), the watchdog uses the independent service mode for API adaptation. In this mode, each device independently publishes a service to process external access requests. When receiving an access request, the HDF DeviceManager extracts parameters from the request to call the internal APIs of the target device. In the independent service mode, the HDF DeviceManager provides service management capabilities. However, you need to configure a node for each device, which increases memory usage. +A watchdog, also called a watchdog timer, is a hardware timing device used to facilitate automatic correction of temporary hardware faults or recover from system malfunctions. Generally, it has an input to feed the watchdog and an output to the reset pin of the system. If an error occurs in the main program of the system and the watchdog timer is not cleared in time, the watchdog timer sends a reset signal to restore the system to the normal state. - **Figure 1** Independent service mode +### Basic Concepts - ![image](figures/independent-service-mode.png "Watchdog independent service mode") +When the system works properly, a signal is output to the watchdog to prevent it from timing out. This operation is called watchdog feeding. If the watchdog is not fed within the specified time, the watchdog times out and a reset signal is sent to the system to reset the system. +### Working Principles -## Available APIs +In the Hardware Driver Foundation (HDF), the watchdog module uses the independent service mode (see Figure 1) for API adaptation. In this mode, each device independently publishes a service to process external access requests. When receiving an access request, the HDF DeviceManager extracts parameters from the request to call the internal APIs of the target device. In the independent service mode, the HDF DeviceManager provides service management capabilities. However, you need to configure a node for each device, which increases memory usage. -**WatchdogMethod**: +In the independent service mode, the core layer does not publish a service for the upper layer. Therefore, a service must be published for each controller. To achieve this purpose: +- You need to implement the **Bind()** function in **HdfDriverEntry** to bind services. +- The **policy** field of **deviceNode** in the **device_info.hcs** file can be **1** or **2**, but not **0**. -``` +The watchdog module is divided into the following layers: + +- Interface layer: provides APIs for opening or closing a watchdog, starting or stopping a watchdog, setting or obtaining the watchdog timeout period, and feeding a watchdog +- Core layer: provides the capabilities of adding or removing a watchdog controller and managing watchdog devices. The core layer interacts with the adaptation layer through hook functions. +- Adaptation layer: instantiates the hook functions to implement specific features. + +**Figure 1** Independent service mode + +![image](figures/independent-service-mode.png "Watchdog independent service mode") + +## Development Guidelines + +### When to Use + +Watchdogs are used to automatically detect the software exceptions that cannot be directly observed and reset the system when an exception is detected. Before using your watchdogs with OpenHarmony, you need to perform watchdog driver adaptation. The following describes how to do it. + +### **Available APIs** + +To enable the upper layer to successfully operate the watchdog controller by calling the watchdog APIs, hook functions are defined in **//drivers/hdf_core/framework/support/platform/include/watchdog/watchdog_core.h** for the core layer. You need to implement these hook functions at the adaptation layer and hook them to implement the interaction between the interface layer and the core layer. + +**WatchdogMethod**: + +```c struct WatchdogMethod { - int32_t (*getStatus)(struct WatchdogCntlr *wdt, int32_t *status); - int32_t (*setTimeout)(struct WatchdogCntlr *wdt, uint32_t seconds); - int32_t (*getTimeout)(struct WatchdogCntlr *wdt, uint32_t *seconds); - int32_t (*start)(struct WatchdogCntlr *wdt); - int32_t (*stop)(struct WatchdogCntlr *wdt); - int32_t (*feed)(struct WatchdogCntlr *wdt); - int32_t (*getPriv)(struct WatchdogCntlr *wdt); // (Optional) If WatchdogCntlr has the priv member, instantiate priv. - void (*releasePriv)(struct WatchdogCntlr *wdt);// (Optional) + int32_t (*getStatus)(struct WatchdogCntlr *wdt, int32_t *status); + int32_t (*setTimeout)(struct WatchdogCntlr *wdt, uint32_t seconds); + int32_t (*getTimeout)(struct WatchdogCntlr *wdt, uint32_t *seconds); + int32_t (*start)(struct WatchdogCntlr *wdt); + int32_t (*stop)(struct WatchdogCntlr *wdt); + int32_t (*feed)(struct WatchdogCntlr *wdt); + int32_t (*getPriv)(struct WatchdogCntlr *wdt); // (Optional) If WatchdogCntlr has the priv member, instantiate priv. + void (*releasePriv)(struct WatchdogCntlr *wdt); // (Optional) }; ``` - **Table 1** Description of the callback functions in WatchdogMethod +**Table 1** Hook functions in WatchdogMethod | Function| Input Parameter| Output Parameter| Return Value| Description| | -------- | -------- | -------- | -------- | -------- | -| getStatus | **wdt**: structure pointer to the watchdog controller at the core layer.| **status**: int32_t pointer to the watchdog status (started or stopped).| HDF_STATUS| Obtains the watchdog status.| -| start | **wdt**: structure pointer to the watchdog controller at the core layer.| –| HDF_STATUS| Starts a watchdog.| -| stop | **wdt**: structure pointer to the watchdog controller at the core layer.| –| HDF_STATUS | Stops a watchdog.| -| setTimeout | **wdt**: structure pointer to the watchdog controller at the core layer.
**seconds**: Timeout duration to set, in seconds. The value is of the uint32_t type. | – | HDF_STATUS | Sets the timeout duration for a watchdog. | -| getTimeout | **wdt**: structure pointer to the watchdog controller at the core layer.| **seconds**: Pointer to the watchdog timeout duration obtained. The value is of the uint32_t type. | HDF_STATUS| Obtains the timeout duration of a watchdog.| +| getStatus | **wdt**: structure pointer to the watchdog controller at the core layer.| status: pointer to the watchdog status (opened or closed) obtained. The value is of the int32_t type.| HDF_STATUS| Obtains the watchdog status.| +| setTimeout | **wdt**: structure pointer to the watchdog controller at the core layer.
**seconds**: watchdog timeout duration to set.| –| HDF_STATUS| Sets the watchdog timeout duration, in seconds. Ensure that the actual running time of the watchdog complies with the value set.| +| getTimeout | **wdt**: structure pointer to the watchdog controller at the core layer. | **seconds**: pointer to the timeout duration obtained. The value is of the uint32_t type. | HDF_STATUS| Obtains the watchdog timeout duration. | +| start | **wdt**: structure pointer to the watchdog controller at the core layer. | – | HDF_STATUS| Starts a watchdog. | +| stop | **wdt**: structure pointer to the watchdog controller at the core layer. | – | HDF_STATUS| Stops a watchdog. | | feed | **wdt**: structure pointer to the watchdog controller at the core layer.| –| HDF_STATUS| Feeds a watchdog. | +| getPriv | **wdt**: structure pointer to the watchdog controller at the core layer.| –| HDF_STATUS| Obtains the private data of the watchdog driver.| +| releasePriv | **wdt**: structure pointer to the watchdog controller at the core layer.| –| HDF_STATUS| Releases the private data of the watchdog driver.| + +### How to Develop +The watchdog module adaptation procedure is as follows: + +1. Instantiate the driver entry. +2. Configure attribute files. +3. Instantiate the watchdog controller object. +4. Debug the driver. -## How to Develop +### Example -The watchdog module adaptation involves the following steps: +The following uses the **//device_soc_hisilicon/common/platform/watchdog/watchdog_hi35xx.c** driver of the Hi3516D V300 development board as an example to describe the watchdog driver adaptation. 1. Instantiate the driver entry. - - Instantiate the **HdfDriverEntry** structure. - - Call **HDF_INIT** to register the **HdfDriverEntry** instance with the HDF. -2. Configure attribute files. - - Add the **deviceNode** information to the **device_info.hcs** file. - - (Optional) Add the **watchdog_config.hcs** file. + The driver entry must be a global variable of the **HdfDriverEntry** type (defined in **hdf_device_desc.h**), and the value of **moduleName** must be the same as that in **device_info.hcs**. In the HDF framework, the start address of each **HdfDriverEntry** object of all loaded drivers is collected to form a segment address space similar to an array for the upper layer to invoke. + Generally, the HDF calls the **Bind** function and then the **Init** function to load a driver. If **Init** fails to be called, the HDF calls **Release** to release driver resources and exit. -3. Instantiate the watchdog controller object. - - Initialize **WatchdogCntlr**. - - Instantiate **WatchdogMethod** in the **WatchdogCntlr** object. - + Watchdog driver entry example: - > ![](../public_sys-resources/icon-note.gif) **NOTE** - > - > For details about the functions in **WatchdogMethod**, see [Available APIs](#available-apis). + ```c + struct HdfDriverEntry g_watchdogDriverEntry = { + .moduleVersion = 1, + .Bind = Hi35xxWatchdogBind, // See the Bind function. + .Init = Hi35xxWatchdogInit, // See the Init function. + .Release = Hi35xxWatchdogRelease, // See the Release function. + .moduleName = "HDF_PLATFORM_WATCHDOG", // (Mandatory) The value must be the same as that of moduleName in the .hcs file. + }; + HDF_INIT(g_watchdogDriverEntry); // Call HDF_INIT to register the driver entry with the HDF. + ``` -4. Debug the driver. - - (Optional) For new drivers, verify basic functions, for example, check the information returned after the driver is attached and whether the watchdog timer is successfully set. +2. Configure attribute files. + Add the deviceNode information to the **device_info.hcs** file. The deviceNode information is related to the driver entry registration. The following example uses one watchdog controller as an example. If there are more watchdog controllers, add the deviceNode information to the **device_info.hcs** file for each controller. The device attribute values configured in **watchdog_config.hcs** are closely related to default values or value ranges of the **WatchdogCntlr** members at the core layer. + + - **device_info.hcs** example: + + Add the deviceNode information to the **//vendor/hisilicon/hispark_taurus/hdf_config/device_info/device_info.hcs** file. + + ```c + root { + device_info { + match_attr = "hdf_manager"; + device_watchdog :: device { // Device node. + device0 :: deviceNode { // DeviceNode of the driver. + policy = 2; // The value 2 means to publish services for both kernel- and user-mode processes. + priority = 20; // Driver startup priority. + permission = 0644; // Permission for the device node created. + moduleName = "HDF_PLATFORM_WATCHDOG"; // (Mandatory) Driver name, which must be the same as that of moduleName in the driver entry structure. + serviceName = "HDF_PLATFORM_WATCHDOG_0"; // (Mandatory) Unique name of the service released by the driver. + deviceMatchAttr = "hisilicon_hi35xx_watchdog_0"; // (Mandatory) Controller private data, which must be the same as the value of match_attr in watchdog_config.hcs. + } + } + } + } + ``` -## Development Example + - **watchdog_config.hcs** example: + + Configure the device attributes in the **//device/soc/hisilicon/hi3516dv300/sdk_liteos/hdf_config/watchdog/watchdog_config.hcs** file. The parameters are as follows: + + ```c + root { + platform { + template watchdog_controller { // (Mandatory) Template configuration. If the template is used to configure device node information, the default values in the template will be used for the fields that are not declared for the node. + id = 0; // Watchdog ID. + match_attr = ""; + regBase = 0x12050000; // (Mandatory) Physical base address used for address mapping. + regStep = 0x1000; // (Mandatory) Register offset step used for address mapping. + } + controller_0x12050000 :: watchdog_controller { // (Mandatory) Keyword for matching the private data of the device driver. + match_attr = "hisilicon_hi35xx_watchdog_0"; // (Mandatory) The value must be the same as that of deviceMatchAttr in device_info.hcs. + } + // Add node information for each watchdog device. + ... + } + } + ``` -The following uses **watchdog_hi35xx.c** as an example to present the information required for implementing device functions. + After the **watchdog_config.hcs** file is configured, include the file in the **hdf.hcs** file. Otherwise, the configuration file cannot take effect. -1. Instantiate the driver entry. - - The driver entry must be a global variable of the **HdfDriverEntry** type (defined in **hdf_device_desc.h**), and the value of **moduleName** must be the same as that in **device_info.hcs**. In the HDF, the start address of each **HdfDriverEntry** object of all loaded drivers is collected to form a segment address space similar to an array for the upper layer to invoke. - - Generally, the HDF calls the **Bind** function and then the **Init** function to load a driver. If **Init** fails to be called, the HDF calls **Release** to release driver resources and exit. - - Watchdog driver entry example: - - ``` - struct HdfDriverEntry g_watchdogDriverEntry = { - .moduleVersion = 1, - .Bind = Hi35xxWatchdogBind, // See the Bind function. - .Init = Hi35xxWatchdogInit, // See the Init function. - .Release = Hi35xxWatchdogRelease, // See the Release function. - .moduleName = "HDF_PLATFORM_WATCHDOG",// (Mandatory) The value must be the same as that of moduleName in the .hcs file. - }; - HDF_INIT(g_watchdogDriverEntry);// Call HDF_INIT to register the driver entry with the HDF. - ``` - -2. Add the **deviceNode** information to the **device_info.hcs** file and configure the component attributes in the **watchdog_config.hcs** file. - - The **deviceNode** information is related to registration of the driver entry. The device attribute values are closely related to the default values or value ranges of the **WatchdogCntlr** members at the core layer. - - In this example, there is only one watchdog controller. If there are multiple watchdog controllers, you need to add the **deviceNode** information to the **device_info** file and add the corresponding device attributes to the **watchdog_config** file for each controller. - - - **device_info.hcs** configuration example: - - - ``` - root { - device_info { - match_attr = "hdf_manager"; - device_watchdog :: device {// Device node. - device0:: deviceNode { // Device node of the driver. - policy = 1; // Policy for the driver to provide services. - priority = 20; // Driver startup priority. - permission = 0644; // Permission to create device nodes for the driver. - moduleName = "HDF_PLATFORM_WATCHDOG"; - // (Mandatory) Driver name. The value must be the same as that of moduleName in the driver entry structure. - serviceName = "HDF_PLATFORM_WATCHDOG_0"; - // (Mandatory) Unique name of the service published by the driver. - deviceMatchAttr = "hisilicon_hi35xx_watchdog_0"; - // (Mandatory) Keyword matching the private data of the driver. The value must be the same as that of match_attr in the private data configuration table of the driver. - } - } - } - } - ``` - - - **watchdog_config.hcs** configuration example: - - - ``` - root { - platform { - template watchdog_controller {// (Mandatory) Template configuration. In the template, you can configure the common parameters shared by device nodes. - id = 0; - match_attr = ""; - regBase = 0x12050000; // (Mandatory) Used for address mapping. - regStep = 0x1000; // (Mandatory) Used for address mapping. - } - controller_0x12050000 :: watchdog_controller {// (Mandatory) Keyword for matching the private data of the device driver. - match_attr = "hisilicon_hi35xx_watchdog_0"; // (Mandatory) The value must be the same as that of deviceMatchAttr in device_info.hcs. - } - // Configure this parameter when there are multiple watchdogs. - ... - } - } - ``` - -3. Initialize the **WatchdogCntlr** object at the core layer, including defining a custom structure (to pass parameters and data) and implementing the **HdfDriverEntry** member functions (**Bind**, **Init**, and **Release**) to instantiate **WatchdogMethod** in **WatchdogCntlr** (so that the underlying driver functions can be called). - - Defining a custom structure - - To the driver, the custom structure holds parameters and data. The **DeviceResourceIface** method provided by the HDF reads the values in the **watchdog_config.hcs** file to initialize the members in the custom structure and passes important parameters, such as the index and the number of pins, to the **WatchdogCntlr** object at the core layer. - - + ```c + #include "../../../../device/soc/hisilicon/hi3516dv300/sdk_liteos/hdf_config/watchdog/watchdog_config.hcs" // Relative path of the file. ``` + +3. Instantiate the watchdog controller object. + + Initialize the **WatchdogCntlr** object at the core layer, including defining a custom structure (to pass parameters and data) and implementing the **HdfDriverEntry** member functions (**Bind**, **Init** and **Release**) to instantiate **WatchdogMethod** in **WatchdogCntlr** (so that the underlying driver functions can be called). + + - Define a custom structure. + + To the driver, the custom structure holds parameters and data. The **DeviceResourceIface** method provided by the HDF reads the values in the **watchdog_config.hcs** file to initialize the members in the custom structure and passes important parameters, such as the watchdog ID, to the object at the core layer. + + ```c struct Hi35xxWatchdog { - struct WatchdogCntlr wdt; // (Mandatory) Carrier that connects the upper and underlying layers. For details, see the following description. - OsalSpinlock lock; - volatile unsigned char *regBase;// [Mandatory] Used for address mapping. - uint32_t phyBase; // (Mandatory) Used for address mapping. - uint32_t regStep; // (Mandatory) Used for address mapping. + struct WatchdogCntlr wdt; // (Mandatory) Control object of the core layer. For details, see the following description. + OsalSpinlock lock; // (Mandatory) You need to implement the spinlock for your watchdog. + volatile unsigned char *regBase; // (Mandatory) Register base address used for address mapping. + uint32_t phyBase; // (Mandatory) Physical base address used for address mapping. + uint32_t regStep; // (Mandatory) Register offset step used for address mapping. }; - // WatchdogCntlr is the core layer controller structure. The Init function assigns values to the members of WatchdogCntlr. - struct WatchdogCntlr { - struct IDeviceIoService service;// Driver service. - struct HdfDeviceObject *device; // Driver device. - OsalSpinlock lock; // This variable is called by the HDF core layer to implement the spinlock function. - struct WatchdogMethod *ops; // Callbacks. - int16_t wdtId // ID of the watchdog device. - void *priv; // Pointer to the driver's private data. + + struct WatchdogCntlr { // WatchdogCntlr is the controller structure at the core layer. The Init function assigns values to WatchdogCntlr. + struct IDeviceIoService service; // Driver service. + struct HdfDeviceObject *device; // Driver device object. + OsalSpinlock lock; // Spinlock. + struct WatchdogMethod *ops; // Hook functions. + int16_t wdtId; // Watchdog ID. + void *priv; // Private data. }; ``` - - - Instantiating **WatchdogMethod** in **WatchdogCntlr** (other members are initialized by **Init** and **Bind**) - - ``` - static struct WatchdogMethod g_method = { - .getStatus = Hi35xxWatchdogGetStatus, - .start = Hi35xxWatchdogStart, - .stop = Hi35xxWatchdogStop, - .setTimeout = Hi35xxWatchdogSetTimeout, - .getTimeout = Hi35xxWatchdogGetTimeout, - .feed = Hi35xxWatchdogFeed, + - Instantiate **WatchdogMethod** in **WatchdogCntlr**. + + ```c + static struct WatchdogMethod g_method = { // Instantiate the hook functions. + .getStatus = Hi35xxWatchdogGetStatus, // Obtain the watchdog status. + .start = Hi35xxWatchdogStart, // Start the watchdog. + .stop = Hi35xxWatchdogStop, // Stop the watchdog. + .setTimeout = Hi35xxWatchdogSetTimeout, // Set the watchdog timeout duration. + .getTimeout = Hi35xxWatchdogGetTimeout, //Obtain the watchdog timeout duration. + .feed = Hi35xxWatchdogFeed, // Feed the watchdog. }; ``` - - **Init** and **Bind** functions + - Implement the **Init** and **Bind** functions. Input parameter: @@ -189,15 +200,13 @@ The following uses **watchdog_hi35xx.c** as an example to present the informatio Return value: - HDF_STATUS + **HDF_STATUS**
The table below describes some status. For more information, see **HDF_STATUS** in the **//drivers/hdf_core/framework/include/utils/hdf_base.h** file. + + **Table 2** Description of HDF_STATUS - The table below lists some status. For more information, see **HDF_STATUS** in the /drivers/framework/include/utils/hdf_base.h file. - - **Table 2** HDF_STATUS - | Status| Description| | -------- | -------- | - | HDF_ERR_INVALID_OBJECT | Failed to locate the watchdog device.| + | HDF_ERR_INVALID_OBJECT | Invalid controller object.| | HDF_ERR_MALLOC_FAIL | Failed to allocate memory.| | HDF_ERR_IO | I/O error.| | HDF_SUCCESS | Initialization successful.| @@ -205,42 +214,41 @@ The following uses **watchdog_hi35xx.c** as an example to present the informatio Function description: - Initializes the custom structure object and **WatchdogCntlr**, and calls the **WatchdogCntlrAdd** function at the core layer. - - - ``` + Initializes the custom structure object and **WatchdogCntlr**, and calls **WatchdogCntlrAdd()** at the core layer to add the watchdog controller. + + ```c // Generally, the Init function initializes the members of the Hi35xxWatchdog structure based on the attribute values in **HdfDeviceObject**. - // In this example, the Bind function initializes the Hi35xxWatchdog structure. + // In watchdog_hi35xx.c, it is implemented by the Bind function. static int32_t Hi35xxWatchdogInit(struct HdfDeviceObject *device) { - (void)device; - return HDF_SUCCESS; + (void)device; + return HDF_SUCCESS; } - + static int32_t Hi35xxWatchdogBind(struct HdfDeviceObject *device) { - int32_t ret; - struct Hi35xxWatchdog *hwdt = NULL; - ... - hwdt = (struct Hi35xxWatchdog *)OsalMemCalloc(sizeof(*hwdt));// Apply for memory for the Hi35xxWatchdog structure. - ... - hwdt->regBase = OsalIoRemap(hwdt->phyBase, hwdt->regStep); // Address mapping - ... - hwdt->wdt.priv = (void *)device->property;// (Optional) Assign the device attribute values to priv. However, priv is not called subsequently. - //If the priv member is required, instantiate getPriv() and releasePriv() of WatchdogMethod. - hwdt->wdt.ops = &g_method; // (Mandatory) Assign the instantiated objects to the ops members so that the top layer can invoke the WatchdogMethod functions. - hwdt->wdt.device = device; // (Mandatory) Enable conversion between HdfDeviceObject and WatchdogcCntlr. - ret = WatchdogCntlrAdd(&hwdt->wdt); // (Mandatory) Call this function to initialize the structure of the core layer. The driver accesses the platform core layer only after a success signal is returned. - if (ret != HDF_SUCCESS) { // If the operation fails, release the resources used by Init(). - OsalIoUnmap((void *)hwdt->regBase); - OsalMemFree(hwdt); - return ret; - } - return HDF_SUCCESS; + int32_t ret; + struct Hi35xxWatchdog *hwdt = NULL; + ... + hwdt = (struct Hi35xxWatchdog *)OsalMemCalloc(sizeof(*hwdt)); // Allocate memory for the Hi35xxWatchdog structure pointer. + ... + hwdt->regBase = OsalIoRemap(hwdt->phyBase, hwdt->regStep); // Address mapping. + ... + hwdt->wdt.priv = (void *)device->property; // (Mandatory) Use the device attributes to assign values to privr, but priv is not called later. + //If the priv member is required, instantiate getPriv() and releasePriv() of WatchdogMethod. + hwdt->wdt.ops = &g_method; // (Mandatory) Hook the WatchdogMethod instance. + hwdt->wdt.device = device; // (Mandatory) Enable conversion between HdfDeviceObject and WatchdogcCntlr. + ret = WatchdogCntlrAdd(&hwdt->wdt); // (Mandatory) Call this function to initialize the core layer structure. The driver can access the platform core layer only after a success signal is returned. + if (ret != HDF_SUCCESS) { // If the operation fails, remove the mapping and release the resources requested by the Init function. + OsalIoUnmap((void *)hwdt->regBase); + OsalMemFree(hwdt); + return ret; + } + return HDF_SUCCESS; } ``` - - - **Release** function + + - Implement the **Release** function. Input parameter: @@ -252,28 +260,29 @@ The following uses **watchdog_hi35xx.c** as an example to present the informatio Function description: - Releases driver resources. This function assigns values to **Release()** in the driver entry structure. When the HDF fails to call the **Init** function to initialize the driver, **Release()** can be called to release driver resources. The **Release()** function must contain the operations for releasing the memory and deleting the controller. + Releases driver resources. This function assigns values to **Release()** in the driver entry structure. When the HDF fails to call **Init()** to initialize the driver, **Release()** is called to release driver resources. The **Release()** function must contain the operations for releasing the memory and deleting the controller. - All forced conversion operations for obtaining the corresponding object can be successful only when **Init()** has the corresponding value assignment operations. - - - ``` + ```c static void Hi35xxWatchdogRelease(struct HdfDeviceObject *device) { - struct WatchdogCntlr *wdt = NULL; - struct Hi35xxWatchdog *hwdt = NULL; - ... - wdt = WatchdogCntlrFromDevice(device);// Use service to convert HdfDeviceObject to WatchdogCntlr. - // return (device == NULL) ? NULL : (struct WatchdogCntlr *)device->service; - if (wdt == NULL) { - return; - } - WatchdogCntlrRemove(wdt); // Core layer function used to execute wdt->device->service = NULL and release cntlr->lock. - hwdt = (struct Hi35xxWatchdog *)wdt; // Convert WatchdogCntlr to HimciHost. - if (hwdt->regBase != NULL) { // Unmap addresses. - OsalIoUnmap((void *)hwdt->regBase); - hwdt->regBase = NULL; - } - OsalMemFree(hwdt); // Release the memory occupied by the vendor-defined objects. + struct WatchdogCntlr *wdt = NULL; + struct Hi35xxWatchdog *hwdt = NULL; + ... + wdt = WatchdogCntlrFromDevice(device); // (Mandatory) Obtain WatchdogCntlr through device. + ... + if (wdt == NULL) { + return; + } + WatchdogCntlrRemove(wdt); // (Mandatory) Call WatchdogCntlrRemove to release the WatchdogCntlr object. + hwdt = (struct Hi35xxWatchdog *)wdt; // Convert WatchdogCntlr to Hi35xxWatchdog. + if (hwdt->regBase != NULL) { // (Mandatory) Remove the address mapping. + OsalIoUnmap((void *)hwdt->regBase); + hwdt->regBase = NULL; + } + OsalMemFree(hwdt); // (Mandatory) Release the memory occupied by the custom object. } ``` + +4. Debug the driver. + + (Optional) For new drivers, verify basic functions, for example, check the information returned after the driver is attached and whether data is successfully transmitted. diff --git a/en/device-dev/driver/figures/4-wire-uart-communication.png b/en/device-dev/driver/figures/4-wire-uart-communication.png index 6ac63e41108abd4776621356c3034fc52b6f436f..b5e82f09cd764b0cd9dc835e55f8f878b77eb91e 100644 Binary files a/en/device-dev/driver/figures/4-wire-uart-communication.png and b/en/device-dev/driver/figures/4-wire-uart-communication.png differ diff --git a/en/device-dev/driver/figures/using-PWM-process.png b/en/device-dev/driver/figures/using-PWM-process.png index 5ec65550b9996307c0432a7651a27d034628717e..b9be0064076e4eb4b981f5a9bf5068ddf6d1f19d 100644 Binary files a/en/device-dev/driver/figures/using-PWM-process.png and b/en/device-dev/driver/figures/using-PWM-process.png differ diff --git a/en/device-dev/driver/figures/using-UART-process.png b/en/device-dev/driver/figures/using-UART-process.png index 3259fd96752d1244da00eb9be85651997943d316..d4acf5276d878a09438eaada4e4c7441f227fae2 100644 Binary files a/en/device-dev/driver/figures/using-UART-process.png and b/en/device-dev/driver/figures/using-UART-process.png differ diff --git a/en/device-dev/driver/figures/using-watchdog-process.png b/en/device-dev/driver/figures/using-watchdog-process.png index 7a2bc982174295faea3be1c0e51d05bcdf69b523..6ad4752401bd7fc60680e552015ba2d70d6796ec 100644 Binary files a/en/device-dev/driver/figures/using-watchdog-process.png and b/en/device-dev/driver/figures/using-watchdog-process.png differ