class="hide-preCode-box">

此处以Hi3516DV300为例,给出HCS配置参考。其中部分字段为Hi3516DV300特有功能,驱动适配者可根据需要进行删除或添加字段。

        root {
            platform {
                gpio_config {
                    controller_0x120d0000 {
                        match_attr = "hisilicon_hi35xx_pl061";    // 【必要】必须和device_info.hcs中的deviceMatchAttr值一致
                        groupNum = 12;                            // 【必要】GPIO组索引,需要根据设备情况填写
                        bitNum = 8;                               // 【必要】每组GPIO管脚数
                        regBase = 0x120d0000;                     // 【必要】物理基地址
                        regStep = 0x1000;                         // 【必要】寄存器偏移步进
                        irqStart = 48;                            // 【必要】开启中断
                        irqShare = 0;                             // 【必要】共享中断
                    }
                    template gpio_info {                          // gpio_info模板
                        gpioCustomName = "";                      // gpio管脚默认名称
                    }
                    GPIO0 :: gpio_info {                          
                        gpioCustomName = "GPIO0_0";
                    }
                    ......
                }
            }
        }
        c
 class="hljs-button signin active" data-title="登录复制" data-report-click="{"spm":"1001.2101.3001.4334"}"> class="hide-preCode-box">

需要注意的是,新增gpio_config.hcs配置文件后,必须在产品对应的hdf.hcs文件中将其包含如下语句所示,否则配置文件无法生效。

        #include "../../../../device/soc/hisilicon/hi3516dv300/sdk_liteos/hdf_config/gpio/gpio_config.hcs" // 配置文件相对路径
        c
 class="hljs-button signin active" data-title="登录复制" data-report-click="{"spm":"1001.2101.3001.4334"}">

本例基于Hi3516DV300开发板的小型系统LiteOS内核运行,对应的hdf.hcs文件路径为vendor/hisilicon/hispark_taurus/hdf_config/hdf.hcs以及//device/hisilicon/hispark_taurus/sdk_liteos/hdf_config/hdf.hcs。驱动适配者需根据实际情况选择对应路径下的文件进行修改。

  1. 实例化GPIO控制器对象

完成驱动入口注册之后,下一步就是以核心层GpioCntlr对象的初始化为核心,包括驱动适配者自定义结构体(传递参数和数据),实例化GpioCntlr成员GpioMethod(让用户可以通过接口来调用驱动底层函数),实现HdfDriverEntry成员函数(Bind,Init,Release)。

从驱动的角度看,自定义结构体是参数和数据的载体,而且gpio_config.hcs文件中的数值会被HDF读入并通过DeviceResourceIface来初始化结构体成员,其中一些重要数值也会传递给核心层GpioCntlr对象,例如索引、管脚数等。

        //GPIO分组信息定义
        struct Pl061GpioGroup {
            struct GpioCntlr cntlr;             // 【必要】是核心层控制对象,其成员定义见下面。
            volatile unsigned char *regBase;    // 【必要】寄存器基地址。
            unsigned int index;
            unsigned int irq;
            OsalIRQHandle irqFunc;
            OsalSpinlock lock;
            uint32_t irqSave;
            bool irqShare;
            struct PlatformDumper *dumper;
            char *dumperName;
        };

        struct Pl061GpioData {
            volatile unsigned char *regBase;    // 【必要】寄存器基地址。
            uint32_t phyBase;                   // 【必要】物理基址。
            uint32_t regStep;                   // 【必要】寄存器偏移步进。
            uint32_t irqStart;                  // 【必要】中断开启。
            uint16_t groupNum;                  // 【必要】用于描述厂商的GPIO端口号的参数。
            uint16_t bitNum;                    // 【必要】用于描述厂商的GPIO端口号的参数。
            uint8_t irqShare;                   // 【必要】共享中断。
            struct Pl061GpioGroup *groups;      // 【可选】根据厂商需要设置。
            struct GpioInfo *gpioInfo;
            void *priv;
        };

        struct GpioInfo {
            struct GpioCntlr *cntlr;
            char name[GPIO_NAME_LEN];
            OsalSpinlock spin;
            uint32_t irqSave;
            struct GpioIrqRecord *irqRecord;
        };
        // GpioCntlr是核心层控制器结构体,其中的成员在Init函数中会被赋值。
        struct GpioCntlr {
            struct PlatformDevice device;
            struct GpioMethod *ops;
            uint16_t start;
            uint16_t count;
            struct GpioInfo *ginfos;
            bool isAutoAlloced;
            void *priv;
        };
        c
 class="hljs-button signin active" data-title="登录复制" data-report-click="{"spm":"1001.2101.3001.4334"}"> class="hide-preCode-box">
        //GpioMethod结构体成员都是钩子函数,驱动适配者需要根据表1完成相应的函数功能。
        static struct GpioMethod g_method = {
            .request = NULL,
            .release = NULL,
            .write = Pl061GpioWrite,              // 写管脚
            .read = Pl061GpioRead,                // 读管脚
            .setDir = Pl061GpioSetDir,            // 设置管脚方向
            .getDir = Pl061GpioGetDir,            // 获取管脚方向
            .toIrq = NULL,                        
            .setIrq = Pl061GpioSetIrq,            // 设置管脚中断,如不具备此能力可忽略
            .unsetIrq = Pl061GpioUnsetIrq,        // 取消管脚中断设置,如不具备此能力可忽略
            .enableIrq = Pl061GpioEnableIrq,      // 使能管脚中断,如不具备此能力可忽略
            .disableIrq = Pl061GpioDisableIrq,    // 禁止管脚中断,如不具备此能力可忽略
        };
        c
 class="hljs-button signin active" data-title="登录复制" data-report-click="{"spm":"1001.2101.3001.4334"}">

入参:

HdfDeviceObject:HDF框架给每一个驱动创建的设备对象,用来保存设备相关的私有数据和服务接口。

返回值:

HDF_STATUS相关状态(表3为部分展示,如需使用其他状态,可参考//drivers/hdf_core/interfaces/inner_api/utils/hdf_base.h中HDF_STATUS定义)。

表 3 HDF_STATUS相关状态说明

class="table-box">
状态(值)问题描述
HDF_ERR_INVALID_OBJECT控制器对象非法
HDF_ERR_MALLOC_FAIL内存分配失败
HDF_ERR_INVALID_PARAM参数非法
HDF_ERR_IOI/O 错误
HDF_SUCCESS初始化成功
HDF_FAILURE初始化失败

函数说明:

初始化自定义结构体对象,初始化GpioCntlr成员,调用核心层GpioCntlrAdd函数,接入VFS(可选)。

        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++) {
                // 相关信息初始化
                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); // 向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;
                }
                ret = GpioDumperCreate(&pl061->groups[i]);
                if (ret != HDF_SUCCESS) {
                    HDF_LOGE("%s: create dumper failed:%d", __func__, ret);
                    return ret;
                }
            }
            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)
        {
            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);                                 // 利用从gpio_config.HCS文件读取的属性值来初始化自定义结构体对象成员
            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, pl0  61->bitNum);
                return HDF_ERR_INVALID_PARAM;                                                
            }                                                                                

            pl061->regBase = OsalIoRemap(pl061->phyBase, pl061->groupNum * pl061->regStep);  // 地址映射
            if (pl061->regBase == NULL) {
                HDF_LOGE("%s: err remap phy:0x%x", __func__, pl061->phyBase);
                return HDF_ERR_IO;
            }

            ret = Pl061GpioInitGroups(pl061);                                                // group信息初始化,并添加到HDF核心层
            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;
        }
        c
 class="hljs-button signin active" data-title="登录复制" data-report-click="{"spm":"1001.2101.3001.4334"}"> class="hide-preCode-box">

入参:

HdfDeviceObject:HDF框架给每一个驱动创建的设备对象,用来保存设备相关的私有数据和服务接口。

返回值:

无。

函数说明:

释放内存和删除控制器,该函数需要在驱动入口结构体中赋值给Release接口,当HDF框架调用Init函数初始化驱动失败时,可以调用Release释放驱动资源。

说明:
所有强制转换获取相应对象的操作前提是在Init函数中具备对应赋值的操作。

        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);        // 从HDF核心层删除
            }

            OsalMemFree(pl061->groups);
            pl061->groups = NULL;
        }

        static void Pl061GpioRelease(struct HdfDeviceObject *device)
        {
            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;
        }
        c
 class="hljs-button signin active" data-title="登录复制" data-report-click="{"spm":"1001.2101.3001.4334"}"> class="hide-preCode-box">
  1. 驱动调试

【可选】针对新增驱动程序,建议验证驱动基本功能,例如GPIO控制状态,中断响应情况等。

如果大家想更加深入的学习 OpenHarmony(鸿蒙南向) 开发的全栈内容,不妨可以参考以下相关学习文档进行学习,助你快速提升自己:

OpenHarmony 开发环境搭建:https://qr18.cn/CgxrRy

《OpenHarmony源码解析》:https://qr18.cn/CgxrRy

系统架构分析:https://qr18.cn/CgxrRy

OpenHarmony 设备开发学习手册:https://qr18.cn/CgxrRy

在这里插入图片描述

OpenHarmony面试题(内含参考答案):https://qr18.cn/CgxrRy

写在最后

data-report-view="{"mod":"1585297308_001","spm":"1001.2101.3001.6548","dest":"https://blog.csdn.net/maniuT/article/details/140994256","extend1":"pc","ab":"new"}">> id="blogExtensionBox" style="width:400px;margin:auto;margin-top:12px" class="blog-extension-box"> class="blog_extension blog_extension_type2" id="blog_extension"> class="extension_official" data-report-click="{"spm":"1001.2101.3001.6471"}" data-report-view="{"spm":"1001.2101.3001.6471"}"> class="blog_extension_card_left"> class="blog_extension_card_cont"> 鸿蒙开发学习资料领取!!! class="blog_extension_card_cont_r"> 微信名片
注:本文转载自blog.csdn.net的沧海一笑-dj的文章"https://blog.csdn.net/dengjin20104042056/article/details/99305483"。版权归原作者所有,此博客不拥有其著作权,亦不承担相应法律责任。如有侵权,请联系我们删除。
复制链接

评论记录:

未查询到任何数据!