fuchisia 驱动框架分析

一、驱动添加教程

1.在”zircon/system/dev/board/“目录下添加板级相关目录,如orangepipc2。目录下添加bsp文件,如orangepipc2.c。为platform bus添加board driver。

具体写法示例:

ZIRCON_DRIVER_BEGIN(orangepipc2_bus, orangepipc2_bus_driver_ops, "zircon""0.1"6)

    BI_ABORT_IF(NE, BIND_PROTOCOL, ZX_PROTOCOL_PBUS),

    BI_GOTO_IF(NE, BIND_PLATFORM_DEV_VID, PDEV_VID_ALLWINNER, 0),

    BI_MATCH_IF(EQ, BIND_PLATFORM_DEV_PID, PDEV_PID_ORANGEPIPC2),

ZIRCON_DRIVER_END(orangepipc2_bus)

匹配的是platform bus协议——ZX_PROTOCOL_PBUS。

在驱动的bind函数中会初始化硬件资源,如pbus_mmio_t、pbus_irq_t、pbus_bti_t等,最后通过pbus_device_add或pbus_protocol_device_add将mmc和sysmem设备添加到platform bus下。

注:sysmem貌似为每个单板都要加的标配。是否与单板通用系统内存有关(没进一步分析)?

 

2.在驱动目录添加mmc相关目录及文件,如zircon/system/dev/block/sunxi-mmc/sunxi-mmc.c。头需要匹配platform device协议——ZX_PROTOCOL_PDEV,如下:

ZIRCON_DRIVER_BEGIN(sunxi_mmc, sunxi_mmc_driver_ops, "zircon""0.1"4)

    BI_ABORT_IF(NE, BIND_PROTOCOL, ZX_PROTOCOL_PDEV),

    BI_ABORT_IF(NE, BIND_PLATFORM_DEV_VID, PDEV_VID_ALLWINNER),

    BI_ABORT_IF(NE, BIND_PLATFORM_DEV_DID, PDEV_DID_SUNXI_MMC),

    BI_MATCH_IF(EQ, BIND_PLATFORM_DEV_PID, PDEV_PID_ORANGEPIPC2),

ZIRCON_DRIVER_END(sunxi_mmc)

驱动中要实现bind函数。

 

3.驱动bind函数中添加设备时发布相关协议,如orangepi的mmc需要发布ZX_PROTOCOL_SDMMC协议,以供上层sdmmc协议层绑定用。

 

附:

zx_driver_rec_t __zircon_driver_rec__ __EXPORT  //记录驱动ops

extern const struct zircon_driver_note __zircon_driver_note__ __EXPORT //记录驱动binding

上面两个结构在devhost.cpp中找驱动(dh_find_driver)时用,如下:

dn = static_cast<const zircon_driver_note_t*>(dlsym(dl, "__zircon_driver_note__")

dr = static_cast(dlsym(dl, "__zircon_driver_rec__"));

 

dh_find_driver

    for (auto& drv : dh_drivers){……}//从已加载的驱动中找

    构造zx_driver类型新驱动结构(new_driver)

    dlopen_vmo //从vmo中获取所有驱动dso指针

    dlsym(dl, "__zircon_driver_note__")

    dr = dlsym(dl, "__zircon_driver_rec__")

    检查driver的ops不能为空,且ops的version不能为空

    利用driver的ops及name等初始化new_driver,同时将new_driver赋值给dr->driver(驱动注册的时候这个域为NULL)

    调用driver的Init函数,初始化。

 

调用dh_find_driver的函数:fidl_CreateDevice、fidl_BindDriver,这两个函数是fuchsia_device_manager_Controller_ops_t fidl_ops的成员

二、驱动扫描、加载及绑定

1.驱动框架中涉及的devmgr概述

    devmgr负责在系统启动初始化阶段对系统所有驱动的扫描,并通过coordinator完成对驱动的添加和绑定。

    对于需要隔离的设备,coordinator会为其单独创建一个devhost来供其运行。一个隔离区域会对应一个devhost,当然devhost也可以嵌套。

    devmgr会在初始化阶段为系统默认创建4个devhost,分别为:root、sys、misc和test。其中,sys、misc和test都是root的嵌套devhost,也就是从最顶层视图来看,系统只有一个devhost就是root。

    devmgr初始化会为sys和test设备分别创建一个proxy设备来代理绑定到其下的所有设备。

    最后,devmgr会调用coordinator的BindDrivers方法,尝试将系统中所有的驱动进行绑定、初始化。

2.驱动扫描、加载及绑定流程分析

从devmgr开始往下跟:

devmgr启动参数中如果没有传“sys-device-driver”则args.sys_device_driver设置为"/boot/driver/platform-bus.so"

coordinator.InitializeCoreDevices(args.sys_device_driver)

    初始化root设备root_device_

    初始化misc_device_设备,并设置其parent为root_device_

    初始化sys_device_设备,并设置其parent为root_device_

    初始化test_device_设备,并设置其parent为root_device_

devfs_init //devfs初始化根设备,是否会创建dev目录?

devfs_publish //这里会依次发布misc、sys、test设备,是否这里会创建相应目录:/dev/misc ……?

devmgr_vfs_init(&coordinator, needs_svc_mount) //这里创建dev目录?

在fuchsia_starter中会调用coordinator->ScanSystemDrivers()  //这一步是启动线程来运行,应该在主程序之后。

    新启动一个线程(callback)来扫描"/system/driver"下的驱动

        find_loadable_drivers //从指定目录寻找有效驱动

        又启动一个线程来调用BindSystemDrivers

            BindDriver

                1.判断如果是root、misc或test驱动则调用AttemptBind添加到相应设备(root_device_、misc_device_、test_device_)

                2.如果不属于上面的设备则从coordinator现存的设备devices_中查找是否可以匹配dc_is_bindable,然后调用AttemptBind绑定

 

==对上面的1.如果没有设置DEV_CTX_MUST_ISOLATE标志则直接调用dh_bind_driver,否则需要新创一个devhost来连接到一个proxy设备。

==这里先看需要proxy的情况。

    PrepareProxy

        dc_create_proxy(Coordinator* coordinator, Device* parent)//利用coordinator构造Device设备作为parent的proxy设备

        NewDevhost //launch一个devhost并赋值给dev->proxy->host

        dh_create_device //向devhost发送message,请求创建设备

            dh_send_create_device

                AsyncLoopOwnedRpcHandler::HandleRpcEntry //待确认fidl到devhost入口

                    DevcoordinatorConnection::HandleRpc

                        dh_handle_rpc_read

                            fidl_ops.fidl_CreateDevice

                                dh_find_driver

                                drv->CreateOp //named driver -- ask it to create the device

        dh_send_connect_proxy流程同上,最终调用到devhost的fidl_ConnectProxy

            …… fidl_ConnectProxy

                ProxyIostate::Create //将传入的channel挂在消息循环

        devfs_connect //proxy连接待分析

    dh_bind_driver

        dh_send_bind_driver IPC通信到devhost的fidl_BindDriver

            …… fidl_BindDriver

                dh_find_driver

                drv->BindOp

                    ops_->bind //调用驱动bind函数

     

coordinator.set_loader_service

从args.driver_search_paths(默认应该是“/boot/driver”)中寻找有效驱动

从args.load_drivers中加载驱动

coordinator.PrepareProxy(&coordinator.sys_device()); //为sys_device_设置proxy

coordinator.PrepareProxy(&coordinator.test_device()); //为test_device_设置proxy

coordinator.BindDrivers //将前面设置到drivers_中的驱动进行绑定

 

三、platform框架分析

 

整体框架图如下:

fuchisia 驱动框架分析_第1张图片

1.platform bus根设备创建

    在devmgr中args.sys_device_driver设置为"/boot/driver/platform-bus.so",随后调用coordinator.InitializeCoreDevices(args.sys_device_driver)对系统4个主要devhost进行初始化。此处我们重点关注sys_device_,它的libname设置为"/boot/driver/platform-bus.so",并将args设置为了“sys”(这就是我们的devhost名)。

    最后,devmgr会调用coordinator.PrepareProxy(&coordinator.sys_device())。我们再一次来跟踪PrepareProxy(忽略部分已知步骤)

PrepareProxy(&sys_device_)

    dc_create_proxy //创建proxy设备

    h1 = std::move(bootdata_vmo_); //由于是sys_device_设备,这里会把bootdata的vmo传给platform bus

    NewDevhost //为proxy设备创建devhost

    dh_create_device(dev->proxy, dev->proxy->host, arg1, std::move(h1))

        zx::channel::create(0, &hrpc, &hrpc_remote) //创建一对channel,一端给proxy,另一端传给devhost

        LibnameToVmo //通过"/boot/driver/platform-bus.so",获取其vmo

        dh_send_create_device //发送IPC到devhost

            fidl_CreateDevice

                dh_find_driver //根据vmo构造zx_driver_t drv

                drv->has_create_op() //对于一般的设备驱动只提供bind方法(此下级流程没有),而对于总线设备会提供create方法

                    zx_device::Create(&parent) //创建一个dummy parrent

                    CreationContext creation_context //构造create上下文

                    drv->CreateOp //调用platform bus create方法

                        platform_bus_create

                            PlatformBus::Create

                                device_add //驱动函数最后会调用device_add,见后面单独分析

                                bus(new (&ac) platform_bus::PlatformBus(parent)) //构造platform对象

                                bus->Init(std::move(zbi)) //传入zbi,此init函数会解析zbi

                                    ReadZbi //这个函数较长,但我们最关心的是它从zbi中解析出boot-shim中设置的单板vid、pid和board_name,后面platform驱动会匹配这些参数。

                                    zx::iommu::create //创建iommu的handle

                                    GetZbiMetadata

                                    DdkAdd("platform", DEVICE_ADD_INVISIBLE, props, fbl::count_of(props)) //后面单独来分析这个函数。这里会添加platform根设备供挂载(协议为ZX_PROTOCOL_PBUS)

                                    DdkMakeVisible //这里才让设备节点可见

                                 

                DevcoordinatorConnection::BeginWait //devhost会在传入的hrpc_remote通道上等待消息

             

        xxx//proxy设备在hrpc上等待消息

        dh->devices().push_back(dev) //devhost将proxy设备加入其管理的设备链表devices_

2.platform bus板级驱动挂接

    以orangepipc2添加mmc驱动为例。

    如第一章节中介绍的,在orangepipc2.c中添加board driver匹配ZX_PROTOCOL_PBUS协议。这样就可以挂接到platform bus([platform])下。在驱动扫描绑定的最后会调用驱动的bind函数(细节参考前面章节),bind函数往后的关键流程分析如下:

orangepipc2_bus_bind

    device_get_protocol //获取ZX_PROTOCOL_PBUS和ZX_PROTOCOL_IOMMU协议ops

    iommu_get_bti //见DDK接口分析

    device_add //添加orangepipc2设备(不可绑定子节点)

    orangepipc2_start_thread

        orangepipc2_mmc_init

            pbus_device_add //调用DDK接口,创建platform device,将mmc的mmio、irq等硬件资源保存起来,发布ZX_PROTOCOL_PDEV设备。

        orangepipc2_sysmem_init

            pbus_protocol_device_add ////调用DDK接口,创建platform device,保存bti,发布ZX_PROTOCOL_PDEV设备。

 

3.设备驱动挂接(orangepi mmc为例)

仍然以devmgr中对驱动的绑定开始。这里,我们从BindDriver开始:

BindDriver

    dc_is_bindable //从现有devices_中匹配,前面第2步发布的platform device设备将是挂载点

    AttemptBind //这里会单独创建devhost来运行

        PrepareProxy

            dc_create_proxy //默认的设备都是non-immortal的只有root、sys、misc和test四个顶级设备是immortal(永生),所以这里proxy的libname会被替换为platform-bus.proxy.so

            zx::channel::create(0, &h0, &c1) //h1=c1

            NewDevhost

                zx_channel_create(0, &hrpc, &dh_hrpc) //一对channel,一端给devhost成员hrpc_,另一端给DevcoordinatorConnection进行监听

                dh->set_hrpc(dh_hrpc);

                dc_launch_devhost(dh, ……, hrpc, ……)

            dh_create_device(……, h1)

                zx::channel::create(0, &hrpc, &hrpc_remote) //一对channel,一端给proxy,另一端传给devhost的g_creation_context变量,当有调用devhost_device_add时,此channel赋值给入参dev->rpc,建立devhost中设备与proxy通信通路。

                dh_send_create_device(……,hrpc_remote,……,h1)

                    fidl_CreateDevice

                        drv->CreateOp //由于前面步骤创建proxy时,对libname的修改,跳过了platform-bus.proxy.so驱动的匹配过程,直接调用其create函数

                            platform_proxy_create

                                PlatformProxy::Create //前面传入的h1通道在这里会赋值给PlatformProxy的rpc_channel_成员,会在PlatformProxy::Rpc中调用

                                    PlatformProxy::Init //设置代理相关参数,待后续详细分析

                xxx //proxy设备在hrpc上等待消息

            dh_send_connect_proxy(……, h0)

                fidl_ConnectProxy

                    ProxyIostate::Create(ctx->conn->dev, std::move(shadow))

                        ios = fbl::make_unique() //创建ProxyIostate对象

                        ios->set_channel(std::move(rpc)) //设置”等待channel“为之前传入的h0,后续platformProxy设备可以通过其rpc_channel_发送请求,这里会通过ProxyIostate::HandleRpc将请求转发给总线设备。详细流程待分析。

                        dev->proxy_ios = ios.get()

        dh_bind_driver(dev->proxy,) //参见前面分析,最终会调用mmc的bind函数,完成驱动初始化

 

下面以imx8mevk单板为例分析GPIO、I2C、USB、GPU驱动框架。

    同orangepi单板,首先有一个板级驱动入口——imx8mevk_bus_bind,函数流程同orangepi,不做具体分析。我们重点看GPIO等部分。

    创建线程,执行imx8mevk_start_thread函数,里面会依次调用imx8m_gpio_init、imx_i2c_init、imx_usb_init、imx_gpu_init等。

 

imx8m_gpio_init //类sysmem设备

    pbus_protocol_device_add

imx_i2c_init //类sysmem设备

    pbus_protocol_device_add

imx_usb_init //类mmc设备

    imx_usb_phy_init

    pbus_device_add

imx_gpu_init //类mmc设备

    mmio_buffer_init_physical

    clock_init

    pbus_device_add

     

可以发现,这类设备主要分为两类:类mmc设备(需要由platform proxy代理)、类sysmem设备(协议注册到platform bus)。mmc设备已经分析过了,下面重点看下sysmem设备。

入口在zircon/system/dev/sysmem/sysmem/binding.cpp的sysmem_bind,这里会绑定板级驱动发布的sysmem设备。

sysmem_bind

    device->Bind

        Device::Bind //调用sysmem的Device::Bind

            device_add //这里会发布一个ZX_PROTOCOL_SYSMEM设备

            pbus_register_protocol //注册sysmem协议到platform bus。这之后,板级驱动中sysmem的while循环就退出了。

 

四、DDK接口分析

DdkAdd

    device_add_args_t args //用入参的name、flags和prop等构造args

    AddProtocol //将platform bus的ddk_proto_id_(ZX_PROTOCOL_PBUS)和ddk_proto_ops_(&pbus_protocol_ops_)存入args结构体.两个变量的赋值在”out/x64/banjoing/gen/ddktl/protocol/platform/bus.h“的PBusProtocol类构造函数中完成。如果此函数模板变量D不是继承自base_protocol,则此函数为空(如platform device设备)。

    device_add

        device_add_from_driver

            devhost_device_create //构造zx_device对象,用driver的参数初始化device对象

            devhost_device_add //这里分三种情况:DEVICE_ADD_MUST_ISOLATE(会绑定一个proxy设备,挂在其下的子设备将会新启动一个devhost来加载)、DEVICE_ADD_INSTANCE(设备实例,在devfs中不可见)、DEVICE_ADD_INVISIBLE(需要调用device_make_visible手动使可见)

                devhost_add

                    fuchsia_device_manager_CoordinatorAddDevice //发IPC到devhost

                        fidl_AddDevice

                            Coordinator::AddDevice //将设备加入parrent下,与parrent同处一个devhost;在devfs下发布设备

            devhost_device_connect

                devhost_get_handles

                    devhost_start_connection //加入消息loop

 

pdev_get_mmio

    proto->ops->get_mmio

        ProxyDevice::PDevGetMmio

            create_physical

pdev_get_bti

    proto->ops->get_bti

        ProxyDevice::PDevGetBti

            proxy_->Rpc //IPC到platform device

                PlatformDevice::DdkRxrpc

                    RpcGetBti

                        bus_->IommuGetBti

                            zx::bti::create(iommu_handle_,)

pdev_get_interrupt

    proto->ops->get_interrupt

        ProxyDevice::PDevGetInterrupt

            zx::interrupt::create

 

pbus_device_add

    proto->ops->device_add(proto->ctx, dev)

        PlatformBus::PBusDeviceAdd

            PlatformDevice::Create

                PlatformDevice::Init

                    DeviceResources::Init

                        CopyResources //这里会将硬件资源如mmio、irq、gpio、i2c、bti等复制到DeviceResources对象

                    DeviceResources::BuildDeviceIndex

            PlatformDevice::Start

                DdkAdd //platform的设备在单独的devhost中运行,如果rootdevice带有protocol则传入ZX_PROTOCOL_PLATFORM_PROXY标志,否则传入ZX_PROTOCOL_PDEV标志(orangepi的mmc属于这种情况)

                    AddProtocol //这里模板匹配为空实现

                    device_add

 

pbus_protocol_device_add

    proto->ops->protocol_device_add //这里要传入依赖的protocol,如orangepi board驱动中传入的ZX_PROTOCOL_SYSMEM

        PlatformBus::PBusProtocolDeviceAdd

            ProtocolDevice::Create

                ProtocolDevice::Init //复制platform bus的protocol,设置其device_add和protocol_device_add为不支持

            ProtocolDevice::Start //protocol的设备在platform bus的devhost中运行。

                DdkAdd

            while (DdkGetProtocol(proto_id, &dummy_proto) == ZX_ERR_NOT_SUPPORTED) //等待协议驱动注册好,如ZX_PROTOCOL_SYSMEM应该是在sysmem中注册。

pbus_register_protocol

    proto->ops->register_protocol

        PlatformBus::PBusRegisterProtocol

iommu_get_bti

    proto->ops->get_bti

 

gpio_impl_set_alt_function

    proto->ops->set_alt_function

        根据具体单板,调用其GpioImplSetAltFunction函数

五、DDK协议(protocol)分析

 

1.ZX_PROTOCOL_PBUS:是platform bus协议。注册在其下的设备可以通过类似device_get_protocol(parent, ZX_PROTOCOL_PBUS, &bus->pbus)的方式获取协议提供的方法结构体——pbus_protocol_t。

pbus_protocol_t定义在out/x64/banjoing/gen/ddk/protocol/platform/bus.h中:

 

typedef struct pbus_protocol pbus_protocol_t;

struct pbus_protocol {

    pbus_protocol_ops_t* ops;

    void* ctx;

};

typedef struct pbus_protocol_ops {

    zx_status_t (*device_add)(void* ctx, const pbus_dev_t* dev);

    zx_status_t (*protocol_device_add)(void* ctx, uint32_t proto_id, const pbus_dev_t* dev);

    zx_status_t (*register_protocol)(void* ctx, uint32_t proto_id, const void* protocol_buffer, size_t protocol_size, const platform_proxy_cb_t* proxy_cb);

    zx_status_t (*get_board_info)(void* ctx, pdev_board_info_t* out_info);

    zx_status_t (*set_board_info)(void* ctx, const pbus_board_info_t* info);

    zx_status_t (*register_sys_suspend_callback)(void* ctx, const pbus_sys_suspend_t* suspend_cb);

} pbus_protocol_ops_t;

 

而在out/x64/banjoing/gen/ddktl/protocol/platform/bus.h中PBusProtocol的构造函数中,对这些构造函数进行了赋值:

public:

    PBusProtocol() {

        internal::CheckPBusProtocolSubclass();

        pbus_protocol_ops_.device_add = PBusDeviceAdd;

        pbus_protocol_ops_.protocol_device_add = PBusProtocolDeviceAdd;

        pbus_protocol_ops_.register_protocol = PBusRegisterProtocol;

        pbus_protocol_ops_.get_board_info = PBusGetBoardInfo;

        pbus_protocol_ops_.set_board_info = PBusSetBoardInfo;

        pbus_protocol_ops_.register_sys_suspend_callback = PBusRegisterSysSuspendCallback;

 

        if constexpr (internal::is_base_proto::value) {

            auto dev = static_cast(this);

            // Can only inherit from one base_protocol implementation.

            ZX_ASSERT(dev->ddk_proto_id_ == 0);

            dev->ddk_proto_id_ = ZX_PROTOCOL_PBUS;

            dev->ddk_proto_ops_ = &pbus_protocol_ops_;

        }

    }

这些函数的实现(PBusDeviceAdd ……)详见platform-bus.cpp。

 

2.ZX_PROTOCOL_PDEV协议:是platform device协议。同ZX_PROTOCOL_PBUS,可以通过类似device_get_protocol(parent, ZX_PROTOCOL_PDEV, &dev->pdev)的方式获取协议提供的方法结构体——pdev_protocol_t。

pdev_protocol_t定义在out/x64/banjoing/gen/ddk/protocol/platform/device.h中:

 

typedef struct pdev_protocol pdev_protocol_t;

typedef struct pdev_protocol_ops {

    zx_status_t (*get_mmio)(void* ctx, uint32_t index, pdev_mmio_t* out_mmio);

    zx_status_t (*get_interrupt)(void* ctx, uint32_t index, uint32_t flags, zx_handle_t* out_irq);

    zx_status_t (*get_bti)(void* ctx, uint32_t index, zx_handle_t* out_bti);

    zx_status_t (*get_smc)(void* ctx, uint32_t index, zx_handle_t* out_smc);

    zx_status_t (*get_device_info)(void* ctx, pdev_device_info_t* out_info);

    zx_status_t (*get_board_info)(void* ctx, pdev_board_info_t* out_info);

    zx_status_t (*device_add)(void* ctx, uint32_t index, const device_add_args_t* args, zx_device_t** out_device);

    zx_status_t (*get_protocol)(void* ctx, uint32_t proto_id, uint32_t index, void* out_out_protocol_buffer, size_t out_protocol_size, size_t* out_out_protocol_actual);

} pdev_protocol_ops_t;

 

struct pdev_protocol {

    pdev_protocol_ops_t* ops;

    void* ctx;

};

 

而在out/x64/banjoing/gen/ddktl/protocol/platform/device.h中的PDevProtocol构造函数中,对这些函数进行了赋值:

public:

    PDevProtocol() {

        internal::CheckPDevProtocolSubclass();

        pdev_protocol_ops_.get_mmio = PDevGetMmio;

        pdev_protocol_ops_.get_interrupt = PDevGetInterrupt;

        pdev_protocol_ops_.get_bti = PDevGetBti;

        pdev_protocol_ops_.get_smc = PDevGetSmc;

        pdev_protocol_ops_.get_device_info = PDevGetDeviceInfo;

        pdev_protocol_ops_.get_board_info = PDevGetBoardInfo;

        pdev_protocol_ops_.device_add = PDevDeviceAdd;

        pdev_protocol_ops_.get_protocol = PDevGetProtocol;

 

        if constexpr (internal::is_base_proto::value) {

            auto dev = static_cast(this);

            // Can only inherit from one base_protocol implementation.

            ZX_ASSERT(dev->ddk_proto_id_ == 0);

            dev->ddk_proto_id_ = ZX_PROTOCOL_PDEV;

            dev->ddk_proto_ops_ = &pdev_protocol_ops_;

        }

    }

这些函数的实现(PDevGetMmio ……)详见platform-proxy-device.cpp。

 

3.ZX_PROTOCOL_IOMMU协议:是platform bus提供的协议。同样可以通过device_get_protocol获取协议方法结构体——iommu_protocol_t。

 

iommu_protocol_t定义在out/build-zircon/build-x64/system/banjo/ddk-protocol-iommu/gen/include/ddk/protocol/iommu.h中:

typedef struct iommu_protocol iommu_protocol_t;

 

typedef struct iommu_protocol_ops {

    zx_status_t (*get_bti)(void* ctx, uint32_t iommu_index, uint32_t bti_id, zx_handle_t* out_handle);

} iommu_protocol_ops_t;

 

struct iommu_protocol {

    iommu_protocol_ops_t* ops;

    void* ctx;

};

而在out/build-zircon/build-x64/system/banjo/ddk-protocol-iommu/gen/include/ddktl/protocol/iommu.h中IommuProtocol的构造函数中,对这些构造函数进行了赋值:

public:

    IommuProtocol() {

        internal::CheckIommuProtocolSubclass();

        iommu_protocol_ops_.get_bti = IommuGetBti;

 

        if constexpr (internal::is_base_proto::value) {

            auto dev = static_cast(this);

            // Can only inherit from one base_protocol implementation.

            ZX_ASSERT(dev->ddk_proto_id_ == 0);

            dev->ddk_proto_id_ = ZX_PROTOCOL_IOMMU;

            dev->ddk_proto_ops_ = &iommu_protocol_ops_;

        }

    }

IommuGetBti函数的实现,详见PlatformBus::IommuGetBti

 

4.ZX_PROTOCOL_SYSMEM协议

5.ZX_PROTOCOL_SDMMC协议

6.ZX_PROTOCOL_GPIO_IMPL协议

 

六、devhost与coordinator分析

devmgr中调用CreateDevhostJob创建devhost的根job——”zircon-drivers“。然后构造coordinator对象,利用coordinator完成后续流程的交互。

devmgr进程:-----------------------------------------------------------------------------------------------

1.Coordinator类

在devmgr中实例化,全局只有一份。它保存了系统所有的driver,所有的device以及所有的devhost。同时,root_device_、misc_device_、sys_device_、test_device_也是其成员变量。也就是说,devmgr通过它来管理系统设备。

 

它提供的主要方法包括:

InitializeCoreDevices:初始化root、misc、sys及test设备属性

ScanSystemDrivers:扫描/system/driver目录下的所有驱动,并调用BindSystemDrivers尝试进行驱动绑定

BindSystemDrivers:调用BindDriver绑定system驱动

BindDriver:调用AttemptBind,绑定驱动到合适的设备

AttemptBind:如果要绑定的设备属性为DEV_CTX_MUST_ISOLATE,则需要先调用PrepareProxy创建代理,否则直接调用dh_bind_driver绑定驱动。

PrepareProxy:此为Coordinator类的核心函数之一。它会依次调用dc_create_proxy、NewDevhost、dh_create_device、dh_send_connect_proxy

NewDevhost:会创建一对channel,一端给将要创建的devhost,另一端在devhost主进程中传给根DevcoordinatorConnection对象。

BindDrivers:遍历系统中所有的driver,调用BindDriver尝试进行绑定

AddDevice:添加一个新设备到parent(同一个devhost)。

BindDevice:遍历系统所有driver,调用AttemptBind绑定驱动

HandleDeviceRead:读取设备收到的消息,并调用相应的fidl_ops方法处理。

HandleNewDevice:对于invisible的设备,MakeVisible后会调用;可见设备则在AddDevice后就调用。函数会遍历所有驱动,对设备进行绑定。

 

2.Device类

每个Device设备都保存着一个指针(Coordinator* coordinator),指向devmgr的coordinator对象。

每个Device设备还保存着一个channel hrpc保存着与Devhost通信的通道。

另外,还包含着async::WaitMethod成员及相应的HandleRpcEntry和BeginWait方法,用于支持在Device上执行消息等待与处理。

 

3.devmgr全局函数:

dh_bind_driver:调用dh_send_bind_driver函数,通过dev->hrpc通道,发消息让devhost绑定驱动

dc_create_proxy:构造代理设备,设备名、驱动lib名(会将.so后缀换为.proxy.so)、protocol_id都设置与被代理设备相同

dh_create_device:创建一对channel,一端给proxy对象监听上,另一端给platform的parrent对象(sys)对应的DevcoordinatorConnection对象监听上。

dh_send_connect_proxy:与devhost的ProxyIostate建立连接关系(待分析)。

 

devmgr中的fidl_ops方法如下,详细信息见代码:

static fuchsia_device_manager_Coordinator_ops_t fidl_ops = {

    .AddDevice = fidl_AddDevice, //添加设备

    .AddDeviceInvisible = fidl_AddDeviceInvisible, //添加设备,默认不可见

    .RemoveDevice = fidl_RemoveDevice,

    .MakeVisible = fidl_MakeVisible,

    .BindDevice = fidl_BindDevice, //绑定设备

    .GetTopologicalPath = fidl_GetTopologicalPath,

    .LoadFirmware = fidl_LoadFirmware,

    .GetMetadata = fidl_GetMetadata,

    .GetMetadataSize = fidl_GetMetadataSize,

    .AddMetadata = fidl_AddMetadata,

    .PublishMetadata = fidl_PublishMetadata,

    .DmCommand = fidl_DmCommand,

    .DmOpenVirtcon = fidl_DmOpenVirtcon,

    .DmMexec = fidl_DmMexec,

    .DirectoryWatch = fidl_DirectoryWatch,

};

 

devhost进程:------------------------------------------------------------------------------------------------

1.Devhost类:

其包含的hrpc_成员,负责与根DevcoordinatorConnection对象通信 //不太清楚作用。

提供的方法略过。

 

2.DevcoordinatorConnection类

继承自AsyncLoopOwnedRpcHandler,实现了HandleRpc方法,提供消息监听、分发处理功能

 

3.zx_driver类

成员函数包括zx_driver_rec_t* driver_rec_、const zx_driver_ops_t* ops_以及name、libname等。

主要方法为BindOp:调用驱动bind方法;CreateOp:调用驱动create方法。其余方法略过。

 

4.zx_device类

包含zx_protocol_device_t* ops、protocol_id、protocol_ops用于发布协议;rpc用于与devmgr中对应Device对象通信;conn用于存储关联的DevcoordinatorConnection对象。

主要方法包括:OpenOp、OpenAtOp、ReadOp、WriteOp、IoctlOp //mmc驱动未提供

 

5.devhost提供的全局函数:

dh_handle_rpc_read:读取监听通道消息,调用fuchsia_device_manager_Controller_dispatch进行分发处理

 

devhost中的fidl_ops方法如下,详细信息见代码:

static fuchsia_device_manager_Controller_ops_t fidl_ops = {

    .CreateDeviceStub = fidl_CreateDeviceStub,

    .CreateDevice = fidl_CreateDevice, //创建设备,一般用于总线设备创建,如platform bus等

    .BindDriver = fidl_BindDriver, //绑定驱动,调用驱动bind方法

    .ConnectProxy = fidl_ConnectProxy,

    .Suspend = fidl_Suspend,

    .RemoveDevice = fidl_RemoveDevice,

};

 

-----------------------------------------------------------------------------------------------------------

IPC交互过程

对于platform设备来说,全局coordinator对象中保存着所有的Device设备(维护着一颗设备树,根节点为sys),而platform中会保存着一一对应的虚拟设备(zx_device),维护着一颗虚拟树(根节点也叫sys)。

Device中成员变量zx::channel hrpc和对应zx_device的zx::unowned_channel rpc是一对,可以相互通信。

Device设备会在Coordinator::AddDevice函数中调用dev->wait.set_object(dev->hrpc.get()),加入coordinator的等待队列;而zx_device的处理略微不同,devhost会为每个zx_device设备新建一个DevcoordinatorConnection对象来实现对channel的监听。

Device设备收到消息后会走如下流程:

Device::HandleRpcEntry——》HandleRpc——》”dev->coordinator->HandleDeviceRead“(Coordinator::HandleDeviceRead)——》fuchsia_device_manager_Coordinator_try_dispatch,然后调用devmgr的fidl_ops对应的处理函数。

zx_device虚拟设备收到消息后流程如下:

AsyncLoopOwnedRpcHandler::HandleRpcEntry——》DevcoordinatorConnection::HandleRpc——》dh_handle_rpc_read——》fuchsia_device_manager_Controller_dispatch,然后调用devhost的的fidl_ops对应的处理函数。

 

说明:

    在linux中,系统通过扫描dts来创建单板支持的设备,然后根据设备来扫描匹配的驱动(通过compatible属性),然后再调用驱动的probe函数完成驱动初始化,及相关ops函数与设备的绑定。

    而fuchsia中,则是通过扫描驱动,然后根据驱动来创建设备、文件节点。创建设备目的是为了在其下挂接子设备。驱动匹配的是其父设备提供的protocol及相关属性。

附图:Coordinator中与platform bus中两颗设备树对比图

fuchisia 驱动框架分析_第2张图片

 

附表:orangepi单板增加mmc及网卡驱动后的设备树图

dm dump

[root]

    pid=2077

      [null] pid=2077 /boot/driver/builtin.so

      [zero] pid=2077 /boot/driver/builtin.so

   [misc]

       pid=2146

         [console] pid=2146 /boot/driver/console.so

         [demo-fifo] pid=2146 /boot/driver/demo-fifo.so

         [dmctl] pid=2146 /boot/driver/dmctl.so

         [tapctl] pid=2146 /boot/driver/ethertap.so

         [hidctl] pid=2146 /boot/driver/hidctl.so

         [ktrace] pid=2146 /boot/driver/ktrace.so

         [ptmx] pid=2146 /boot/driver/pty.so

         [nand-ctl] pid=2146 /boot/driver/ram-nand.so

         [ramctl] pid=2146 /boot/driver/ramdisk.so

            [ramdisk-0] pid=2146 /boot/driver/ramdisk.so

               [block] pid=2146 /boot/driver/block.so

                  [fvm] pid=2146 /boot/driver/fvm.so

                     [blobfs-p-1] pid=2146 /boot/driver/fvm.so

                        [block] pid=2146 /boot/driver/block.so

                     [minfs-p-2] pid=2146 /boot/driver/fvm.so

                        [block] pid=2146 /boot/driver/block.so

         [sysinfo] pid=2146 /boot/driver/sysinfo.so

   [sys]

       pid=1936 /boot/driver/platform-bus.so

         [platform] pid=1936 /boot/driver/platform-bus.so

            [orangepipc2] pid=1936 /boot/driver/orangepipc2.so

            [14:01:1] pid=1936 /boot/driver/platform-bus.so

               <14:01:1> pid=2783 /boot/driver/platform-bus.proxy.so

                  [sunxi_mmc] pid=2783 /boot/driver/sunxi-mmc.so                    

                     [sdmmc] pid=2783 /boot/driver/sdmmc.so

                        [sdmmc-block] pid=2783 /boot/driver/sdmmc.so

                           [block] pid=2783 /boot/driver/block.so

            [14:01:d] pid=1936 /boot/driver/platform-bus.so

               <14:01:d> pid=2826 /boot/driver/platform-bus.proxy.so

                  [Designware MAC] pid=2826 /boot/driver/sun8i.so

                     [ethernet] pid=2826 /boot/driver/ethernet.so

            [00:00:1b] pid=1936 /boot/driver/platform-bus.so

               [sysmem] pid=1936 /boot/driver/sysmem.so

   [test]

       pid=2037

         [test] pid=2037 /boot/driver/test.so

         [usb-virtual-bus] pid=2037 /boot/driver/usb-virtual-bus.so

         [wlantapctl] pid=2037 /system/driver/wlantap.so

你可能感兴趣的:(fuchsia)