精华内容
下载资源
问答
  • 鸿蒙驱动框架

    千次阅读 2020-12-21 08:18:45
    鸿蒙驱动是基于HDF(Harmony Driver Foundation)驱动框架,为开发者提供了一系列统一接口供其调用,包括驱动加载、驱动服务管理和驱动消息机制。我们要做的,是学习如何使用这些接口,并基于这些接口的使用实现某些...

    本文首发于:LHM’s notes 欢迎关注我的新博客

    鸿蒙驱动是基于HDF(Harmony Driver Foundation)驱动框架,为开发者提供了一系列统一接口供其调用,包括驱动加载、驱动服务管理和驱动消息机制。我们要做的,是学习如何使用这些接口,并基于这些接口的使用实现某些业务功能。

    设备驱动概述

    相信每个人都有给电脑安装驱动的经历,驱动的使用就是去某个官网去下载个软件包,然后一路点击安装就行了。这里可以明确一个定义:驱动是一段程序代码。那么设备呢? 鼠标、键盘、显示器、这些都叫做设备,但是和我们这个里面的驱动设备里面的设备不一样,设备也是一段代码,而这段代码可以描述这个设备的各种信息,比如设备版本号、mac地址等等各种关于这个设备的信息。而驱动的作用是让这个设备能够正常运行起来,因此从模型的意义上来说,设备和驱动是两个模块,一个驱动可以对应多个设备,比如你电脑上插入两个鼠标,那么分别是鼠标设备1和鼠标设备2,但是他们的驱动是同一个,这也就是每个驱动只用安装一次的道理。

    驱动和设备如何绑定在一起,在linux内核的源码中,是通过bind将一个设备和某个驱动 进行捆绑。同时考虑到驱动的丰富多样性,A电脑只需要一个打印机的驱动,不需要显示器驱动;而B电脑可能只需要显示器的驱动。各个用户对驱动的需求不一致,如果都放到内核里面,会导致内核的代码越来越大,且有大量冗余,因此驱动的加载自己可配置。在Windows上,除了常见功能的驱动是自带之外,其他设备的驱动一般都要自己安装。

    驱动加载

    在linux上,驱动的自我加载就是在内核上有选择的插入某个驱动模块(.ko文件),而鸿蒙也是基于这个原理。HDF驱动加载包括按需加载和按序加载。

    HDF驱动加载包括按需加载和按序加载。

    1、按需加载

    ​ HDF框架支持驱动在系统启动过程中默认加载,或者在系统启动之后动态加载。

    2、按序加载

    ​ HDF框架支持驱动在系统启动过程中按照驱动的优先级进行加载

    需要注意的是:我们在此之后所提到的所有驱动,其实是包含设备和驱动两部分,设备和驱动的代码实现整体称之为驱动加载

    驱动服务管理

    HDF框架可以集中管理驱动服务,使用者可以通过HDF框架对外提供的能力接口获取驱动相关的服务。

    当前服务有三种:

    1、服务绑定
    驱动服务结构的定义
    struct ISampleDriverService {
        struct IDeviceIoService ioService;   // 服务结构的首个成员必须是IDeviceIoService类型的成员
        int32_t (*ServiceA)(void);               // 驱动的第一个服务接口
        int32_t (*ServiceB)(uint32_t inputCode); // 驱动的第二个服务接口,有多个可以依次往下累加
    };
    
    int32_t SampleDriverDispatch(struct HdfDeviceObject *device, int cmdCode, struct HdfSBuf *data, struct HdfSBuf *reply)
    {
        HDF_LOGE("sample driver lite A dispatch");
        return 0;
    }
    
    int32_t SampleDriverServiceA(void)
    {
        // 驱动开发者实现业务逻辑
        return 0;
    }
    
    int32_t SampleDriverServiceB(uint32_t inputCode)
    {
        // 驱动开发者实现业务逻辑
        return 0;
    }
    

    ServiceA 和ServiceB都是开发者可以随意修改定制的类型,而第一个ioService的类型是固定的,这个在驱动消息机制小节中会讲到为啥是固定的。

    由开发者实现的驱动服务接口都会注册到ISampleDriverService 结构体中,如下第9、10行; 接着将ISampleDriverService结构体注册到deviceObject中; 这样,内核就可以通过deviceObject调用开发者实现的服务。

    int32_t SampleDriverBind(struct HdfDeviceObject *deviceObject)
    {
        // deviceObject为HDF框架给每一个驱动创建的设备对象,用来保存设备相关的私有数据和服务接口
        if (deviceObject== NULL) {
            HDF_LOGE("Sample device object is null!");
            return -1;
        }
        static struct ISampleDriverService sampleDriverA = {
            .ioService.Dispatch = SampleDriverDispatch,
            .ServiceA = SampleDriverServiceA,
            .ServiceB = SampleDriverServiceB,
            
        };
        deviceObject->service = &sampleDriverA.ioService;
        return 0;
    }
    
    2、服务获取

    当明确驱动已经加载完成时,获取该驱动的服务可以通过HDF框架提供的能力接口直接获取,如下所示:

    const struct ISampleDriverService *sampleService =
            (const struct ISampleDriverService *)DevSvcManagerClntGetService("sample_driver");
    if (sampleService == NULL) {
        return -1;
    }
    sampleService->ServiceA();
    sampleService->ServiceB(5);
    
    3、服务订阅

    服务订阅也是服务获取的一种,只是是通过订阅的方式进行实现,因为上一小节服务获取的前提条件是已经明确驱动已经加载完成了。但是这个条件不一定已经实现,通过服务订阅的方式,当被订阅的驱动加载完成后,系统会自己调用callback函数从而可以调用开发者实现的服务函数。

    // 订阅回调函数的编写,当被订阅的驱动加载完成后,HDF框架会将被订阅驱动的服务发布给订阅者,通过这个回调函数给订阅者使用
    // object为订阅者的私有数据,service为被订阅的服务对象
    int32_t TestDriverSubCallBack(struct HdfDeviceObject *deviceObject, const struct HdfObject *service)
    {
        const struct ISampleDriverService *sampleService =
            (const struct ISampleDriverService *)service;
        if (sampleService == NULL) {
            return -1;
        }
        sampleService->ServiceA();
        sampleService->ServiceB(5);
    }
    // 订阅过程的实现
    int32_t TestDriverInit(struct HdfDeviceObject *deviceObject)
    {
        if (deviceObject== NULL) {
            HDF_LOGE("Test driver init failed, deviceObject is null!");
            return -1;
        }
        struct SubscriberCallback callBack;
        callBack.deviceObject = deviceObject;
        callBack.OnServiceConnected = TestDriverSubCallBack;
        int32_t ret = HdfDeviceSubscribeService(deviceObject, "sample_driver", callBack);
        if (ret != 0) {
            HDF_LOGE("Test driver subscribe sample driver failed!");
        }
        return ret;
    }
    

    驱动消息机制

    HDF框架提供统一的驱动消息机制,支持用户态应用向内核态驱动发送消息,也支持内核态驱动向用户态应用发送消息。在linux内核中,常用的指令ioctl用来用户态传递指令到内核态, 但是ioctl不能主动将内核态消息传递到用户态。内核态与用户态可以相互传递消息可以使用netlink机制。

    鸿蒙系统中也有固定的一套接口,在服务绑定小节中讲到第一个服务的函数指针类型是固定的,如下

    int32_t SampleDriverDispatch(struct HdfDeviceObject *device, int cmdCode, struct HdfSBuf *data, struct HdfSBuf *reply);

    这个固定类型的服务其实是一个基本的通信接口,类似于linux中的ioctl,有比较固定的使用方法,第一个参数是设备对象,第二个是读写指令,第三个是发送数据地址,第四个参数是回复消息地址。

    个人感觉这就是个模板,供开发者去参考,当然开发者也可以定义像ServiceA、ServiceB这种没有参数或者一个参数的服务,但是远没有这种模板的功能齐全。

    用户态通过服务获取或者服务订阅定制机制获取到该服务集,接着调用里面的dispatcher触发第一个服务。

     int ret = serv->dispatcher->Dispatch(&serv->object, SAMPLE_WRITE_READ, data, reply);
        if (ret != HDF_SUCCESS) {
    

    用户态给内核态写数据时:将第二个参数置为 WRITE; 将要发送的数据指针传到第三个参数(data);再调用上述接口即可进行消息下发操作。

    用户态获取内核态数据时:将第二个参数置为READ; Dispatch执行成功后,读取reply中的数据即可。

    WRITE和READ指令可以同时使用。

    但是这还没有解决一个问题,就是内核态主动给用户态发送消息。这个linux的ioctl机制也不支持,鸿蒙采用的解决方式是用户态采用监听的方式,如果内核态调用了HdfDeviceSendEvent(deviceObject, cmdCode, data);用户态可以感知到,接着通过用户态注册的回调函数来处理内核态发送过来的消息。具体操作如下:

    1. 用户态编写驱动上报消息的处理函数。

      static int OnDevEventReceived(void *priv,  uint32_t id, struct HdfSBuf *data)
      {
          OsalTimespec time;
          OsalGetTime(&time);
          HDF_LOGE("%s received event at %llu.%llu", (char *)priv, time.sec, time.usec);
      
          const char *string = HdfSbufReadString(data);
          if (string == NULL) {
              HDF_LOGE("fail to read string in event data");
              return -1;
          }
          HDF_LOGE("%s: dev event received: %d %s",  (char *)priv, id, string);
          return 0;
      }
      
    2. 用户态注册接收驱动上报消息的操作方法。

      int RegisterListen()
      {
          struct HdfIoService *serv = HdfIoServiceBind("sample_driver", 0);
          if (serv == NULL) {
              HDF_LOGE("fail to get service");
              return -1;
          }
          static struct HdfDevEventlistener listener = {
              .callBack = OnDevEventReceived,
              .priv ="Service0"
          };
          if (HdfDeviceRegisterEventListener(serv, &listener) != 0) {
              HDF_LOGE("fail to register event listener");
              return -1;
          }
          ......
          HdfDeviceUnregisterEventListener(serv, &listener);
          HdfIoServiceRecycle(serv);
          return 0;
      }
      
    3. 内核态驱动上报事件。

      int32_t SampleDriverDispatch(struct HdfDeviceObject *device, int cmdCode, struct HdfSBuf *data, struct HdfSBuf *reply)
      {
          ... // process api call here
          return HdfDeviceSendEvent(deviceObject, cmdCode, data);
      }
      

    总结

    可以看到,鸿蒙的这几个接口基于一套service机制实现了用户态和内核态的之间的相互通信,这就是驱动的大致框架了,开发者只需要用户态写写业务逻辑,通过上述框架将指令传递到内核,然后内核根据下发的指令完成相应的功能,这样一个驱动的功能就可以完整实现了。

    展开全文
  • HDF(HarmonyOSDriver Foundation)驱动框架,为驱动开发者提供驱动框架能力,包括驱动加载、驱动服务管理和驱动消息机制。旨在构建统一的驱动架构平台,为驱动开发者提供更精准、更高效的开发环境,力求做到一次...

    一、简介

    HDF(HarmonyOS Driver Foundation)驱动框架,为驱动开发者提供驱动框架能力,包括驱动加载、驱动服务管理和驱动消息机制。旨在构建统一的驱动架构平台,为驱动开发者提供更精准、更高效的开发环境,力求做到一次开发,多系统部署。

     

    驱动加载:

    HDF驱动加载包括按需加载和按序加载:

    1.驱动按需加载

    HDF框架支持驱动在系统启动过程中默认加载,或者在系统启动之后动态加载。

    2.驱动按序加载

    HDF框架支持驱动在系统启动的过程中按照驱动的优先级进行加载。

     

    驱动服务管理:

    HDF框架可以集中管理驱动服务,开发者可直接通过HDF框架对外提供的能力接口获取驱动相关的服务。

     

    驱动消息机制:

    HDF框架提供统一的驱动消息机制,支持用户态应用向内核态驱动发送消息,也支持内核态驱动向用户态应用发送消息。

     

    二、驱动模型

    HDF框架以组件化的驱动模型作为核心设计思路,为开发者提供更精细化的驱动管理,让驱动开发和部署更加规范。HDF框架将一类设备驱动放在同一个host里面,开发者也可以将驱动功能分层独立开发和部署,支持一个驱动多个node,HDF框架管理驱动模型如下图所示:

    三、驱动开发实例

    接下来几个课程中,我们将手把手带大家进行鸿蒙OS驱动开发,具体内容涉及:

    1. 驱动添加
    2. 驱动服务管理
    3. 驱动消息机制

    此外这个过程中还会设计到HCS(HDF驱动框架配置描述)等内容。

     

    本文档接下来的内容会先给大家讲解如何添加驱动。

    四、鸿蒙OS驱动添加

     

    A)在源码路径:/home/test/hmos2/vendor/huawei/hdf下创建一个test文件夹,用来保存我们新添的驱动文件,如下:

    B)在test文件夹下新建一个sample_driver.c的驱动文件,并编写我们的驱动代码,如下:

    Ps:源码具体内容在后文附上!

     

    D)此时驱动文件默认是不参与编译的,接下来我们需要做一下驱动文件编译必要的配置工作:

    1. 添加驱动配置信息

    驱动配置分为两个部分——HDF框架定义的驱动设备描述和驱动的私有配置信息

     

    驱动设备描述:

    HDF框架加载驱动所需要的信息来源于HDF框架定义的驱动设备描述,因此该项是驱动添加的必选项,我

    们需要在HDF框架定义的配置文件中添加对应的设备描述,打开源码路径:

    vendor/hisi/hi35xx/hi3518ev300/config/device_info/

    下的device_info/device_info.hcs文件,新添我们的设备驱动描述信息。

    此处设备描述信息添加有两种方式,第一种是新建一个sample_host节点:

    sample_host :: host{

    hostName = "host0"; // host名称,host节点是用来存放某一类驱动的容器

    priority = 100; // host启动优先级(0-200),值越大优先级越低,建议默认配100,优先级相同则不保证host的加载顺序

    device_sample :: device { // sample设备节点

    device0 :: deviceNode { // sample驱动的DeviceNode节点

    policy = 2; // policy字段是驱动服务发布的策略

    priority= 100; // 驱动启动优先级(0-200),值越大优先级越低,建议默认配100,优先级相同则不保证device的加载顺序

    preload = 0; // 驱动按需加载字段

    permission = 0664; // 驱动创建设备节点权限

    moduleName = "sample_driver"; // 驱动名称,该字段的值必须和驱动入口结构的moduleName值一致

    serviceName = "sample_service"; // 驱动对外发布服务的名称,必须唯一

    deviceMatchAttr = "sample_config"; // 驱动私有数据匹配的关键字,必须和驱动私有数据配置表中的match_attr值相等

    }

    }

    }

     

    添加的结果如下图所示:

    另一种方式在原有源码配置plaform节点中添加(此时添加认为新增的驱动跟platform中的设备是同类设备):

                device_sample :: device {

                    device0 :: deviceNode {

                        policy = 2;

                        priority = 0;

                        permission = 0644;

                        moduleName = "sample_driver";

                        serviceName = "sample_service";

                        deviceMatchAttr = "sample_driver";

                    }

                }

    添加的结果如下图所示:

    以上内容中驱动服务发布策略policy为2表示驱动对内核态和用户态都发布服务。

    priority(取值范围为整数0到200)是用来表示host和驱动的优先级,即驱动按序加载,不同的host内的驱动,host的priority值越小,驱动加载优先级越高;同一个host内驱动的priority值越小,加载优先级越高。

    preload 字段取值为如下的枚举量:

    typedef enum {

           DEVICE_PRELOAD_ENABLE = 0,

         DEVICE_PRELOAD_DISABLE,

          DEVICE_PRELOAD_INVALID

    } DevicePreload;

    配成0(DEVICE_PRELOAD_ENABLE ),则系统启动过程中默认加载;配成1(DEVICE_PRELOAD_DISABLE),则系统启动过程中默认不加载,支持后续动态加载,当用户态获取驱动服务时,如果驱动服务不存在时,HDF框架会尝试动态加载该驱动。

     

     

     

     

    驱动私有配置信息:

    如果驱动有私有配置,则可以添加一个驱动的配置文件,用来填写一些驱动的默认配置信息,HDF框架在加载驱动的时候,该配置非必须项。

    在源码路径vendor/hisi/hi35xx/hi3518ev300/config/下新建一个test文件夹,用于存放我们驱动的私有配置文件,新增一个sample_config.hcs,用来配置驱动私有信息:

    root{

            sample_version = 1;

            author = "fengke team";

            module = "sample_driver";

            serviceName = "sample_service";

            match_attr = "sample_config"; //该字段的值必须和device_info.hcs中的deviceMatchAttr值一致,见上文配置

    }

    将该私有配置文件添加到板级配置入口文件hdf.hcs:

    驱动编译配置项

    驱动代码的编译必须要使用HDF框架提供的Makefile模板进行编译,具体可参考驱动文件夹test同级目录下的sample文件中的示例。

    在test目录下新增Kconfig文件,如下:

    config LOSCFG_DRIVERS_HDF_PLATFORM_SAMPLE

         bool "Enable HDF platform sample driver"

         default n

      depends on LOSCFG_DRIVERS_HDF_PLATFORM

          help

           Answer Y to enable HDF platform sample driver.

    在test目录下新增Makefile文件如下:

    include $(LITEOSTOPDIR)/config.mk

    include $(LITEOSTOPDIR)/../../drivers/hdf/lite/lite.mk

    MODULE_NAME := sample_driver

    LOCAL_CFLAGS += $(HDF_INCLUDE)

    LOCAL_SRCS += sample_driver.c

    LOCAL_HCS_SRCS += test/sample_config.hcs

    LOCAL_CFLAGS += -fstack-protector-strong

    Include  $(HDF_DRIVER)

    将上述Kconfig文件添加到test文件夹同级目录下的Kconfig文件中:

    修改test文件夹同级目录下的hdf_vendor.mk文件:

    Ps: LITEOS_BASELIB += -lsample_driver

    LIB_SUBDIRS    += $(VENDOR_HDF_DRIVERS_ROOT)/test

     

     

    到此我们sample_driver 驱动就已经添加到源码的配置中了,接下来只需编译即可。

    下一节课我们将在此基础上,对驱动相关的内容做更深入的探究。

    谢谢!

    本课程驱动源码如下:

     

    #include "hdf_device_desc.h"  // HDF框架对驱动开放相关能力接口的头文件

    #include "hdf_log.h"          // HDF 框架提供的日志接口头文件

     

    #define HDF_LOG_TAG sample_driver   // 打印日志所包含的标签,如果不定义则用默认定义的HDF_TAG标签

     

    //驱动服务结构的定义

    struct ISampleDriverService {

        struct IDeviceIoService ioService;       // 服务结构的首个成员必须是IDeviceIoService类型的成员

        //以下可添加自定义的驱动的服务接口

    };

    //驱动对外提供的服务能力,将相关的服务接口绑定到HDF框架

    int32_t HdfSampleDriverBind(struct HdfDeviceObject *deviceObject) {

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

        if (deviceObject== NULL) {

            HDF_LOGE("Sample device object is null!");

            return -1;

        }

        static struct ISampleDriverService mysampleDriver;

        deviceObject->service = &mysampleDriver.ioService;

        return 0;

    }

    // 驱动自身业务初始的接口

    int32_t HdfSampleDriverInit(struct HdfDeviceObject *deviceObject) {

        return 0;

    }

    // 驱动资源释放的接口

    void HdfSampleDriverRelease(struct HdfDeviceObject *deviceObject) {

        return;

    }

    // 定义驱动入口的对象,必须为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);

     

     

    展开全文
  • 关注+星标公众号,不错过精彩内容转自 |漫谈嵌入式hello 大家好,今天带领大家学习一下USB 设备端驱动内核版本:4.4.941. Linux USB 子系统 在介绍设备端驱动前,...

    关注+星标公众,不错过精彩内容

    转自 | 漫谈嵌入式

    hello 大家好,今天带领大家学习一下USB 设备端驱动 

    内核版本:4.4.94

    1. Linux USB 子系统

    在介绍设备端驱动前,我们先来看看 Linux USB子系统。这里的子系统是相对于整个Linux kernel 来说的,而非单一设备。从整体概括了USB主机端和设备端的通信框架。

    Linux kernel 中早已集成了较为完善的USB协议栈,由于其规模庞大,包含多个类别的设备驱动,所以Linux系统中的USB协议栈也被称为USB子系统。

    1.1 主机端

    主机端,简化抽象三层:

    • 各种类设备驱动:mass sotrage, CDC, HID等

    • USB 设备驱动:USB 核心处理

    • 主机控制器驱动:不同的USB主机控制器(OHCI/EHCI/UHCI),抽象为HDC。

    1.2 设备端

    设备端,也抽象为三层:

    • 设备功能驱动:mass sotage , CDC, HID 等,对应主机端的类设备驱动

    • Gadget 设备驱动:中间层,向下直接和UDC通信,建立链接;向上提供通用接口,屏蔽USB请求以及传输细节。

    • 设备控制器驱动:UDC驱动,直接处理USB设备控制器。

    2. USB 设备驱动

    2.1 gadget 驱动框架拆解1

    我们将USB 设备端驱动拆解一下,其驱动框架如下:

    上文提到,Gadget 设备层起着至关重要的作用。为上层提供通用的驱动框架,与下层UDC通过Gadget Interface 建立联系。

    其中Compsite Framwork 提供了一个通用的usb_gadget_driver 模板,包括各种方法供上层Function driver 使用。(driver/usb/gadget/compsite.c)

    从上图我们可以看出,对于USB设备端驱动开发而言,更多的关注的是Function driver这层。USB 控制相关过程,内核提供了一个中间层帮我们屏蔽掉了。

    2.2 gadget 驱动框架拆解2

    内核版本:Linux Kernel 4.4.94,我们以这个版本进行拆解分析

    4.x 的内核相对于3.x的内核在gadget 驱动上分解的更加完善,显得目录结构,层次分明,分工合理,更便于理解。

    相对于3.x 的版本,4.4.94这个内核,将原来的、driver/usb/gadget目录进行拆分。通用接口保持不变,比如compsite.c以及functions.c。将usb function driver 进行细分,分为legacy和functions。

    有了这些背景,我们再看4.4.94这版内核,gadget驱动框架,如图所示(下图笔者按自己的理解绘制的):

    • legacy:整个Gadget 设备驱动的入口。位于driver/usb/gadget/legacy下,里面给出了常用的usb类设备的驱动sample。其作用就是配置USB设备描述符信息,提供一个usb_composite_driver, 然后注册到composite层。

    • functions:各种usb 子类设备功能驱动。位于driver/usb/gadget/functions,里面也给出了对应的sample。其作用是配置USB子类协议的接口描述以及其他子类协议,比如uvc协议,hid等。

    • 注意:对于一个compsite 设备一个有一个或者多个function,对应的也就有多个functions driver

    从这张图上,有没有发现,设备端驱动开发似乎越来越简单了。没错,事实上,我们只需要根据legacy的源码,添加对应的usb设备描述符信息,以及其他若干配置即可。

    换言之,我们只需要关心 legacy 这一丢丢就行,对于functions这层会根据业务需要略微调整,不过整体变动不大。

    usb 驱动框架之所以复杂,除了需要研究各种复杂的协议,还融合了各种驱动,对于初学者来说,理解起来有点困难。事实上,光是legacy这里也包含其他驱动,比如webcam里有大名鼎鼎的 v4l2 驱动框架。

    所以当我学习USB驱动框架的时候,一定要抓大放小,【把握主要脉络,忽略细节】。当我们把一个复杂的驱动逐一拆解的话,其实发现,就没有那么可怕了。

    2.3 usb compsite 设备构建

    为了便于理解,我们来简单了解一个usb compsite 设备的构建过程:

    假设构建一个usb 复合设备,需要支持uac, uac, hid 三个功能 其驱动框架如下:

    • 首先,我们需要一个驱动入口 legacy,用来配置设备描述信息,支持的协议等

    • 然后添加一个配置支持多种接口,这里支持uvc uac hid, 每个接口对应一个functions driver

    • 最后我们把它注册到compsite 层

    • 对于functions driver 有个usb function driver list,在内核注册function driver 时会自动添加到一个链表上。functions.c 就是用来管理所有的function drivers

    3. USB gadget 驱动剖析

    3.1 相关数据结构

    在梳理整个框架前我们先梳理一下几个重要的数据结构,从下到上依次介绍:

    usb_udc:

    udc 使用,内嵌usb_gadget_driver 和 usb_gadget

    struct usb_udc {
     struct usb_gadget_driver *driver;
     struct usb_gadget  *gadget;
     struct device   dev;
     struct list_head  list;
     bool    vbus;
    };
    

    usb gadget:

    usb 底层操作,包括udc,端点请求等。

    struct usb_gadget {
     struct work_struct  work;         /* 工作队列 */
     struct usb_udc   *udc;          /* udc */
     /* readonly to gadget driver */
     const struct usb_gadget_ops *ops; /*gadget 设备操作函数集*/
     struct usb_ep   *ep0;           /* 控制端点,只对setup包响应*/
     struct list_head  ep_list;      /* 将设备的所有端点连成链表,ep0不在其中 */
     enum usb_device_speed  speed; /* 高速、全速和低速 */
     enum usb_device_speed  max_speed; /* 最大速度 */
     enum usb_device_state  state;
     const char   *name;
     struct device   dev;
     unsigned   out_epnum;       /* out ep number */
     unsigned   in_epnum;         /* in ep number */
     struct usb_otg_caps  *otg_caps;
    
     unsigned   sg_supported:1;
     unsigned   is_otg:1;
     unsigned   is_a_peripheral:1;
     unsigned   b_hnp_enable:1;
     unsigned   a_hnp_support:1;
     unsigned   a_alt_hnp_support:1;
     unsigned   quirk_ep_out_aligned_size:1;
     unsigned   quirk_altset_not_supp:1;
     unsigned   quirk_stall_not_supp:1;
     unsigned   quirk_zlp_not_supp:1;
     unsigned   is_selfpowered:1;
     unsigned   deactivated:1;
     unsigned   connected:1;
    };
    

    usb_gadget_driver:

    usb_gadget_driver - driver for usb 'slave' devices. usb 从设备驱动通用结构。

    • 作用:提供一个通用的usb gadget driver 模板,向下注册到udc,向上给functions driver提供bind 回调等。

    • 关注:bind 回调、function 驱动名、setup 处理请求

    struct usb_gadget_driver {
     char   *function;    /* String describing the gadget's function */
     enum usb_device_speed max_speed; /* Highest speed the driver handles */
     int   (*bind)(struct usb_gadget *gadget, /* the driver's bind callback */
         struct usb_gadget_driver *driver);
     void   (*unbind)(struct usb_gadget *);
     int   (*setup)(struct usb_gadget *, /* 处理ep0 request */
         const struct usb_ctrlrequest *);
     void   (*disconnect)(struct usb_gadget *); 
     void   (*suspend)(struct usb_gadget *);
     void   (*resume)(struct usb_gadget *);
     void   (*reset)(struct usb_gadget *);
    
     /* FIXME support safe rmmod */
     struct device_driver driver;
    };
    

    usb_composite_driver:

    usb_composite_driver ,设备驱动的入口,用来管理设备配置信息,保存设备描述符。 

    重点:关注 bind 方法。

    struct usb_composite_driver {
     const char    *name; /* 驱动名字 */
     const struct usb_device_descriptor *dev ; /* 设备描述符 */
     struct usb_gadget_strings  **strings;
     enum usb_device_speed   max_speed;
     unsigned  needs_serial:1;
    
     int   (*bind)(struct usb_composite_dev *cdev); /* bind 方法 */
     int   (*unbind)(struct usb_composite_dev *);
    
     void   (*disconnect)(struct usb_composite_dev *);
    
     /* global suspend hooks */
     void   (*suspend)(struct usb_composite_dev *);
     void   (*resume)(struct usb_composite_dev *);
     struct usb_gadget_driver  gadget_driver;     /* usb gadget driver */
    };
    

    usb_composite_dev:

    内嵌gadget对象,以及usb 设备的一些配置和请求,主要用于初始化。

    struct usb_composite_dev {
     struct usb_gadget  *gadget;
     struct usb_request  *req;
     struct usb_request  *os_desc_req;
    
     struct usb_configuration *config; /* usb 配置信息 */
    
     /* OS String is a custom (yet popular) extension to the USB standard. */
     u8    qw_sign[OS_STRING_QW_SIGN_LEN];
     u8    b_vendor_code;
     struct usb_configuration *os_desc_config;
     unsigned int   use_os_string:1;
    
     /* private: */
     /* internals */
     unsigned int   suspended:1;
     struct usb_device_descriptor desc; /* 设备描述符 */
     struct list_head  configs;
     struct list_head  gstrings;
     struct usb_composite_driver *driver; /* composite driver */
     u8    next_string_id;
     char    *def_manufacturer;
    
     /* the gadget driver won't enable the data pullup
      * while the deactivation count is nonzero.
      */
     unsigned   deactivations;
    
     /* the composite driver won't complete the control transfer's
      * data/status stages till delayed_status is zero.
      */
     int    delayed_status;
    
     /* protects deactivations and delayed_status counts*/
     spinlock_t   lock;
    
     unsigned   setup_pending:1;
     unsigned   os_desc_pending:1;
    };
    

    3.2 驱动剖析

    如图为一个通用的usb gadget 驱动剖析,框图中只列出了两个function,如果有多个function可以继续添加。关于udc控制器部分,,没有继续画下去,注意我们始终保持一个原则,【抓大放小】,把握重要的脉络即可。

    有关 usb 设备端驱动,比较复杂图片包含内容比较多,如果觉得不清楚,后台回复【gadget 驱动】获取高清图

    分层分块

    上下分层,左右分离的思想。

    • 设备功能驱动

      • legacy 驱动入口

      • functions 驱动实现

    • Gadget 设备层:最重要的是compsite_bind 方法,承上启下的作用。

    • udc 设备控制器层。usb 协议的真正处理。

    驱动走向

    • 向下:usb_composite_driver -> usb_gadget_driver->usb_udc

    • 向上回调:udc_bind_to_driver -> composite_bind -> webcam_bind 其中其主要作用的两个结构就是usb_gadget_driver 和 usb_compsite_dev。前者向下注册到udc list 里面,与udc控制器建立绑定关系;后者向上提供接口,供上层配置usb 设备的各种functions 和其他配置信息。

    代码分析

    1. 注册usb_composite_driver

    module_usb_composite_driver(webcam_driver)
         module_driver(webcam_driver, usb_composite_probe, \
             usb_composite_unregister)
    
    1. usb_composite_probe

    usb_composite_probe(webcam_driver);
            driver->gadget_driver = composite_driver_template;
            gadget_driver = &driver->gadget_driver;
            ...
            usb_gadget_probe_driver(composite_driver_template);
                    udc_bind_to_driver(udc, driver);
                            composite_driver_template->bind(udc->gadget, composite_driver_template);
                            usb_gadget_udc_start(udc);
    
    1. composite_bind

    composite_bind(udc->gadget,composite_driver_template);
            cdev->gadget = gadget;
            composite_dev_prepare(webcam_driver,cdev);
                    cdev->req = usb_ep_alloc_request(gadget->ep0, GFP_KERNEL); /* 申请端点0 */
                    cdev->req->complete = composite_setup_complete;
                    cdev->driver = webcam_driver;
                    usb_ep_autoconfig_reset(gadget);
            webcam_driver->bind(cdev);
    
    1. webcam_bind

    webcam_bind(cdev);
            usb_get_function_instance("uvc");
                    try_get_usb_function_instance("uvc");
                            uvc_alloc_inst();
            usb_add_config();
                    webcam_config_bind();
                            usb_get_function();
                            usb_add_function();
                    others_config_bind();
    

    其他

    关于function driver 我们这里没有详细介绍,这个框图只是一个通用的usb 设备驱动框架图,对于具体的usb function driver 我们这里没有做具体分析。

    以f_uvc简单举例,详细过程见内核源码。

    DECLARE_USB_FUNCTION_INIT(uvc, uvc_alloc_inst, uvc_alloc);

    DECLARE_USB_FUNCTION_INIT(uvc, uvc_alloc_inst, uvc_alloc);
            usb_function_register(&uvcusb_func);
                    list_for_each_entry(fd, &func_list, list)
                    list_add_tail();
    

    DECLARE_USB_FUNCTION_INIT 

    一个通用的驱动模板,用来注册usb_function_driver,并添加到func_list上。

    #define DECLARE_USB_FUNCTION(_name, _inst_alloc, _func_alloc)  \
     static struct usb_function_driver _name ## usb_func = {  \
      .name = __stringify(_name),    \
      .mod  = THIS_MODULE,     \
      .alloc_inst = _inst_alloc,    \
      .alloc_func = _func_alloc,    \
     };        \
     MODULE_ALIAS("usbfunc:"__stringify(_name));
    
    #define DECLARE_USB_FUNCTION_INIT(_name, _inst_alloc, _func_alloc) \
     DECLARE_USB_FUNCTION(_name, _inst_alloc, _func_alloc)  \
     static int __init _name ## mod_init(void)   \
     {        \
      return usb_function_register(&_name ## usb_func); \
     }        \
     static void __exit _name ## mod_exit(void)   \
     {        \
      usb_function_unregister(&_name ## usb_func);  \
     }        \
     module_init(_name ## mod_init);     \
     module_exit(_name ## mod_exit)
    

    4. 总结

    本文以拆解的方式,逐步剥离 usb 设备端驱动框架,带领大家来重新认识usb 设备端驱动,同时给出了一个 compsite 设备的通用驱动框架模型,并从源码层次分析整个驱动流程。

    有关USB 或者 其他类似的高级驱动,笔者有个建议,在初学时一点更要【把握主次,忽略细节】。

    比如一个复合的usb 设备可能包含,uvc,uac,hid,等等,视频有uvc function驱动和v4l2驱动,uac也有相应的驱动,衍生展开会非常复杂。

    所以当我们先掌握设备端驱动框架以及流程,等后面需要加入其他usb function 驱动再去研究其协议或者驱动,以及衍生驱动。

    ------------ END ------------

    后台回复『USB』『Linux』阅读更多相关文章。

    欢迎关注我的公众号回复“加群”按规则加入技术交流群,回复“1024”查看更多内容。

    欢迎关注我的视频号:

    点击“阅读原文”查看更多分享,欢迎点分享、收藏、点赞、在看。

    展开全文
  • 简介rt-smart相关文章《rt-smart的第一个应用程序,imx6ull用户态点灯》《rt-smart用户态通过IPC通信玩转传感器数据》rt-thread驱动相关文章《rt-th...

    简介

    • rt-smart相关文章

    • rt-thread驱动相关文章

    • 对于imx6ull这个平台,目前RT-Thread是没有对应的BSP,已经相关的硬件驱动,所以通过这个平台来学习RT-Thread的驱动框架也是非常好的,而且又能玩rt-smart,两全其美。

    • 100ask_imx6ull带有7寸的屏幕,有屏幕不玩起来还是挺无聊的,所以花了点时间对接一下相关驱动,LCD驱动和touch驱动,我分成两篇。

    • 为了把屏幕跑起来,中间走了不少路,屏幕出现一闪一闪的(时钟配置错了),屏幕错位等,下面讲解的是我个人的做法,不对的地方,请指出。。。。

    • 关于RT-Smart对接屏幕,后面我会重新更新一篇文章,并对比一个rt-thread和rt-smart的不同之处。

    • 基于100ask_imx6ull的rt-thread和rt-smart仓库:

      • rt-thread的仓库:https://gitee.com/RiceChen0/imx6ull_rt_rthread

      • rt-smart的厂库:https://gitee.com/RiceChen0/imx6ull_rt_smart

    LCD驱动:

    • imx6ull这款芯片自带LCD控制器,所以比较方便。

    • 关于LCD驱动,从RTT的代码是没有对应的驱动框架的,所以直接对接设备驱动框架中。而且只需要实现通用设备接口的init和control接口。

        /* common device interface */
        rt_err_t  (*init)   (rt_device_t dev);
        rt_err_t  (*open)   (rt_device_t dev, rt_uint16_t oflag);
        rt_err_t  (*close)  (rt_device_t dev);
        rt_size_t (*read)   (rt_device_t dev, rt_off_t pos, void *buffer, rt_size_t size);
        rt_size_t (*write)  (rt_device_t dev, rt_off_t pos, const void *buffer, rt_size_t size);
        rt_err_t  (*control)(rt_device_t dev, int cmd, void *args);
    

    init接口:我主要做了以下四个步骤(详情实现请clone代码):

    1. framebuffer清0,

    2. LCD控制器的相关GPIO初始化

    3. LCD控制器的相关时钟初始化

    4. LCD控制器的相关配置初始化(这里要结合屏幕手册进行配置)

    • 时钟配置,控制配置需要参考LCD的手册。

    control接口:

    • rtt提供了如下6条命令:

    /**
     * graphic device control command
     */
    #define RTGRAPHIC_CTRL_RECT_UPDATE      0
    #define RTGRAPHIC_CTRL_POWERON          1
    #define RTGRAPHIC_CTRL_POWEROFF         2
    #define RTGRAPHIC_CTRL_GET_INFO         3
    #define RTGRAPHIC_CTRL_SET_MODE         4
    #define RTGRAPHIC_CTRL_GET_EXT          5
    
    • 因为我使用的是单个FB,所以实际只实现了3条命令:RTGRAPHIC_CTRL_POWERON、RTGRAPHIC_CTRL_POWEROFF、RTGRAPHIC_CTRL_GET_INFO

    static rt_err_t imx6ull_lcd_control(rt_device_t dev, int cmd, void *args)
    {
        struct imx6ull_lcd *lcd_dev = (struct imx6ull_lcd *)dev;
    
        switch(cmd)
        {
            case RTGRAPHIC_CTRL_RECT_UPDATE:
            {
                break;
            }
            case RTGRAPHIC_CTRL_POWERON:
            {
                rt_pin_write(IMX6ULL_LCD_BL_PIN, PIN_HIGH);
                break;
            }
            case RTGRAPHIC_CTRL_POWEROFF:
            {
                rt_pin_write(IMX6ULL_LCD_BL_PIN, PIN_LOW);
                break;
            }
            case RTGRAPHIC_CTRL_GET_INFO:
            {
                struct rt_device_graphic_info *info = (struct rt_device_graphic_info*)args;
                RT_ASSERT(info != RT_NULL);
                rt_memcpy(info, &lcd_dev->info, sizeof(lcd.info));
                break;
            }
        }
        return RT_EOK;
    }
    

    注册设备

    • 前面也说道LCD没有独立的驱动框架,所以以一个通用设备驱动框架来实现:

    int rt_hw_lcd_init(void)
    {
        rt_err_t ret;
    
        lcd.device.type    = RT_Device_Class_Graphic;
    #ifdef RT_USING_DEVICE_OPS
        lcd.device.ops         = &lcd_ops;
    #else
        lcd.device.init    = imx6ull_lcd_init;
        lcd.device.open    = RT_NULL;
        lcd.device.close   = RT_NULL;
        lcd.device.read    = RT_NULL;
        lcd.device.write   = RT_NULL;
        lcd.device.control = imx6ull_lcd_control;
    #endif
        lcd.device.user_data = (void *)&lcd.info;
    
        ret = rt_device_register(&lcd.device, "lcd", RT_DEVICE_FLAG_RDWR);
        return ret;
    }
    INIT_BOARD_EXPORT(rt_hw_lcd_init);
    

    效果(通过刷屏演示)

    • 在代码中,我提供了测试样例,命令:lcd_test

    关注微信公众号『Rice嵌入式开发技术分享』,后台回复“微信”添加作者微信,备注”入群“,便可邀请进入技术交流群。

    展开全文
  • 消息系统事件驱动框架参照一些消息系统中的模式。我们将进行如下类比。 事件与消息,事件处理器与通道,事件转发器与路由。一个实例是邮递系统。邮递员有一个背包里面有若干信件,上面有要寄送的地址,邮递员必须将...
  • 驱动服务管理驱动服务是HDF驱动设备对外提供能力的对象,由HDF框架统一管理。驱动服务管理主要包含驱动服务的发布和获取。HDF框架定了驱动对外发布服务的策略,是由配置文件中的policy字段来控制,policy字段的取值...
  • (3条消息) linux字符驱动框架_daha1314的博客-CSDN博客_linux字符驱动框架 (3条消息) Linux驱动开发16之再论register_chrdev_region_wangdapao12138的博客-CSDN博客 29.使用register_chrdev_region()系列来注
  • V4L2视频驱动框架简述 奶牛养殖场小马 2018-05-14 15:40:1013469 收藏 76 分类专栏: Linux驱动 版权 V4L2框架简述(结合高通平台) 1 硬件 1.1 硬件结构分析 1.2 电路分析 2 软件 2.1 v4l2驱动框架 ...
  • V4L2是video for Linux 2的缩写,是一套Linux内核视频设备的驱动框架,为上层的访问底层的视频设备提供了统一的接口。 V4L2支持多种类型设备:视频输入输出设备、VBI设备和radio设备等,分别会在/dev目录下产生...
  • DRM 驱动框架介绍

    2021-03-21 17:38:16
    本文主要介绍,Linux显示驱动框架DRM子系统,它和V4L2一样,也是Linux驱动中的一个子系统,主要应用于display; 其分为两部分,一个是系统core 驱动,另一个是具体的平台驱动,系统core 驱动主要负责搭建DRM框架,...
  • RT-Thread传感器驱动框架使用详解1、整体框架2、使用步骤①ENV工具配置②Sensor框架初始化③创建线程3、源码追踪 由于传感器种类众多,RT-Thread官方设计了传感器框架,为上层提供统一的操作接口,提高上层代码的可...
  • 一、前言前面介绍了 Linux内核 的 2 个驱动框架—— I2C 和 SPI ,这 2 个框架相对简单一些,直来直去,没有比较难以理解的点,层次分明。而今天我们要讲述的是我们熟悉的 串口驱动,该驱动加框也较之之前的驱动来说...
  • 1、概述SylixOS中AHCI驱动框架分析。本文档介绍SATA和AHCI相关协议,以IMX6Q实验平台为基础,分析SylixOS中AHCI驱动框架的具体实现。2、SATA简介2.1 SATA硬盘串口硬盘SATA(Serial ATA)与以往的并口硬盘PATA(Parallel...
  • 像这样的总线很多,而 Linux 系统要考虑到驱动的可重用性,因此提出了驱动的分离与分层这样的软件思路,于是 Linux从2.6起就加入了 platform 设备驱动,也叫做平台设备驱动。它是一条虚拟的总线, 并不是一个物理的...
  • 设计 phy 驱动框架的目的 大部分网络设备由向 MAC 层提供接口的多个寄存器的集合组成,MAC 层通过 PHY 与物理链路连接。 phy自身要解决与对端网络链接时链路参数协商的功能,并且提供一个寄存器接口让驱动来确定当前...
  • watchdog驱动分为以下三层: 统一driver层 watchdog_dev.c; 核心层 watchdog_core.c; 具体的设备层。 eg: s3c2410_wdt.c 从上层到下层依次为: 给应用层提供open等接口 drivers\watchdog\watchdog_dev.c ...
  • 但是,在驱动框架的开发和部署过程中,由于终端设备对硬件的计算和存储能力的需求不同、设备厂商提供的设备软硬件操作接口不同、内核提供的操作接口不同,这就使得OEM厂商部署系统的时候需要投入大量的精力来适配和...
  • Linux I2C驱动框架(超详细) 小结 I2C驱动有4个重要的东西,I2C总线、I2C驱动、I2C设备、I2C设备器 I2C总线:维护着两个链表(I2C驱动、I2C设备),管理I2C设备和I2C驱动的匹配和删除等 I2C驱动:对应的就是I2C设备...
  • 本文以软件复用及软件分层的思想为依据,提出并设计了基于Linux的USB无线网卡通用驱动框架程序. 本文通过研究不同类型USB无线网卡的驱动程序,寻找出了它们的相同点及不同点后对USB无线网卡通用驱动框架程序进行了整体...
  • SPI是非常常见一种串行通信协议,如何在Linux操作系统下使用SPI来读取对应六轴传感器的数据。 SPI驱动框架分为主机控制器驱动和设备驱动,这里面主机控制器指的是SPI控制器接口。 ...
  • 背 景Read the fucking source code! --By 鲁迅A picture is worth a thousand words. --By 高尔基说明:linux... 概述本文将分析Linux PCI子系统的框架,主要围绕Linux PCI子系统的初始化以及枚举过程分析;若...
  • linux驱动中,hwmon越来越多的被使用,利用hwmon下的节点查看内核导出的信息非常方便,也用hwmon框架做了两个设备的驱动了,该梳理下,做为知识储备了。在3.4内核中,hwmon非常简单,对外只有一个函数:struct ...
  • platform 驱动框架

    2021-05-24 20:15:14
    platform 只是为了驱动的分离与分层而提出来的一种框架,其驱动的具体实现还是需要字符设备驱动、块设备驱动或网络设备驱动。 /* 设备结构体 */ struct xxx_dev{ struct cdev cdev; /* 设备结构体其他具体...
  • 用户不必关系物理显示缓冲区的具体位置及存放方式,这些都由帧缓冲设备驱动本身来完成。对于帧缓冲设备而言,只要在显示缓冲区中与显示点对应的区域写入颜色值,对应的颜色会自动在屏幕上显示。帧缓冲为标准字符设备...
  • linux驱动:直接操作寄存器不现实,需要根据linux下框架开发,/include/linux/fs.h中有个叫file_operations的结构体,它是linux内核驱动操作函数集合,框架开发其实就是file_operations结构体成员变量的实现:open、 close...
  • 简易总结,Linux驱动开发中最最基本的框架
  • 前面一篇,我们大概了解了什么是关键字驱动框架和它的优点。这篇,我们就来介绍搭建这个关键字驱动框架的步骤的第一个步骤。我把这个过程分成12个步骤:1.自动化实现一个端对端的流程2.区分和实现动作关键字3.设置...
  • LCD设备驱动框架

    2021-08-09 15:26:25
    LCD设备驱动框架
  • linux内核input驱动框架

    2021-04-22 11:01:35

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 421,491
精华内容 168,596
关键字:

驱动框架