• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1# UART
2
3
4## Overview
5
6In 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.
7
8  **Figure 1** Independent service mode
9
10  ![image](figures/independent-service-mode.png)
11
12
13## Available APIs
14
15**UartHostMethod**:
16
17
18```
19struct UartHostMethod {
20  int32_t (*Init)(struct UartHost *host);
21  int32_t (*Deinit)(struct UartHost *host);
22  int32_t (*Read)(struct UartHost *host, uint8_t *data, uint32_t size);
23  int32_t (*Write)(struct UartHost *host, uint8_t *data, uint32_t size);
24  int32_t (*GetBaud)(struct UartHost *host, uint32_t *baudRate);
25  int32_t (*SetBaud)(struct UartHost *host, uint32_t baudRate);
26  int32_t (*GetAttribute)(struct UartHost *host, struct UartAttribute *attribute);
27  int32_t (*SetAttribute)(struct UartHost *host, struct UartAttribute *attribute);
28  int32_t (*SetTransMode)(struct UartHost *host, enum UartTransMode mode);
29  int32_t (*pollEvent)(struct UartHost *host, void *filep, void *table);
30};
31```
32
33  **Table 1** Description of the callback functions in UartHostMethod
34
35| Function| Input Parameter| Output Parameter| Return Value| Description|
36| -------- | -------- | -------- | -------- | -------- |
37| Init | **host**: structure pointer to the UART controller at the core layer.| –| HDF_STATUS| Initializes a UART device.|
38| Deinit | **host**: structure pointer to the UART controller at the core layer.| –| HDF_STATUS| Deinitializes a UART device.|
39| Read | **host**: structure pointer to the UART controller at the core layer.<br>**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.|
40| Write | **host**: structure pointer to the UART controller at the core layer.<br>**data**: pointer to the data to write. The value is of the uint8_t type.<br>**size**: data size, which is of the uint32_t type. | –| HDF_STATUS| Writes data.|
41| SetBaud | **host**: structure pointer to the UART controller at the core layer.<br>**baudRate**: pointer to the baud rate to set. The value is of the uint32_t type. | –| HDF_STATUS| Sets the baud rate.|
42| 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.|
43| 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.|
44| SetAttribute | **host**: structure pointer to the UART controller at the core layer.<br>**attribute**: structure pointer to the attribute to set. | –| HDF_STATUS| Sets UART attributes.|
45| SetTransMode | **host**: structure pointer to the UART controller at the core layer.<br>**mode**: transfer mode to set. For details, see **UartTransMode** in **uart_if.h**.| –| HDF_STATUS| Sets the UART transfer mode.|
46| PollEvent | **host**: structure pointer to the UART controller at the core layer.<br>**filep**: void pointer to a file.<br>**table**: void pointer to poll_table.| –| HDF_STATUS| Polls for pending events.|
47
48
49## How to Develop
50
51The UART module adaptation involves the following steps:
52
531. Instantiate the driver entry.
54   - Instantiate the **HdfDriverEntry** structure.
55   - Call **HDF_INIT** to register the **HdfDriverEntry** instance with the HDF.
56
572. Configure attribute files.
58   - Add the **deviceNode** information to the **device_info.hcs** file.
59   - (Optional) Add the **uart_config.hcs** file.
60
613. Instantiate the UART controller object.
62   - Initialize **UartHost**.
63   - Instantiate **UartHostMethod** in the **UartHost** object.
64      > ![icon-note.gif](public_sys-resources/icon-note.gif) **NOTE**<br>
65      > For details about the functions in **UartHostMethod**, see [Available APIs](#available-apis).
66
674. Debug the driver.
68
69   (Optional) For new drivers, verify the basic functions, such as the UART status control and response to interrupts.
70
71
72## Development Example
73
74The following uses **uart_hi35xx.c** as an example to present the information required for implementing device functions.
75
761. Instantiate the driver entry.
77
78   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**.
79
80   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.
81
82   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.
83
84   UART driver entry example:
85
86   ```
87   struct HdfDriverEntry g_hdfUartDevice = {
88       .moduleVersion = 1,
89       .moduleName = "HDF_PLATFORM_UART", // (Mandatory) The value must be the same as that in the .hcs file.
90       .Bind = HdfUartDeviceBind, // See the Bind function.
91       .Init = HdfUartDeviceInit,  // See the Init function.
92       .Release = HdfUartDeviceRelease, //See the Release function.
93   };
94   // Call HDF_INIT to register the driver entry with the HDF.
95   HDF_INIT(g_hdfUartDevice);
96   ```
97
982. Add the **deviceNode** information to the **device_info.hcs** file and configure the device attributes in the **uart_config.hcs** file.
99
100   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.
101
102   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.
103
104   - **device_info.hcs** configuration example:
105
106
107     ```
108     root {
109       device_info {
110         match_attr = "hdf_manager";
111         platform :: host {
112           hostName = "platform_host";
113           priority = 50;
114           device_uart :: device {
115             device0 :: deviceNode {
116               policy = 1;                                 // The driver publishes services only for kernel-mode processes.
117               priority = 40;                              // Driver startup priority.
118               permission = 0644;                         // Permission for the driver to create a device node.
119               moduleName = "HDF_PLATFORM_UART";           // Driver name, which must be the same as moduleName in the HdfDriverEntry structure.
120               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.
121               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.
122             }
123             device1 :: deviceNode {
124               policy = 2;    // The driver publishes services for both kernel- and user-mode processes.
125               permission = 0644;
126               priority = 40;
127               moduleName = "HDF_PLATFORM_UART";
128               serviceName = "HDF_PLATFORM_UART_1";
129               deviceMatchAttr = "hisilicon_hi35xx_uart_1";
130             }
131             ...
132           }
133         }
134       }
135     }
136     ```
137
138   - **uart_config.hcs** configuration example
139
140
141     ```
142     root {
143       platform {
144         template uart_controller { // Template configuration. In the template, you can configure the common parameters shared by device nodes.
145           match_attr = "";
146           num = 0;                 // (Mandatory) Device number.
147           baudrate = 115200;       // (Mandatory) Baud rate. Set the value based on service requirements.
148           fifoRxEn = 1;            // (Mandatory) Enable FIFOs to be received.
149           fifoTxEn = 1;            // (Mandatory) Enable FIFOs to be transferred.
150           flags = 4;               // (Mandatory) Flag signal.
151           regPbase = 0x120a0000;   // (Mandatory) Used for address mapping.
152           interrupt = 38;          // (Mandatory) Interrupt number.
153           iomemCount = 0x48;       // (Mandatory) Used for address mapping.
154         }
155         controller_0x120a0000 :: uart_controller {
156           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.
157         }
158         controller_0x120a1000 :: uart_controller {
159           num = 1;
160           baudrate = 9600;
161           regPbase = 0x120a1000;
162           interrupt = 39;
163           match_attr = "hisilicon_hi35xx_uart_1";
164         }
165         ...
166         //(Optional) Add more controller data. The node information must have been added in the device_info.hcs file.
167       }
168     }
169     ```
170
1713. 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).
172
173   - Defining a custom structure
174
175      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.
176
177
178      ```
179      struct UartPl011Port {                   // Interface structure
180          int32_t             enable;
181          unsigned long       physBase;        // Physical address
182          uint32_t            irqNum;          // Interrupt number
183          uint32_t            defaultBaudrate; // Default baud rate
184          uint32_t            flags;           // Flags related to the following three macros
185      #define PL011_FLG_IRQ_REQUESTED    (1 << 0)
186      #define PL011_FLG_DMA_RX_REQUESTED (1 << 1)
187      #define PL011_FLG_DMA_TX_REQUESTED (1 << 2)
188          struct UartDmaTransfer *rxUdt;       // DMA transfer
189          struct UartDriverData *udd;          // The data structure is defined as follows:
190      };
191      struct UartDriverData {                  // Structure related to data transfer
192          uint32_t num;
193          uint32_t baudrate;                   // Baud rate (configurable)
194          struct UartAttribute attr;           // Attributes, such as the data bit and stop bit, related to data transfer.
195          struct UartTransfer *rxTransfer;     // Buffer (FIFO structure)
196          wait_queue_head_t wait;              // Queuing signal related to conditional variables
197          int32_t count;                       // Data count
198          int32_t state;                       // UART controller state
199      #define UART_STATE_NOT_OPENED 0
200      #define UART_STATE_OPENING    1
201      #define UART_STATE_USEABLE    2
202      #define UART_STATE_SUSPENDED  3
203          uint32_t flags;                      // Status flags
204      #define UART_FLG_DMA_RX       (1 << 0)
205      #define UART_FLG_DMA_TX       (1 << 1)
206      #define UART_FLG_RD_BLOCK     (1 << 2)
207          RecvNotify recv;                     // Pointer to the function that receives serial port data.
208          struct UartOps *ops;                 // Custom function pointer structure. For details, see device/hisilicon/drivers/uart/uart_pl011.c.
209          void *private;                       // It stores the pointer to the start address of UartPl011Port for easy invocation.
210      };
211
212      // UartHost is the controller structure at the core layer. The Init function assigns values to the members of UartHost.
213      struct UartHost {
214          struct IDeviceIoService service;
215          struct HdfDeviceObject *device;
216          uint32_t num;
217          OsalAtomic atom;
218          void *priv;                         // It stores the pointer to the start address of the vendor's custom structure for easy invocation.
219          struct UartHostMethod *method;      // Hook at the core layer. You need to implement and instantiate its member functions.
220      };
221      ```
222
223   - Instantiating **UartHostMethod** in **UartHost** (other members are initialized by **Bind**)
224
225
226      ```
227      // Example in uart_hi35xx.c: instantiate the hook.
228      struct UartHostMethod g_uartHostMethod = {
229        .Init = Hi35xxInit,
230        .Deinit = Hi35xxDeinit,
231        .Read = Hi35xxRead,
232        .Write = Hi35xxWrite,
233        .SetBaud = Hi35xxSetBaud,
234        .GetBaud = Hi35xxGetBaud,
235        .SetAttribute = Hi35xxSetAttribute,
236        .GetAttribute = Hi35xxGetAttribute,
237        .SetTransMode = Hi35xxSetTransMode,
238        .pollEvent = Hi35xxPollEvent,
239      };
240      ```
241
242   - **Bind** function
243
244      **Input parameter**:
245
246      **HdfDeviceObject**, an interface parameter exposed by the driver, contains the .hcs information.
247
248      **Return value**:
249
250      **HDF_STATUS**<br/>The table below describes some status. For more information, see **HDF_STATUS** in the **/drivers/framework/include/utils/hdf_base.h** file.
251
252        **Table 2** Description of HDF_STATUS
253
254      | Status| Description|
255      | -------- | -------- |
256      | HDF_ERR_INVALID_OBJECT | Invalid controller object.|
257      | HDF_ERR_MALLOC_FAIL | Failed to allocate memory.|
258      | HDF_ERR_INVALID_PARAM | Invalid parameter.|
259      | HDF_ERR_IO | I/O error.|
260      | HDF_SUCCESS | Initialization successful.|
261      | HDF_FAILURE | Initialization failed.|
262
263      **Function description**:
264
265      Initializes the custom structure object and **UartHost**.
266
267
268      ```
269      //uart_hi35xx.c
270      static int32_t HdfUartDeviceBind(struct HdfDeviceObject *device)
271      {
272          ...
273          return (UartHostCreate(device) == NULL)? HDF_FAILURE: HDF_SUCCESS;// (Mandatory) Call UartHostCreate.
274      }
275      // Description of UartHostCreate() in uart_core.c
276      struct UartHost *UartHostCreate(struct HdfDeviceObject *device)
277      {
278          struct UartHost *host = NULL;                          // Create UartHost.
279          ...
280          host = (struct UartHost *)OsalMemCalloc(sizeof(*host));// Allocate memory.
281          ...
282          host->device = device;                           // (Mandatory) Prerequisites for conversion between HdfDeviceObject and UartHost.
283          device->service = &(host->service;              // (Mandatory) Prerequisites for conversion between HdfDeviceObject and UartHost.
284          host->device->service->Dispatch = UartIoDispatch;      // Assign values to Dispatch of service.
285          OsalAtomicSet(&host->atom, 0);                          // Initialize or set the atomic services.
286          host->priv = NULL;
287          host->method = NULL;
288          return host;
289      }
290      ```
291
292   - **Init** function
293
294      **Input parameter**:
295
296      **HdfDeviceObject**, an interface parameter exposed by the driver, contains the .hcs information.
297
298      **Return value**:
299
300      **HDF_STATUS**
301
302      **Function description**:
303
304      Initializes the custom structure object and **UartHost**, calls the **artAddDev** function at the core layer, and connects to the VFS.
305
306
307      ```
308      int32_t HdfUartDeviceInit(struct HdfDeviceObject *device)
309      {
310          int32_t ret;
311          struct UartHost *host = NULL;
312          HDF_LOGI("%s: entry", __func__);
313          ...
314          host = UartHostFromDevice(device);// Forcibly convert to UartHost by using service. The values are assigned by Bind().
315          ...
316          ret = Hi35xxAttach(host, device); // Initialize the UartHost object.
317          ...
318          host->method = &g_uartHostMethod; // Attach the UartHostMethod instance.
319          return ret;
320      }
321      // Initialize UartHost.
322      static int32_t Hi35xxAttach(struct UartHost *host, struct HdfDeviceObject *device)
323      {
324          int32_t ret;
325          // udd and port are customized structure objects. Implement the related functions as required.
326          struct UartDriverData *udd = NULL;
327          struct UartPl011Port *port = NULL;
328          ...
329          // Steps 1 to 7 instantiate and assign values to the udd object, and then assign values to UartHost.
330          udd = (struct UartDriverData *)OsalMemCalloc(sizeof(*udd));// Step 1
331          ...
332          port = (struct UartPl011Port *)OsalMemCalloc(sizeof(struct UartPl011Port));// Step 2
333          ...
334          udd->ops = Pl011GetOps();     // Step 3 Hook the functions for starting or stopping a device, setting attributes, and sending data.
335          udd->recv = PL011UartRecvNotify;// Step 4 Hook the data receiving notification function (conditional lock mechanism).
336          udd->count = 0;          // Step 5
337          port->udd = udd;          // Step 6  Enable conversion between UartPl011Port and UartDriverData.
338          ret = UartGetConfigFromHcs(port, device->property);// Pass the attributes of HdfDeviceObject to the custom structure.
339                                                             // The sample code is as follows:
340          ...
341          udd->private = port;     // Step 7
342
343          host->priv = udd;        // (Mandatory) Enable conversion between UartHost and UartDriverData.
344          host->num = udd->num; // (Mandatory) UART device number
345          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.
346          return HDF_SUCCESS;
347      }
348
349      static int32_t UartGetConfigFromHcs(struct UartPl011Port *port, const struct DeviceResourceNode *node)
350      {
351          uint32_t tmp, regPbase, iomemCount;
352          struct UartDriverData *udd = port->udd;
353          struct DeviceResourceIface *iface = DeviceResourceGetIfaceInstance(HDF_CONFIG_SOURCE);
354          ...
355          // Extract the values based on the request and assign the values to the custom structure.
356          if (iface->GetUint32(node, "num", &udd->num, 0) != HDF_SUCCESS) {
357              HDF_LOGE("%s: read busNum fail", __func__);
358              return HDF_FAILURE;
359          }
360          ...
361          return 0;
362          }
363      ```
364   - **Release** function
365
366      **Input parameter**:
367
368      **HdfDeviceObject**, an interface parameter exposed by the driver, contains the .hcs information.
369
370      **Return value**:
371
372      No value is returned.
373
374      **Function description**:
375
376      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.
377
378      > ![icon-note.gif](public_sys-resources/icon-note.gif) **NOTE**
379      >
380      > All forced conversion operations for obtaining the corresponding object can be successful only when **Init()** has the corresponding value assignment operations.
381
382
383      ```
384      void HdfUartDeviceRelease(struct HdfDeviceObject *device)
385      {
386          struct UartHost *host = NULL;
387          ...
388          host = UartHostFromDevice(device); // Forcibly convert HdfDeviceObject to UartHost by using service. For details about the value assignment, see the Bind function.
389          ...
390          if (host->priv != NULL) {
391              Hi35xxDetach(host);            // Customized memory release function.
392          }
393          UartHostDestroy(host);             // Call the function of the core layer to release the host.
394      }
395
396      static void Hi35xxDetach(struct UartHost *host)
397      {
398          struct UartDriverData *udd = NULL;
399          struct UartPl011Port *port = NULL;
400          ...
401          udd = host->priv;                  // The conversion from UartHost to UartDriverData is involved.
402          ...
403          UartRemoveDev (host);              // Remove the VFS.
404          port = udd->private;               // The conversion from UartDriverData to UartPl011Port is involved.
405          if (port != NULL) {
406              if (port->physBase != 0) {
407                  OsalIoUnmap((void *)port->physBase);// Unmap addresses.
408              }
409              OsalMemFree(port);
410              udd->private = NULL;
411          }
412          OsalMemFree (udd);                           // Release UartDriverData.
413          host->priv = NULL;
414      }
415      ```
416