Hi3516驅動開發全面詳解
驅動框架介紹
驅動子系統
內核驅動是軟件與硬件交互的橋梁,通過文件系統接口訪問OpenHarmony內核的硬件資源,是用戶與內核之間、進程與進程之間通信的一種方式。每類驅動代表一種能力,用戶可以根據需求選擇對應驅動,完成數據的傳輸。OpenHarmony驅動子系統采用C面向對象編程模型構建,通過平臺解耦、內核解耦,兼容不同內核,提供了歸一化的驅動平臺底座,旨在為開發者提供更精準、更高效的開發環境,力求做到一次開發,多系統部署。
1、架構
OpenHarmony驅動框架采用主從架構設計模式,圍繞著框架、模型、能力庫和工具四個維度能力展開構建。

2、目錄
- drivers
- ├── adapter #適配平臺差異性的代碼
- ├── framework #驅動框架核心代碼
- └── peripheral #外設驅動代碼
- └── liteos #hievent和tzdrivert驅動代碼。hievent:事件日志管理驅動;tzdrivert:用于ree/tee切換、通訊,提供應用層訪問的設備節點
3、驅動框架交互流程

驅動框架完成大部分驅動加載的動作,用戶只需注冊自己所需的接口和配置,然后驅動框架就會解析配置的內容,完成驅動加載和初始化動作。
4、安裝
OpenHarmony驅動主要部署在內核態,當前主要采用靜態鏈接方式,隨內核子系統編譯和系統鏡像打包。

5、HDF驅動加載的過程

HDF Framework
1、簡介
HDF Framework提供包括驅動框架、配置管理、配置解析、驅動通用框架模型、硬件通用平臺能力接口等,驅動框架的架構圖:

2、目錄
- /drivers/framework
- ├── ability #提供驅動開發的能力支持,如消息模型庫等
- │ ├── config #配置解析代碼
- │ └── sbuf #數據序列化代碼
- ├── core #實現驅動框架的核心代碼
- │ ├── adapter #實現對內核操作接口適配,提供抽象化的接口供開發者使用
- │ ├── common #驅動框架公共基礎代碼
- │ ├── host #驅動宿主環境模塊
- │ ├── manager #驅動框架管理模塊
- │ └── shared #host和manager共享模塊代碼
- ├── include #驅動框架對外提供能力的頭文件
- │ ├── config #提供配置解析能力的頭文件
- │ ├── core #驅動框架對外提供的頭文件
- │ ├── net #網絡數據操作相關的頭文件
- │ ├── osal #系統適配相關接口的頭文件
- │ ├── platform #平臺設備相關接口的頭文件
- │ ├── utils #驅動框架公共能力的頭文件
- │ └── wifi #WLAN對外提供能力的頭文件
- ├── model #提供驅動通用框架模型
- │ ├── display #顯示框架模型
- │ ├── input #輸入框架模型
- │ ├── network #WLAN框架模型
- │ └── sensor #Sensor驅動模型
- ├── support #提系統的基礎能力
- │ └── platform #平臺設備驅動框架及訪問接口,范圍包括GPIO、I2C、SPI等
- ├── tools #hdf框架工具相關的源碼
- │ └── hc-gen #配置管理工具源碼
- └── utils #提供基礎數據結構和算法等
HDF適配
HDF適配主要實現OpenHarmony驅動子系統內核或用戶態驅動框架的代碼和編譯腳本,提供驅動框架的能力。
2、目錄
- /drivers/adapter
- ├── khdf/linux #提供驅動框架對Linux內核依賴適配
- ├── khdf/liteos #提供驅動框架對LiteOS-A內核依賴適配
- ├── khdf/liteos_m #提供驅動框架對LiteOS-M內核依賴適配
- ├── uhdf #提供用戶態驅動接口對系統依賴適配
- └── uhdf2 #提供用戶態驅動框架對系統依賴適配
linux khdf
1、簡介
drivers/adapter/khdf/linux下提供OpenHarmony驅動子系統適配linux內核的代碼和編譯腳本,在linux內核中部署OpenHarmony驅動框架。
2、目錄
- /drivers/adapter/khdf/linux
- ├── config #linux內核下編譯配置解析代碼的編譯腳本
- ├── hcs #linux內核下HDF的配置管理目錄
- ├── manager #linux內核下啟動適配啟動HDF框架代碼
- ├── model #驅動模型適配linux代碼
- │ ├── display #顯示驅動模型
- │ ├── input #輸入驅動模型
- │ ├── network #wifi驅動模型
- │ └── sensor #傳感器驅動模型
- ├── network #適配linux內核網絡代碼
- ├── osal #適配linux內核的posix接口
- ├── platform #平臺設備接口適配linux內核代碼
- │ ├── emmc #emmc操作接口
- │ ├── gpio #gpio接口
- │ ├── i2c #i2c接口
- │ ├── mipi_dsi #mipi dsi接口
- │ ├── pwm #pwm接口
- │ ├── rtc #rtc接口
- │ ├── sdio #sdio接口
- │ ├── spi #spi接口
- │ ├── uart #uart接口
- │ └── watchdog #watchdog接口
peripheral
1、簡介
peripheral主要包含各外設器件驅動相關的HDI(Hardware Driver Interface)接口、HAL實現、驅動模型及測試用例等,根據模塊劃分不同目錄,具體模塊的相關信息可參閱各模塊子目錄下的readme。
2、目錄
代碼目錄為 /drivers/peripheral,其中包含的各子目錄簡介如下。
- audio:Audio HDI接口的定義,用于管理聲卡驅動的加載和卸載、創建音頻播放對象3及錄音對象、選擇音頻場景、設置音頻屬性及音量、控制音頻播放及錄音的啟停等。
- codec:Codec HDI接口的定義,這些接口對上層服務提供媒體編解碼的驅動能力。
- display:Display HDI 接口定義及其默認實現,對上層圖形服務提供顯示驅動能力,包括顯示圖層的管理、顯示內存的管理及硬件圖形加速。
- format:Format HDI接口定義,此類接口對上層服務提供媒體文件復用和解復用的驅動能力。
- input:Input HDI接口定義及其實現,對上層輸入系統服務提供操作input設備的驅動能力,包括input設備管理、業務流控制、數據上報等。
- sensor:Sensor HDI接口定義與實現,接口主要包括所有Sensor信息查詢、Sensor啟停、Sensor訂閱/去訂閱、Sensor參數配置等穩定的接口,簡化服務開發。
- wlan:WLAN HDI接口定義與實現,包括創建和銷毀HAL層和WLAN驅動的通道、獲取本設備支持的WLAN特性等。
HDF
1、HDF驅動開發簡介
HDF框架以組件化的驅動模型作為核心設計思路,為開發者提供更精細化的驅動管理,讓驅動開發和部署更加規范。HDF框架將一類設備驅動放在同一個host里面,開發者也可以將驅動功能分層獨立開發和部署,支持一個驅動多個node,HDF框架管理驅動模型如下圖所示:

基于HDF(Hardware Driver Foundation驅動框架)開發驅動,用戶只需注冊自己所需的接口和配置,然后驅動框架就會解析配置的內容,完成驅動加載和初始化動作。
開發者基于HDF驅動框架開發的驅動主要包含三大部分:
- 驅動程序部分----完成驅動的功能邏輯。
- 驅動配置信息----指示驅動的加載信息內容。
- 驅動資源配置----配置驅動的硬件配置信息。
驅動程序主要是完成驅動功能的邏輯代碼:
對于開發者首先看到的是驅動入口部分,驅動入口部分通過DriverEntry進行描述。
其中主要包含Bind, Init 和Release三個接口。
- struct HdfDriverEntry g_deviceSample = {
- .moduleVersion = 1,
- .moduleName = "sample_driver",
- .Bind = SampleDriverBind,
- .Init = SampleDriverInit,
- .Release = SampleDriverRelease,
- };
Bind接口描述:該接口的作用主要是完成驅動設備和設備服務接口的bind動作。
- int32_t SampleDriverBind(struct HdfDeviceObject *deviceObject)
- {
- // TODO: Bind device service to device object.
- // And you can also initialize device resources here.
- return HDF_SUCCESS;
- }
Init接口描述:當框架完成設備綁定動作后,就開始調用驅動初始化接口,當初始化成功后,驅動框架根據配置文件決定是否對外創建設備服務接口,還是只是對當前服務接口可見。如果Init初始化失敗的話,驅動框架就會主動釋放創建的設備接口等信息。
- int32_t SampleDriverInit(struct HdfDeviceObject *deviceObject)
- {
- // TODO: Init hardware or other resources here.
- return HDF_SUCCESS;
- }
Release接口描述:當用戶需要卸載驅動時,驅動框架先通過該接口通知驅動程序釋放資源。然后在執行其他內部資源釋放。
- void SampleDriverRelease(struct HdfDeviceObject *deviceObject)
- {
- // Release all resources.
- return;
- }
2、驅動加載
HDF驅動加載包括按需加載和按序加載。
- 按需加載
HDF框架支持驅動在系統啟動過程中默認加載,或者在系統啟動之后動態加載。
- typedef enum { DEVICE_PRELOAD_ENABLE = 0, DEVICE_PRELOAD_ENABLE_STEP2, DEVICE_PRELOAD_DISABLE, DEVICE_PRELOAD_INVALID} DevicePreload;
- 1.
- 1.
- 1.
復制配置文件中preload 字段配成 0 (DEVICE_PRELOAD_ENABLE ),則系統啟動過程中默認加載;配成1(DEVICE_PRELOAD_ENABLE_STEP2),當系統支持快啟的時候,則在系統系統完成之后再加載這一類驅動,否則和DEVICE_PRELOAD_ENABLE 含義相同;
配成2(DEVICE_PRELOAD_DISABLE),則系統啟動過程中默認不加載,支持后續動態加載,當用戶態獲取驅動服務(參考消息機制)時,如果驅動服務不存在時,HDF框架會嘗試動態加載該驅動。
- 按序加載
HDF框架支持驅動在系統啟動的過程中按照驅動的優先級進行加載。
配置文件中的priority(取值范圍為整數0到200)是用來表示host和驅動的優先級,不同的host內的驅動,host的priority值越小,驅動加載優先級越高;同一個host內驅動的priority值越小,加載優先級越高。
3、驅動服務管理
HDF框架可以集中管理驅動服務,開發者可直接通過HDF框架對外提供的能力接口獲取驅動相關的服務。
4、驅動消息機制
HDF框架提供統一的驅動消息機制,支持用戶態應用向內核態驅動發送消息,也支持內核態驅動向用戶態應用發送消息。
驅動開發步驟
驅動實現
驅動實現包含驅動業務代碼和驅動入口注冊,具體寫法如下:
驅動業務代碼
- #include "hdf_device_desc.h" // HDF框架對驅動開放相關能力接口的頭文件#include "hdf_log.h" // HDF 框架提供的日志接口頭文件#define HDF_LOG_TAG "sample_driver" // 打印日志所包含的標簽,如果不定義則用默認定義的HDF_TAG標簽//驅動對外提供的服務能力,將相關的服務接口綁定到HDF框架int32_t HdfSampleDriverBind(struct HdfDeviceObject *deviceObject){ HDF_LOGD("Sample driver bind success"); return 0;}// 驅動自身業務初始的接口int32_t HdfSampleDriverInit(struct HdfDeviceObject *deviceObject){ HDF_LOGD("Sample driver Init success"); return 0;}// 驅動資源釋放的接口void HdfSampleDriverRelease(struct HdfDeviceObject *deviceObject){ HDF_LOGD("Sample driver release success"); return;}
驅動入口注冊到HDF框架
- // 定義驅動入口的對象,必須為HdfDriverEntry(在hdf_device_desc.h中定義)類型的全局變量struct HdfDriverEntry g_sampleDriverEntry = { .moduleVersion = 1, .moduleName = "sample_driver", .Bind = HdfSampleDriverBind, .Init = HdfSampleDriverInit, .Release = HdfSampleDriverRelease,};// 調用HDF_INIT將驅動入口注冊到HDF框架中,在加載驅動時HDF框架會先調用Bind函數,再調用Init函數加載該驅動,當Init調用異常時,HDF框架會調用Release釋放驅動資源并退出。HDF_INIT(g_sampleDriverEntry);
驅動編譯
驅動代碼的編譯必須要使用HDF框架提供的Makefile模板進行編譯。
- include $(LITEOSTOPDIR)/../../drivers/adapter/lite/khdf/lite.mk #導入hdf預定義內容,必需MODULE_NAME := #生成的結果文件LOCAL_INCLUDE := #本驅動的頭文件目錄LOCAL_SRCS := #本驅動的源代碼文件LOCAL_CFLAGS := #自定義的編譯選項include $(HDF_DRIVER) #導入模板makefile完成編譯
編譯結果文件鏈接到內核鏡像,添加到device目錄下的lite.mk里面,示例如下:
- LITEOS_BASELIB += -lxxx #鏈接生成的靜態庫LIB_SUBDIRS += #驅動代碼Makefile的目錄
驅動配置
HDF使用HCS作為配置描述源碼,HCS詳細介紹參考配置管理介紹。
驅動配置包含兩部分,HDF框架定義的驅動設備描述和驅動的私有配置信息,具體寫法如下:
- 驅動設備描述(必選)
HDF框架加載驅動所需要的信息來源于HDF框架定義的驅動設備描述,因此基于HDF框架開發的驅動必須要在HDF框架定義的device_info.hcs配置文件中添加對應的設備描述,驅動的設備描述填寫如下所示:
- root { device_info { match_attr = "hdf_manager"; template host { // host模板,繼承該模板的節點(如下sample_host)如果使用模板中的默認值,則節點字段可以缺省 hostName = ""; priority = 100; template device { template deviceNode { policy = 0; priority = 100; preload = 0; permission = 0664; moduleName = ""; serviceName = ""; deviceMatchAttr = ""; } } } sample_host :: host{ hostName = "host0"; // host名稱,host節點是用來存放某一類驅動的容器 priority = 100; // host啟動優先級(0-200),值越大優先級越低,建議默認配100,優先級相同則不保證host的加載順序 device_sample :: device { // sample設備節點 device0 :: deviceNode { // sample驅動的DeviceNode節點 policy = 1; // policy字段是驅動服務發布的策略,在驅動服務管理章節有詳細介紹 priority = 100; // 驅動啟動優先級(0-200),值越大優先級越低,建議默認配100,優先級相同則不保證device的加載順序 preload = 0; // 驅動按需加載字段,在本章節最后的說明有詳細介紹 permission = 0664; // 驅動創建設備節點權限 moduleName = "sample_driver"; // 驅動名稱,該字段的值必須和驅動入口結構的moduleName值一致 serviceName = "sample_service"; // 驅動對外發布服務的名稱,必須唯一 deviceMatchAttr = "sample_config"; // 驅動私有數據匹配的關鍵字,必須和驅動私有數據配置表中的match_attr值相等 } } } }}
- 驅動私有配置信息(可選)
如果驅動有私有配置,則可以添加一個驅動的配置文件,用來填寫一些驅動的默認配置信息,HDF框架在加載驅動的時候,會將對應的配置信息獲取并保存在HdfDeviceObject 中的property里面,通過Bind和Init(參考驅動開發)傳遞給驅動,驅動的配置信息示例如下:
- root { SampleDriverConfig { sample_version = 1; sample_bus = "I2C_0"; match_attr = "sample_config"; //該字段的值必須和device_info.hcs中的deviceMatchAttr值一致 }}
配置信息定義之后,需要將該配置文件添加到板級配置入口文件hdf.hcs(這一塊可以通過OpenHarmony驅動子系統在DevEco集成驅動開發套件工具一鍵式配置,具體使用方法參考驅動開發套件中的介紹),示例如下:
- #include "device_info/device_info.hcs"#include "sample/sample_config.hcs"
驅動開發示例
下面基于HDF框架,提供一個簡單的UART(Universal Asynchronous Receiver/Transmitter)平臺驅動開發樣例,包含配置文件的添加,驅動代碼的實現以及用戶態程序和驅動交互的流程。驅動程序源碼位于vendor/huawei/hdf/sample目錄
添加配置
HCS(HDF Configuration Source)是HDF驅動框架的配置描述源碼,內容以Key-Value為主要形式。它實現了配置代碼與驅動代碼解耦,便于開發者進行配置管理。
HC-GEN**(HDF Configuration Generator)**是HCS配置轉換工具,可以將HDF配置文件轉換為軟件可讀取的文件格式:
在弱性能環境中,轉換為配置樹源碼,驅動可直接調用C代碼獲取配置。
在高性能環境中,轉換為HCB(HDF Configuration Binary)二進制文件,驅動可使用HDF框架提供的配置解析接口獲取配置。
設備配置文件和驅動配置文件的路徑定義在板級配置入口文件hdf.hcs(vendor\hisilicon\hispark_taurus\config\hdf.hcs)
- #include "../../../../device/hisilicon/hispark_taurus/sdk_liteos/config/i2c/i2c_config.hcs"#include "../../../../device/hisilicon/hispark_taurus/sdk_liteos/config/gpio/gpio_config.hcs"#include "../../../../device/hisilicon/hispark_taurus/sdk_liteos/config/uart/uart_config.hcs"#include "../../../../device/hisilicon/hispark_taurus/sdk_liteos/config/sdio/sdio_config.hcs"#include "../../../../device/hisilicon/hispark_taurus/sdk_liteos/config/emmc/emmc_config.hcs"#include "../../../../device/hisilicon/hispark_taurus/sdk_liteos/config/watchdog/watchdog_config.hcs"#include "../../../../device/hisilicon/hispark_taurus/sdk_liteos/config/usb/usb_config.hcs"#include "../../../../device/hisilicon/hispark_taurus/sdk_liteos/config/rtc/rtc_config.hcs"#include "../../../../device/hisilicon/hispark_taurus/sdk_liteos/config/spi/spi_config.hcs"#include "../../../../device/hisilicon/hispark_taurus/sdk_liteos/config/pwm/pwm_config.hcs"#include "../../../../device/hisilicon/hispark_taurus/sdk_liteos/config/dmac/dmac_config.hcs"#include "device_info/device_info.hcs"#include "wifi/wlan_platform.hcs"#include "wifi/wlan_chip_hi3881.hcs"#include "lcd/lcd_config.hcs"#include "input/input_config.hcs"#include "sensor/sensor_config.hcs"root { module = "hisilicon,hi35xx_chip";}
1、在HDF框架的驅動配置文件(例如device\hisilicon\hispark_taurus\sdk_liteos\config\uart\uart_config.hcs)中添加該驅動的配置信息,如下所示:
- root { platform { uart_sample { num = 5; base = 0x120a0000; // UART base register address irqNum = 38; baudrate = 115200; uartClk = 24000000; // 24 M wlen = 0x60; // 8 bit width parity = 0; stopBit = 0; match_attr = "sample_uart_5"; } }}
2、在HDF框架的設備配置文件(例如vendor\hisilicon\hispark_taurus\config\device_info\device_info.hcs)中添加該驅動的設備節點信息,如下所示:
- root { device_info { platform :: host { hostName = "platform_host"; priority = 50; device_uart :: device { device5 :: deviceNode { policy = 2; priority = 10; permission = 0660; moduleName = "UART_SAMPLE"; serviceName = "HDF_PLATFORM_UART_5"; deviceMatchAttr = "sample_uart_5"; } } } }}
注冊uart驅動入口
基于HDF框架注冊UART驅動的入口HdfDriverEntry,代碼如下:
- // 綁定UART驅動接口到HDF框架static int32_t SampleUartDriverBind(struct HdfDeviceObject *device){ struct UartHost *uartHost = NULL; if (device == NULL) { return HDF_ERR_INVALID_OBJECT; } HDF_LOGI("Enter %s:", __func__); uartHost = UartHostCreate(device); if (uartHost == NULL) { HDF_LOGE("%s: UartHostCreate failed", __func__); return HDF_FAILURE; } uartHost->service.Dispatch = SampleDispatch; return HDF_SUCCESS;} // 從UART驅動的HCS中獲取配置信息static uint32_t GetUartDeviceResource( struct UartDevice *device, const struct DeviceResourceNode *resourceNode){ struct UartResource *resource = &device->resource; struct DeviceResourceIface *dri = NULL; dri = DeviceResourceGetIfaceInstance(HDF_CONFIG_SOURCE); if (dri == NULL || dri->GetUint32 == NULL) { HDF_LOGE("DeviceResourceIface is invalid"); return HDF_FAILURE; } if (dri->GetUint32(resourceNode, "num", &resource->num, 0) != HDF_SUCCESS) { HDF_LOGE("uart config read num fail"); return HDF_FAILURE; } if (dri->GetUint32(resourceNode, "base", &resource->base, 0) != HDF_SUCCESS) { HDF_LOGE("uart config read base fail"); return HDF_FAILURE; } resource->physBase = (unsigned long)OsalIoRemap(resource->base, 0x48); if (resource->physBase == 0) { HDF_LOGE("uart config fail to remap physBase"); return HDF_FAILURE; } if (dri->GetUint32(resourceNode, "irqNum", &resource->irqNum, 0) != HDF_SUCCESS) { HDF_LOGE("uart config read irqNum fail"); return HDF_FAILURE; } if (dri->GetUint32(resourceNode, "baudrate", &resource->baudrate, 0) != HDF_SUCCESS) { HDF_LOGE("uart config read baudrate fail"); return HDF_FAILURE; } if (dri->GetUint32(resourceNode, "wlen", &resource->wlen, 0) != HDF_SUCCESS) { HDF_LOGE("uart config read wlen fail"); return HDF_FAILURE; } if (dri->GetUint32(resourceNode, "parity", &resource->parity, 0) != HDF_SUCCESS) { HDF_LOGE("uart config read parity fail"); return HDF_FAILURE; } if (dri->GetUint32(resourceNode, "stopBit", &resource->stopBit, 0) != HDF_SUCCESS) { HDF_LOGE("uart config read stopBit fail"); return HDF_FAILURE; } if (dri->GetUint32(resourceNode, "uartClk", &resource->uartClk, 0) != HDF_SUCCESS) { HDF_LOGE("uart config read uartClk fail"); return HDF_FAILURE; } return HDF_SUCCESS;} // 將UART驅動的配置和接口附加到HDF驅動框架static int32_t AttachUartDevice(struct UartHost *host, struct HdfDeviceObject *device){ int32_t ret; struct UartDevice *uartDevice = NULL; if (device->property == NULL) { HDF_LOGE("%s: property is NULL", __func__); return HDF_FAILURE; } uartDevice = (struct UartDevice *)OsalMemCalloc(sizeof(struct UartDevice)); if (uartDevice == NULL) { HDF_LOGE("%s: OsalMemCalloc uartDevice error", __func__); return HDF_ERR_MALLOC_FAIL; } ret = GetUartDeviceResource(uartDevice, device->property); if (ret != HDF_SUCCESS) { (void)OsalMemFree(uartDevice); return HDF_FAILURE; } host->num = uartDevice->resource.num; host->priv = uartDevice; AddUartDevice(host); return InitUartDevice(uartDevice);} // 初始化UART驅動static int32_t SampleUartDriverInit(struct HdfDeviceObject *device){ int32_t ret; struct UartHost *host = NULL; if (device == NULL) { HDF_LOGE("%s: device is NULL", __func__); return HDF_ERR_INVALID_OBJECT; } HDF_LOGI("Enter %s:", __func__); host = UartHostFromDevice(device); if (host == NULL) { HDF_LOGE("%s: host is NULL", __func__); return HDF_FAILURE; } ret = AttachUartDevice(host, device); if (ret != HDF_SUCCESS) { HDF_LOGE("%s: attach error", __func__); return HDF_FAILURE; } host->method = &g_sampleUartHostMethod; return ret;} static void DeinitUartDevice(struct UartDevice *device){ struct UartRegisterMap *regMap = (struct UartRegisterMap *)device->resource.physBase; /* wait for uart enter idle. */ while (UartPl011IsBusy(regMap)); UartPl011ResetRegisters(regMap); uart_clk_cfg(0, false); OsalIoUnmap((void *)device->resource.physBase); device->state = UART_DEVICE_UNINITIALIZED;} // 解綁并釋放UART驅動static void DetachUartDevice(struct UartHost *host){ struct UartDevice *uartDevice = NULL; if (host->priv == NULL) { HDF_LOGE("%s: invalid parameter", __func__); return; } uartDevice = host->priv; DeinitUartDevice(uartDevice); (void)OsalMemFree(uartDevice); host->priv = NULL;} // 釋放UART驅動static void SampleUartDriverRelease(struct HdfDeviceObject *device){ struct UartHost *host = NULL; HDF_LOGI("Enter %s:", __func__); if (device == NULL) { HDF_LOGE("%s: device is NULL", __func__); return; } host = UartHostFromDevice(device); if (host == NULL) { HDF_LOGE("%s: host is NULL", __func__); return; } if (host->priv != NULL) { DetachUartDevice(host); } UartHostDestroy(host);} struct HdfDriverEntry g_sampleUartDriverEntry = { .moduleVersion = 1, .moduleName = "UART_SAMPLE", .Bind = SampleUartDriverBind, .Init = SampleUartDriverInit, .Release = SampleUartDriverRelease,}; HDF_INIT(g_sampleUartDriverEntry);
注冊uart驅動接口
HDF框架提供了UART驅動接口的模板方法UartHostMethod,實現UART驅動接口的代碼如下:
- static int32_t SampleUartHostInit(struct UartHost *host){ HDF_LOGI("%s: Enter", __func__); if (host == NULL) { HDF_LOGE("%s: invalid parameter", __func__); return HDF_ERR_INVALID_PARAM; } return HDF_SUCCESS;}static int32_t SampleUartHostDeinit(struct UartHost *host){ HDF_LOGI("%s: Enter", __func__); if (host == NULL) { HDF_LOGE("%s: invalid parameter", __func__); return HDF_ERR_INVALID_PARAM; } return HDF_SUCCESS;}// 向UART中寫入數據static int32_t SampleUartHostWrite(struct UartHost *host, uint8_t *data, uint32_t size){ HDF_LOGI("%s: Enter", __func__); uint32_t idx; struct UartRegisterMap *regMap = NULL; struct UartDevice *device = NULL; if (host == NULL || data == NULL || size == 0) { HDF_LOGE("%s: invalid parameter", __func__); return HDF_ERR_INVALID_PARAM; } device = (struct UartDevice *)host->priv; if (device == NULL) { HDF_LOGE("%s: device is NULL", __func__); return HDF_ERR_INVALID_PARAM; } regMap = (struct UartRegisterMap *)device->resource.physBase; for (idx = 0; idx < size; idx++) { UartPl011Write(regMap, data[idx]); } return HDF_SUCCESS;} // 設置UART的波特率static int32_t SampleUartHostSetBaud(struct UartHost *host, uint32_t baudRate){ HDF_LOGI("%s: Enter", __func__); struct UartDevice *device = NULL; struct UartRegisterMap *regMap = NULL; UartPl011Error err; if (host == NULL) { HDF_LOGE("%s: invalid parameter", __func__); return HDF_ERR_INVALID_PARAM; } device = (struct UartDevice *)host->priv; if (device == NULL) { HDF_LOGE("%s: device is NULL", __func__); return HDF_ERR_INVALID_PARAM; } regMap = (struct UartRegisterMap *)device->resource.physBase; if (device->state != UART_DEVICE_INITIALIZED) { return UART_PL011_ERR_NOT_INIT; } if (baudRate == 0) { return UART_PL011_ERR_INVALID_BAUD; } err = UartPl011SetBaudrate(regMap, device->uartClk, baudRate); if (err == UART_PL011_ERR_NONE) { device->baudrate = baudRate; } return err;} // 獲取UART的波特率static int32_t SampleUartHostGetBaud(struct UartHost *host, uint32_t *baudRate){ HDF_LOGI("%s: Enter", __func__); struct UartDevice *device = NULL; if (host == NULL) { HDF_LOGE("%s: invalid parameter", __func__); return HDF_ERR_INVALID_PARAM; } device = (struct UartDevice *)host->priv; if (device == NULL) { HDF_LOGE("%s: device is NULL", __func__); return HDF_ERR_INVALID_PARAM; } *baudRate = device->baudrate; return HDF_SUCCESS;} // 在HdfUartSampleInit方法中綁定struct UartHostMethod g_sampleUartHostMethod = { .Init = SampleUartHostInit, .Deinit = SampleUartHostDeinit, .Read = NULL, .Write = SampleUartHostWrite, .SetBaud = SampleUartHostSetBaud, .GetBaud = SampleUartHostGetBaud, .SetAttribute = NULL, .GetAttribute = NULL, .SetTransMode = NULL,};
uart驅動Makefile如下:
- include $(LITEOSTOPDIR)/config.mkinclude $(LITEOSTOPDIR)/../../drivers/adapter/khdf/liteos/lite.mkMODULE_NAME := hdf_uart_sampleLOCAL_CFLAGS += $(HDF_INCLUDE)LOCAL_SRCS += src/uart_sample.c \ src/uart_pl011_sample.c \ src/buf_fifo.c \ src/uart_dev_sample.c \ src/uart_dispatch_sample.c \LOCAL_INCLUDE := ./includeLOCAL_CFLAGS += -fstack-protector-stronginclude $(HDF_DRIVER)
在device/hisilicon/drivers/lite.mk編譯腳本中增加示例UART驅動模塊,代碼如下:
- LITEOS_BASELIB += -lhdf_uart_sampleLIB_SUBDIRS += $(LITEOS_SOURCE_ROOT)/vendor/huawei/hdf/sample/platform/uart
用戶程序和驅動交互代碼
UART驅動成功初始化后,會創建/dev/uartdev-5設備節點,通過設備節點與UART驅動交互的代碼如下:
- #include #include #include #include "hdf_log.h"#define HDF_LOG_TAG "hello_uart"#define INFO_SIZE 16int main(void){ int ret; int fd; const char info[INFO_SIZE] = {" HELLO UART! "}; fd = open("/dev/uartdev-5", O_RDWR); if (fd < 0) { HDF_LOGE("hello_uart uartdev-5 open failed %d", fd); return -1; } ret = write(fd, info, INFO_SIZE); if (ret != 0) { HDF_LOGE("hello_uart write uartdev-5 ret is %d", ret); } ret = close(fd); if (ret != 0) { HDF_LOGE("hello_uart uartdev-5 close failed %d", fd); return -1; } return ret;}
將應用程序編譯進hello_uart_sample組件,應用的編譯文件:
(1)vendor/huawei/hdf/sample/platform/uart/BUILD.gn
- import("//build/lite/config/component/lite_component.gni")lite_component("hello_uart_sample") { features = [ "dev:hello_uart", "dispatch:hello_uart_dispatch", ]}
(2)vendor/huawei/hdf/sample/platform/uart/dev/BUILD.gn:
- HDF_FRAMEWORKS = "//drivers/framework"executable("hello_uart") { sources = [ "hello_uart_dev.c" ] include_dirs = [ "$HDF_FRAMEWORKS/ability/sbuf/include", "$HDF_FRAMEWORKS/core/shared/include", "$HDF_FRAMEWORKS/core/host/include", "$HDF_FRAMEWORKS/core/master/include", "$HDF_FRAMEWORKS/include/core", "$HDF_FRAMEWORKS/include/utils", "$HDF_FRAMEWORKS/utils/include", "$HDF_FRAMEWORKS/include/osal", "//drivers/adapter/uhdf/posix/include", "//third_party/bounds_checking_function/include", "//base/hiviewdfx/hilog_lite/interfaces/native/innerkits", ] deps = [ "//base/hiviewdfx/hilog_lite/frameworks/featured:hilog_shared", "//drivers/adapter/uhdf/manager:hdf_core", "//drivers/adapter/uhdf/posix:hdf_posix_osal", ] public_deps = [ "//third_party/bounds_checking_function:libsec_shared" ] defines = [ "__USER__" ] cflags = [ "-Wall", "-Wextra", "-Wno-format", "-Wno-format-extra-args", ]}
在build/lite/components/drivers.json驅動配置中hdf_hi3516dv300_liteos_a組件下的targets中增加hello_uart_sample組件,代碼如下:
- { "components": [ { "component": "hdf_hi3516dv300_liteos_a", "description": "", "optional": "false", "dirs": ["vendor/huawei/hdf/sample/platform/uart"], "targets": [ "//vendor/huawei/hdf/sample/platform/uart:hello_uart_sample" ], "rom": "", "ram": "", "output": [], "adapted_board": [], "adapted_kernel": [ "liteos_a" ], "features": [], "deps": { "third_party": [ "bounds_checking_function" ], "components": [ "hilog" ] } } ]}
修改單板配置文件(vendor/hisilicon/hispark_taurus/config.json),新增hdf_hi3516dv300_liteos_a組件的條目,如下所示代碼片段為driver子系統配置
- { "subsystem": "drivers", "components": [ { "component": "adapter_uhdf", "features":[] }, { "component": "peripheral_display", "features":[] }, { "component": "hdf_hi3516dv300_liteos_a", "features":[] }, { "component": "peripheral_input", "features":[] }, { "component": "peripheral_sensor", "features":[] }, { "component": "peripheral_wlan", "features":[] } ] },
編譯
如果Linux編譯環境通過Docker方式安裝,具體編譯過程請參見Docker方式獲取編譯環境的編譯操作。如果Linux編譯環境通過軟件包方式安裝,請進入源碼根目錄,執行如下命令進行編譯:
- hb set(設置編譯路徑).(選擇當前路徑)選擇ipcamera_hispark_taurus并回車hb build -f(執行編譯)
運行
燒錄成功之后,可執行文件在bin目錄下(/bin/hello_uart),運行:
