一、驱动添加教程
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
"__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框架分析
整体框架图如下:
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
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
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
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中两颗设备树对比图
附表:orangepi单板增加mmc及网卡驱动后的设备树图
dm dump
[root]
2077
[
null
] pid=
2077
/boot/driver/builtin.so
[zero] pid=
2077
/boot/driver/builtin.so
[misc]
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]
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]
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