栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 系统运维 > 运维 > Linux

【SemiDrive源码分析】【X9芯片启动流程】21 - MailBox、RPMSG、IPCC 核间通信机制介绍(代码分析篇)之 Mailbox for Linux 篇

Linux 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

【SemiDrive源码分析】【X9芯片启动流程】21 - MailBox、RPMSG、IPCC 核间通信机制介绍(代码分析篇)之 Mailbox for Linux 篇

【SemiDrive源码分析】【X9芯片启动流程】21 - MailBox、RPMSG、IPCC 核间通信机制介绍(代码分析篇)之 Mailbox for Linux 篇
  • 一、Mailbox for Linux 驱动框架分析
    • 1、mailbox 目录源码简要分析
    • 2、mailbox 各结构体介绍
      • 2.1 sd_mbox_device 结构体: 由芯驰产商封装
      • 2.2 sd_mbox_tx_msg 结构体: 由芯驰产商封装
      • 2.3 sd_mbox_chan结构体:由芯驰产商封装
      • 2.4 mbox_chan 结构体
      • 2.5 mbox_controller 结构体
      • 2.6 mbox_chan_ops 结构体
      • 2.7 mbox_client 结构体
  • 二、Client 驱动示例 mailbox-test.c 源码分析
    • 1、平台驱动与DTS设备树适配,进入 mbox_test_probe()
    • 2、探测函数 mbox_test_probe()
    • 3、mailbox-test.c完整源码
  • 三、MailBox 数据收发链路源码分析
    • 1、mbox_controller_register() 注册 controller 驱动
    • 2、client 通过 mbox_test_request_channel() 申请 channel 通路
    • 3、client 通过 mbox_send_message() 发送数据,并等待 tx_done 回调
    • 4、controller 收到remote中断调用 mbox_chan_received_data() 将数据拷贝到 client->rx_buffer 中


本 SemiDrive源码分析 之 Yocto源码分析 系列文章汇总如下:

  1. 《【SemiDrive源码分析】【Yocto源码分析】01 - yocto/base目录源码分析(编译环境初始化流程)》
  2. 《【SemiDrive源码分析】【Yocto源码分析】02 - yocto/meta-openembedded目录源码分析》
  3. 《【SemiDrive源码分析】【Yocto源码分析】03 - yocto/meta-semidrive目录及Yocto Kernel编译过程分析(上)》
  4. 《【SemiDrive源码分析】【Yocto源码分析】04 - yocto/meta-semidrive目录及Yocto Kernel编译过程分析(下)》
  5. 《【SemiDrive源码分析】【Yocto源码分析】05 - 找一找Yocto Kernel编译过程中所有Task的源码在哪定义的呢?》
  6. 《【SemiDrive源码分析】【Yocto源码分析】06 - Kernel编译生成的Image.bin、Image_nobt.dtb、modules.tgz 这三个文件分别是如何生成的?》
  7. 《【SemiDrive源码分析】【Yocto源码分析】07 - core-image-base-x9h_ref_serdes.rootfs.ext4 文件系统是如何生成的》
  8. 《【SemiDrive源码分析】【X9芯片启动流程】08 - X9平台 lk 目录源码分析 之 目录介绍》
  9. 《【SemiDrive源码分析】【X9芯片启动流程】09 - X9平台系统启动流程分析》
  10. 《【SemiDrive源码分析】【X9芯片启动流程】10 - BareMetal_Suite目录R5 DIL.bin 引导程序源代码分析》
  11. 《【SemiDrive源码分析】【X9芯片启动流程】11 - freertos_safetyos目录Cortex-R5 DIL2.bin 引导程序源代码分析》
  12. 《【SemiDrive源码分析】【X9芯片启动流程】12 - freertos_safetyos目录Cortex-R5 DIL2.bin 之 sdm_display_init 显示初始化源码分析》
  13. 《【SemiDrive源码分析】【X9芯片驱动调试】13 - GPIO 配置方法》
  14. 《【SemiDrive源码分析】【X9芯片启动流程】14 - freertos_safetyos目录Cortex-R5 SafetyOS/RTOS工作流程分析》
  15. 《【SemiDrive源码分析】【X9芯片启动流程】15 - freertos_safetyos目录 R5 SafetyOS 之 tcpip_init() 代码流程分析》
  16. 《【SemiDrive源码分析】【X9 Audio音频模块分析】16 - 音频模块框图及硬件原理图分析》
  17. 《【SemiDrive源码分析】【X9芯片启动流程】17 - R5 SafetyOS 之 LK_INIT_LEVEL_PLATFORM 阶段代码流程分析(上)dcf_init 核间通信初始化》
  18. 《【SemiDrive源码分析】【X9芯片启动流程】18 - R5 SafetyOS 之 LK_INIT_LEVEL_PLATFORM 阶段代码流程分析(下)》
  19. 《【SemiDrive源码分析】【X9芯片启动流程】19 - MailBox、RPMSG、IPCC 核间通信机制介绍(理论篇)》
  20. 《【SemiDrive源码分析】【X9芯片启动流程】20 - MailBox、RPMSG、IPCC 核间通信机制介绍(代码分析篇)之 MailBox for RTOS 篇》
  21. 《【SemiDrive源码分析】【X9芯片启动流程】21 - MailBox、RPMSG、IPCC 核间通信机制介绍(代码分析篇)之 Mailbox for Linux 篇》
  22. 《【SemiDrive源码分析】【X9芯片启动流程】22 - MailBox、RPMSG、IPCC 核间通信机制介绍(代码分析篇)之 RPMSG-VIRTIO篇》
  23. 《【SemiDrive源码分析】【X9芯片启动流程】23 - MailBox、RPMSG、IPCC 核间通信机制介绍(代码分析篇)之 RPMSG-IPCC篇》
  24. 《【SemiDrive源码分析】【X9芯片启动流程】24 - MailBox、RPMSG、IPCC 核间通信机制介绍(代码分析篇)之 Property篇》
  25. 《【SemiDrive源码分析】【X9芯片启动流程】25 - MailBox、RPMSG、IPCC 核间通信机制介绍(代码分析篇)之 RPCall篇》
  26. 《【SemiDrive源码分析】【X9芯片启动流程】26 - MailBox、RPMSG、IPCC 核间通信机制介绍(代码分析篇)之 Notify篇》
  27. 《【SemiDrive源码分析】【X9芯片启动流程】27 - MailBox、RPMSG、IPCC 核间通信机制介绍(代码分析篇)之 Socket篇》
  28. 《【SemiDrive源码分析】【X9芯片启动流程】28 - MailBox、RPMSG、IPCC 核间通信机制介绍(代码分析篇)之 /dev/vircan篇》
  29. 《【SemiDrive源码分析】【X9芯片启动流程】29 - freertos_safetyos目录 R5 SafetyOS 之 LK_INIT_LEVEL_TARGET 阶段代码流程分析》
  30. 《【SemiDrive源码分析】【X9芯片启动流程】30 - freertos_safetyos目录 R5 SafetyOS 之 .apps 应用启动代码流程分析》


在前面文章中,我们主要从理论方面介绍了,基于MailBox的各种场景的API,
其特点如下:

RTOSLinux KernelLinux UserDom0 LinuxDomU LinuxQNX特点和使用场景
Mailbox支持支持不支持支持支持支持中断上下文回调,低延迟,无消息队列,不推荐用户直接使用
RPMSG-VIRTIO支持支持支持支持不支持支持大数据块传输,使用DDR 共享内存数据,高吞吐量,MTU 可调节
RPMSG-IPCC支持支持支持支持不支持支持控制命令,事件,通知等,交互数据小,延迟较小
RPCall支持支持不支持支持支持支持基于 request / reply模型的分布式数据交换方式
Property支持支持支持支持支持支持一种分布式、运行时异构数据存储机制,可以保存运行状态或执行结果,方便异构OS 之间共用某些资源
Notify支持支持不支持支持支持不支持短数据,低延迟,单向传输
Socket无支持支持支持支持支持不支持支持 AF_RPMSG 协议,用户层使用,缺点是需要修改 Linux uapi 头文件
/dev/vircanNANA支持支持支持不支持Linux 访 问 Safety CAN

前面,我们分析了 MailBox for RTOS,也知道了,如何在 RTOS 中实现MailBox的通信过程,还是不难的,是吧。
本文,我们来看看,Android Linux 该如何实现 MailBox 通信功能。


一、Mailbox for Linux 驱动框架分析

芯驰 Mailbox 设备驱动符合 Linux Mailbox 标准驱动模型,
驱动代码位于:${LINUX_KERNEL}/drivers/mailbox/semidrive-mailbox.c
示例代码位于:${LINUX_KERNEL}/drivers/mailbox/mailbox-test.c,另外在 Semidrive rpmsg device 实现中也使用到了 Mailbox 接口。

使用时需包含以下头文件:

#include 
#include 
#include 

mailbox 是一种通过消息队列和中断驱动信号处理多处理器之间通迅的框架,
它的实现主要分为controller和client 两种方式。

client 可以调用 mbox_request_channel() 函数申请 channel 通路,
然后调用 controller 提供的 mbox_send_message() 函数通过channel通路发送信息给到另一个controller,
使用完成后,调用 mbox_free_channel 释放 channel,这样其人就可以继续使用这个channel通路进行通信了。

工作流程如下:

  1. driver 通过 mbox_controller_register 注册 controller
  2. client 通过 mbox_request_channel 调⽤ driver->startup
  3. client 通过 mbox_send_message 调⽤ driver->send_data,并等待 txdone
  4. driver 收到 remote 的中断读取数据调⽤ mbox_chan_received_data 将数据放到 client->rx_buffer 中

1、mailbox 目录源码简要分析

我们进入编译out目录下,看下实际的kernel config 文件:

# androidouttargetproductx9hp_refobjKERNEL_OBJ.config
CONFIG_XEN=y
CONFIG_MAILBOX=y
CONFIG_MAILBOX_TEST=y
CONFIG_SEMIDRIVE_MBOX=y
CONFIG_ARM_MHU=y
CONFIG_PLATFORM_MHU=y

结合config配置,我们来简化下Makefile:

# buildsystemandroidkerneldriversmailboxMakefile
obj-$(CONFIG_MAILBOX)		+= mailbox.o
obj-$(CONFIG_MAILBOX_TEST)	+= mailbox-test.o
obj-$(CONFIG_ARM_MHU)		+= arm_mhu.o
obj-$(CONFIG_PLATFORM_MHU)	+= platform_mhu.o
sd-mb-$(CONFIG_XEN)			+= semidrive-mailbox-front.o semidrive-mailbox-back.o
obj-$(CONFIG_SEMIDRIVE_MBOX)    += semidrive-mailbox.o $(sd-mb-y)

在kerneldriversmailbox中各源码作用如下:

  1. mailbox.c : framework层的框架代码实现,提供controller注册、channel通路申请、chan链路发送接收相关函数实现
		EXPORT_SYMBOL_GPL(mbox_chan_received_data);			// chan 链路接收数据回调
		EXPORT_SYMBOL_GPL(mbox_chan_txdone);				// chan 链路发送数据成功回调
		EXPORT_SYMBOL_GPL(mbox_client_txdone);				// client 发送成功回调
		EXPORT_SYMBOL_GPL(mbox_client_peek_data);			// client poll查询数据接口
		EXPORT_SYMBOL_GPL(mbox_send_message);				// 发送数据
		EXPORT_SYMBOL_GPL(mbox_request_channel);			// 请求channel 通路接口
		EXPORT_SYMBOL_GPL(mbox_request_channel_byname);		// 通过name 申请channel 通路
		EXPORT_SYMBOL_GPL(mbox_free_channel);				// 释放channel 通路
		EXPORT_SYMBOL_GPL(mbox_controller_register);		// 注册 mbox_controller 
		EXPORT_SYMBOL_GPL(mbox_controller_unregister);		// 卸载 mbox_controller 
  1. semidrive-mailbox.c:芯驰平台mailbox 驱动实现 ,负责与 "semidrive,sd-mailbox" 进行设备驱动匹配,注册mbdev->controller``controller 驱动控制器及 mbox中断
		static int sd_mbox_probe(struct platform_device *pdev)
		{
			devm_request_threaded_irq(dev, mbdev->irq, sd_mbox_rx_interrupt,sd_mbox_irq_handler, 0,"mbox", mbdev);
			err = mbox_controller_register(&mbdev->controller);
			mbdev->initialized = true;
			g_mbdev = mbdev;
		}
		
		static const struct dev_pm_ops sd_mbox_pm_ops = {
			SET_SYSTEM_SLEEP_PM_OPS(sd_mbox_suspend, sd_mbox_resume)
		};
		
		static const struct of_device_id sd_mailbox_of_match[] = {{
				.compatible	= "semidrive,sd-mailbox",
		},};
		MODULE_DEVICE_TABLE(of, sd_mailbox_of_match);
		
		static struct platform_driver sd_mbox_driver = {
			.probe	= sd_mbox_probe,
			.remove	= sd_mbox_remove,
			.driver	= {
				.name = "sd-mailbox",
				.pm = &sd_mbox_pm_ops,
				.of_match_table = of_match_ptr(sd_mailbox_of_match),
			},
		};
		platform_driver_register(&sd_mbox_driver);
  1. arm_mhu.c:注册amba_driver,绑定结构体arm_mhu_driver,注册 mhu->mbox controller 驱动控制器
		static int mhu_probe(struct amba_device *adev, const struct amba_id *id){
			mhu = devm_kzalloc(dev, sizeof(*mhu), GFP_KERNEL);
			
			for (i = 0; i < MHU_CHANS; i++) {
				mhu->chan[i].con_priv = &mhu->mlink[i];
				mhu->mlink[i].irq = adev->irq[i];
				mhu->mlink[i].rx_reg = mhu->base + mhu_reg[i];
				mhu->mlink[i].tx_reg = mhu->mlink[i].rx_reg + TX_REG_OFFSET;
			}
			mhu->mbox.dev = dev;
			mhu->mbox.chans = &mhu->chan[0];
			mhu->mbox.num_chans = MHU_CHANS;
			mhu->mbox.ops = &mhu_ops;
			mhu->mbox.txdone_irq = false;
			mhu->mbox.txdone_poll = true;
			mhu->mbox.txpoll_period = 1;
			err = mbox_controller_register(&mhu->mbox);
		}
		static struct amba_id mhu_ids[] = {
			{.id	= 0x1bb098,    .mask	= 0xffffff, },
		};
		MODULE_DEVICE_TABLE(amba, mhu_ids);
		
		static struct amba_driver arm_mhu_driver = {
			.drv = {
				.name	= "mhu",
			},
			.id_table	= mhu_ids,
			.probe		= mhu_probe,
			.remove		= mhu_remove,
		};
		module_amba_driver(arm_mhu_driver);
  1. platform_mhu.c:注册平台驱动platform_mhu_driver ,绑定设备树"amlogic,meson-gxbb-mhu",也是注册 mhu->mbox controller 驱动控制器
		static int platform_mhu_probe(struct platform_device *pdev)
		{
			int platform_mhu_reg[MHU_CHANS] = {
				MHU_SEC_OFFSET, MHU_LP_OFFSET, MHU_HP_OFFSET
			};
		
			mhu = devm_kzalloc(dev, sizeof(*mhu), GFP_KERNEL);
			for (i = 0; i < MHU_CHANS; i++) {
				mhu->chan[i].con_priv = &mhu->mlink[i];
				mhu->mlink[i].irq = platform_get_irq(pdev, i);
				mhu->mlink[i].rx_reg = mhu->base + platform_mhu_reg[i];
				mhu->mlink[i].tx_reg = mhu->mlink[i].rx_reg + TX_REG_OFFSET;
			}
			mhu->mbox.dev = dev;
			mhu->mbox.chans = &mhu->chan[0];
			mhu->mbox.num_chans = MHU_CHANS;
			mhu->mbox.ops = &platform_mhu_ops;
			mhu->mbox.txdone_irq = false;
			mhu->mbox.txdone_poll = true;
			mhu->mbox.txpoll_period = 1;
			err = mbox_controller_register(&mhu->mbox);
		}
		static const struct of_device_id platform_mhu_dt_ids[] = {
			{ .compatible = "amlogic,meson-gxbb-mhu", },
		};
		MODULE_DEVICE_TABLE(of, platform_mhu_dt_ids);
		
		static struct platform_driver platform_mhu_driver = {
			.probe	= platform_mhu_probe,
			.remove	= platform_mhu_remove,
			.driver = {
				.name = "platform-mhu",
				.of_match_table	= platform_mhu_dt_ids,
			},
		};
		module_platform_driver(platform_mhu_driver);
  1. semidrive-mailbox-front.c:注册 xenbus driver 驱动,绑定结构体mbox_front_driver,注册 mbdev->controller controller 驱动控制器
		static int mbox_front_probe(struct xenbus_device *xbdev,const struct xenbus_device_id *id)
		{
			mbdev = devm_kzalloc(&xbdev->dev, sizeof(*mbdev), GFP_KERNEL);
			mbdev->xb_dev = xbdev;
			for (i = 0; i < MB_MAX_CHANS; i++) {
				mbdev->chan[i].con_priv = &mbdev->mlink[i];
				mbdev->mlink[i].is_run = 0;
				mbdev->mlink[i].target = 0xff; 
			}
			np = of_find_compatible_node(NULL, NULL, "semidrive,mbox-front");
			xbdev->dev.of_node = np;
			mbdev->controller.dev = &xbdev->dev;
			mbdev->controller.chans = &mbdev->chan[0];
			mbdev->controller.num_chans = MB_MAX_CHANS;
			mbdev->controller.ops = &mbox_front_ops;
			mbdev->controller.txdone_irq = false;
			mbdev->controller.txdone_poll = true;
			mbdev->controller.txpoll_period = 1;
			mbdev->controller.of_xlate = mbox_front_of_xlate;
			mbdev->inited = true;
			err = mbox_controller_register(&mbdev->controller);
			xenbus_switch_state(xbdev, XenbusStateInitialising);
		}
		
		static const struct xenbus_device_id mbox_front_ids[] = {
			{ XEN_MBOX_DEVICE_ID },
		};
		
		static struct xenbus_driver mbox_front_driver = {
			.name = "mbox",
			.ids  = mbox_front_ids,
			.probe = mbox_front_probe,
			.remove = mbox_front_remove,
			.otherend_changed = mbox_backend_changed,
		};
		xenbus_register_frontend(&mbox_front_driver);
  1. semidrive-mailbox-back.c:注册 xenbus driver驱动,绑了定结构体 mbox_back_driver
		extern struct sd_mbox_device *g_mbdev;
		static int mbox_back_probe(struct xenbus_device *xbdev,  const struct xenbus_device_id *id)
		{
			be = devm_kzalloc(&xbdev->dev, sizeof(struct mbox_backend), GFP_KERNEL);
		
			be->xb_dev = xbdev;
			be->channel = mbox_alloc_channel(xbdev, xbdev->otherend_id);
		
			be->channel->be = be;
			dev_set_drvdata(&xbdev->dev, be);
			be->inited = true;
			be->mbdev = g_mbdev;
			err = mbox_register_backend(be);
			err = xenbus_switch_state(xbdev, XenbusStateInitWait);
		}
		
		static const struct xenbus_device_id mbox_back_ids[] = {
			{ XEN_MBOX_DEVICE_ID },
		};
		static struct xenbus_driver mbox_back_driver = {
			.ids = mbox_back_ids,
			.probe = mbox_back_probe,
			.remove = mbox_back_remove,
			.otherend_changed = mbox_front_changed,
		};
		xenbus_register_backend(&mbox_back_driver);
  1. mailbox-test.c:测试示例 client 程序实现,负责与 "mailbox-test" 进行设备驱动匹配
		static int mbox_test_probe(struct platform_device *pdev)
		{
			tdev = devm_kzalloc(&pdev->dev, sizeof(*tdev), GFP_KERNEL);
			
			res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
			tdev->tx_mmio = devm_ioremap_resource(&pdev->dev, res);
			
			res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
			tdev->rx_mmio = devm_ioremap_resource(&pdev->dev, res);
			
			tdev->tx_channel = mbox_test_request_channel(pdev, "tx");
			tdev->rx_channel = mbox_test_request_channel(pdev, "rx");
			
			tdev->dev = &pdev->dev;
			tdev->rx_buffer = devm_kzalloc(&pdev->dev, MBOX_MAX_MSG_LEN, GFP_KERNEL);
			ret = mbox_test_add_debugfs(pdev, tdev);
		}
		
		static const struct of_device_id mbox_test_match[] = {
			{ .compatible = "mailbox-test" },
			{},
		};
		MODULE_DEVICE_TABLE(of, mbox_test_match);
		
		static struct platform_driver mbox_test_driver = {
			.driver = {
				.name = "mailbox_test",
				.of_match_table = mbox_test_match,
			},
			.probe  = mbox_test_probe,
			.remove = mbox_test_remove,
		};
		module_platform_driver(mbox_test_driver);

通过上面对每个文件的简要分析,其实还是比较清楚的,简单总结如下:

  1. mailbox.c: 实现 MailBox 框架驱动
  2. semidrive-mailbox.c: 注册mbdev->controller 驱动控制器 及 mbox中断
  3. arm_mhu.c:注册平台amba_driver,注册 mhu->mbox 驱动控制器
  4. platform_mhu.c:注册平台驱动platform_mhu_driver ,绑定设备树"amlogic,meson-gxbb-mhu",注册 mhu->mbox 驱动控制器
  5. semidrive-mailbox-front.c:注册 xenbus driver 驱动,注册 mbdev->controller 驱动控制器
  6. semidrive-mailbox-back.c:注册 xenbus driver驱动
  7. mailbox-test.c:测试示例 client 程序实现,负责与 "mailbox-test" 进行设备驱动匹配


2、mailbox 各结构体介绍 2.1 sd_mbox_device 结构体: 由芯驰产商封装
# androidkernelincludelinuxmailboxsemidrive-mailbox.h
typedef struct sd_mbox_device {
	void __iomem *reg_base;						// 使用的 IO内存空间物理地址
	void __iomem *txb_base;						// 发送端的 IO内存空间物理地址
	void __iomem *rxb_base;						// 接收端的 IO内存空间物理地址
	int curr_cpu;								//
	int irq;									// 中断号
	spinlock_t msg_lock;						// 锁
	struct sd_mbox_tx_msg tmsg[MB_MAX_MSGS];	// 当前 mbox_device 使用的 tmsg 结构体数组, 充许有多个 tmsg 结构体
	struct sd_mbox_chan mlink[MB_MAX_CHANS];	// 当前 mbox_device 使用的 mlink 结构体数组, 充许有多个 mlink 结构体
	struct mbox_chan chan[MB_MAX_CHANS];		// 当前 mbox_device 使用的 chan 结构体数组, 充许有多个 chan 结构体
	struct mbox_controller controller;			// 当前 mbox_device 对应的 controller 结构体
	bool initialized;							// 初始化标志位
	struct device *dev;							// 对应的字符设备结构体
} sd_mbox_controller_t;

2.2 sd_mbox_tx_msg 结构体: 由芯驰产商封装
# androidkernelincludelinuxmailboxsemidrive-mailbox.h
struct sd_mbox_tx_msg {
	bool	used;								// 当前 sd_mbox_tx_msg  是否正在使用
	unsigned msg_id;		
	unsigned length;							// 当前 buffer中的消息长度
	bool	 use_buffer;						// 是否使用 buffer 内存
	unsigned remote;							// 远端 controller 号
	void __iomem *tmh;							// Transmit Message Header 头信息相关寄存器地址指针
	void __iomem *tmc;							// Transmit Message Control 传输控制相关寄存器地址指针
	void __iomem *tx_buf;						// 发送 buffer io 地址指针
};

2.3 sd_mbox_chan结构体:由芯驰产商封装
# androidkernelincludelinuxmailboxsemidrive-mailbox.h
struct sd_mbox_chan {
	char chan_name[MB_MAX_NAMES];					// 当前 chan 链路的名字
	struct sd_mbox_tx_msg *msg;						// 当前 chan 链路使用的 消息结构体
	unsigned target;								// 远端 controller 号
	unsigned actual_size;							// 
	unsigned protocol;								//
	bool priority;									//
	bool is_run;									// 是否正在使用
	void __iomem *rmh;								// Receive Message Header 接收相关寄存器 地址
	u32 cl_data;									// 
	u32 dest_addr;									//
	int quota; 
};

2.4 mbox_chan 结构体
# androidkernelincludelinuxmailbox_controller.h
struct mbox_chan {
	struct mbox_controller *mbox;		// 指向当前chan 所属的controller控制器 结构体,Pointer to the parent/provider of this channel
	unsigned txdone_method;				// API 调用 txdone的方法,Way to detect TXDone chosen by the API
	struct mbox_client *cl;				// 正在使用当前 chan 链路的 mbox client 结构体
	struct completion tx_complete;		// 传输完成标志位
	void *active_req;					// 当前的 request 请求
	unsigned msg_count, msg_free;		// 当前使用的msg大小, 以及剩余的msg大小
	void *msg_data[MBOX_TX_QUEUE_LEN]; 	// msg data buffer 
	spinlock_t lock; 					// Serialise access to the channel
	void *con_priv;						// Hook for controller driver to attach private data
};

2.5 mbox_controller 结构体
# androidkernelincludelinuxmailbox_controller.h
struct mbox_controller {
	struct device *dev;					// 当前 controller 对应的 dev设备结构体
	const struct mbox_chan_ops *ops;	// 挂在当前controller下的chan 的操作方法, Operators that work on each communication chan
	struct mbox_chan *chans;			// 挂在当前controller下的所有chan 的数组结构体
	int num_chans;						// 挂在当前controller下的chan 数据
	bool txdone_irq;					// 如果使能,标志 传输完成后会通过中断触发 txdone
	bool txdone_poll;					// 如果使能,标志 传输完成后,需要通过poll 查询来调用 tx_done
	unsigned txpoll_period;				// 如果使能 poll,poll 查询周期配置(单位ms),the API polls for last TX's status after these many millisecs
	struct mbox_chan *(*of_xlate)(struct mbox_controller *mbox, const struct of_phandle_args *sp);		// 获取特定channel的回调函数
					
	struct hrtimer poll_hrt;			// API private. hrtimer used to poll for TXDONE on all channels.
	int hrt_state;						// API private. Flag for current state of poll_hrt.
	spinlock_t hrt_lock;				// API private. Lock for poll_hrt's state machine.

	struct list_head node;				// API private. To hook into list of controllers.
};

2.6 mbox_chan_ops 结构体
# androidkernelincludelinuxmailbox_controller.h

struct mbox_chan_ops {
	int (*send_data)(struct mbox_chan *chan, void *data);
	void (*cancel_data)(struct mbox_chan *chan);
	int (*startup)(struct mbox_chan *chan);
	void (*shutdown)(struct mbox_chan *chan);
	bool (*last_tx_done)(struct mbox_chan *chan);
	bool (*peek_data)(struct mbox_chan *chan);
};

2.7 mbox_client 结构体
# androidkernelincludelinuxmailbox_client.h

struct mbox_client {
	struct device *dev;
	bool tx_block;
	unsigned long tx_tout;
	bool knows_txdone;

	void (*rx_callback)(struct mbox_client *cl, void *mssg);
	void (*tx_prepare)(struct mbox_client *cl, void *mssg);
	void (*tx_done)(struct mbox_client *cl, void *mssg, int r);
};

下面,我们依次进入各代码看看他们具体实现



二、Client 驱动示例 mailbox-test.c 源码分析 1、平台驱动与DTS设备树适配,进入 mbox_test_probe()

在驱动代码中,匹配的字符串为mailbox-test。

# androidkerneldriversmailboxmailbox-test.c
static const struct of_device_id mbox_test_match[] = {
	{ .compatible = "mailbox-test" },
	{},
};
MODULE_DEVICE_TABLE(of, mbox_test_match);

static struct platform_driver mbox_test_driver = {
	.driver = {
		.name = "mailbox_test",
		.of_match_table = mbox_test_match,
	},
	.probe  = mbox_test_probe,
	.remove = mbox_test_remove,
};
module_platform_driver(mbox_test_driver);

以X9为例,dts中配置如下:

# androidkernelarcharm64bootdtssemidrivex9.dtsi
{
	compatible = "semidrive,kunlun";
	#address-cells = <2>;
	#size-cells = <2>;
	soc {
		compatible = "simple-bus";
		#address-cells = <2>;
		#size-cells = <2>;
		ranges;
		
		mbox: mbox@34040000 {
			compatible = "semidrive,sd-mailbox";
			reg = <0x0 0x34040000 0x0 0x200>,
				<0x0 0x34041000 0x0 0x1000>,
				<0x0 0x34042000 0x0 0x8000>;
			interrupts = ;
			#mbox-cells = <2>;
			status = "disabled";
		};
		
		mbox_test: mbox-test@0 {
			compatible = "mailbox-test";
			mbox-names = "tx", "rx";
			mboxes = <&mbox 0x05 0x0501>, <&mbox 0x05 0x0501>;
			status = "disabled";
		};
		rpmsg0: rpmsg@0 {
			compatible = "sd,rpmsg-vq,rp_saf";
			mboxes = <&mbox 0x13 0x1300>;
			status = "disabled";
		};
		ipcc0: ipcc@0 {
			compatible = "sd,rpmsg-ipcc";
			mboxes = <&mbox 0x33 0x3300>;
			#rpmsg-cells = <2>;
			status = "disabled";
		};
	}
}

# androidkernelarcharm64bootdtssemidrivex9_high-plus_ref_native_ivi_serdes_8g.dts
&mbox {
	status = "okay";
};
&mbox_test {
	status = "okay";
};

可以看出,dts配置"mailbox-test" ,使用的是 mbox 的0x5通路。
当代码调用module_platform_driver() 时,驱动与设备树匹配,就会进入 mbox_test_probe() 函数开始初始化mailbox通路。

2、探测函数 mbox_test_probe()

主要工作如下:

  1. 解析 TX 通路的 IO内存空间指针,保存在 tdev->tx_mmio中
  2. 解析 RX 通路的 IO内存空间指针,保存在 tdev->rx_mmio中
  3. 调用 mbox_test_request_channel() 建立 tx、rx的 channel 通路,并配置相应的接收函数:
    (1)client->rx_callback = mbox_test_receive_message;:通过memcpy_formio函数或memcpy函数读取 mbox 缓存区中的信息
    (2)client->tx_prepare = mbox_test_prepare_message;:通过memcpy_toio 函数将message放入发送缓存区中
    (3)client->tx_done = mbox_test_message_sent;:发送成功函数回调函数
  4. 申请接收缓存区 buffer大小,size是128字节
  5. 添加debugfs调试节点
    创建mailbox/message 节点,绑定的是mbox_test_message_ops 操作函数结构体
    创建mailbox/signal 节点,绑定的是mbox_test_signal_ops 操作函数结构体
# androidkerneldriversmailboxmailbox-test.c
static int mbox_test_probe(struct platform_device *pdev)
{
	struct mbox_test_device *tdev;
	struct resource *res;
	resource_size_t size;

	tdev = devm_kzalloc(&pdev->dev, sizeof(*tdev), GFP_KERNEL);
	// 1. 解析 TX 通路的 IO内存空间指针,保存在tdev->tx_mmio中
	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
	if (res) {
		size = resource_size(res);
		tdev->tx_mmio = devm_ioremap_resource(&pdev->dev, res);
	} 
	// 2. 解析 RX 通路的 IO内存空间指针,保存在tdev->rx_mmio中
	res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
	if (res) {
		size = resource_size(res);
		tdev->rx_mmio = devm_ioremap_resource(&pdev->dev, res);
	} 
	// 3. 调用mbox_test_request_channel() 建立 tx、rx的channel 通路,并配置相应的接收函数rx_callback,tx_prepare,tx_done
	tdev->tx_channel = mbox_test_request_channel(pdev, "tx");
	tdev->rx_channel = mbox_test_request_channel(pdev, "rx");
	=============================>
	+	static struct mbox_chan *mbox_test_request_channel(struct platform_device *pdev, const char *name){
	+		struct mbox_client *client;
	+		struct mbox_chan *channel;
	+	
	+		client = devm_kzalloc(&pdev->dev, sizeof(*client), GFP_KERNEL);
	+	
	+		client->dev			= &pdev->dev;
	+		client->rx_callback	= mbox_test_receive_message;		
	+		client->tx_prepare	= mbox_test_prepare_message;
	+		client->tx_done		= mbox_test_message_sent;
	+		client->tx_block	= true;
	+		client->knows_txdone	= false;
	+		client->tx_tout		= 500;
	+	
	+		channel = mbox_request_channel_byname(client, name);
	+		return channel;
	+	}
	<=============================
	dev_info(&pdev->dev, "tx %p rx %p channeln", tdev->tx_channel, tdev->rx_channel);

	tdev->dev = &pdev->dev;
	platform_set_drvdata(pdev, tdev);
	// 4. 申请接收缓存区 buffer大小,size是128字节
	if (tdev->rx_channel) {
		tdev->rx_buffer = devm_kzalloc(&pdev->dev, MBOX_MAX_MSG_LEN, GFP_KERNEL);
	}
	// 5. 添加debugfs调试节点
	// 创建mailbox/message 节点,绑定的是mbox_test_message_ops 操作函数结构体
	// 创建mailbox/signal 节点,绑定的是mbox_test_signal_ops 操作函数结构体
	ret = mbox_test_add_debugfs(pdev, tdev);

	init_waitqueue_head(&tdev->waitq);
	dev_info(&pdev->dev, "Successfully registeredn");
	return 0;
}

3、mailbox-test.c完整源码

附上 mailbox-test.c 源码,我将代码做了下删减,把一些不重要的代码删了,如下:

# androidkerneldriversmailboxmailbox-test.c
#include 
#include 

#define MBOX_MAX_SIG_LEN		8
#define MBOX_MAX_MSG_LEN		(128)
#define MBOX_BYTES_PER_LINE		16
#define MBOX_HEXDUMP_LINE_LEN	((MBOX_BYTES_PER_LINE * 4) + 2)
#define MBOX_HEXDUMP_MAX_LEN	(MBOX_HEXDUMP_LINE_LEN * (MBOX_MAX_MSG_LEN / MBOX_BYTES_PER_LINE))

static bool mbox_data_ready;
static struct dentry *root_debugfs_dir;

struct mbox_test_device {
	struct device		*dev;
	void __iomem		*tx_mmio;
	void __iomem		*rx_mmio;
	struct mbox_chan	*tx_channel;
	struct mbox_chan	*rx_channel;
	char				*rx_buffer;
	char				*signal;
	char				*message;
	spinlock_t			lock;
	wait_queue_head_t	waitq;
	struct fasync_struct	*async_queue;
};

---------------------------------------------------------------------------------------------------------------------
// 字符设备写函数
static ssize_t mbox_test_signal_write(struct file *filp, const char __user *userbuf, size_t count, loff_t *ppos){
	copy_from_user(tdev->signal, userbuf, count)
}
static const struct file_operations mbox_test_signal_ops = {
	.write	= mbox_test_signal_write,
	.open	= simple_open,
	.llseek	= generic_file_llseek,
};

---------------------------------------------------------------------------------------------------------------------------------------
static int mbox_test_message_fasync(int fd, struct file *filp, int on){
	struct mbox_test_device *tdev = filp->private_data;
	return fasync_helper(fd, filp, on, &tdev->async_queue);
}
static ssize_t mbox_test_message_write(struct file *filp, const char __user *userbuf, size_t count, loff_t *ppos){
	tdev->message = kzalloc(MBOX_MAX_MSG_LEN, GFP_KERNEL);
	msg = (sd_msghdr_t *)tdev->message;
	mb_msg_init_head(msg, 3, 0, 0, count + MB_MSG_HDR_SZ, IPCC_ADDR_MB_TEST);
	ret = copy_from_user(msg->data, userbuf, count);
	print_hex_dump_bytes("Client: Sending: Message: ", DUMP_PREFIX_ADDRESS, tdev->message, msg->dat_len);
	
	// 通过mbox_send_message() 函数往mbox发送数据
	ret = mbox_send_message(tdev->tx_channel, data);
	return ret < 0 ? ret : count;
}
static bool mbox_test_message_data_ready(struct mbox_test_device *tdev){
	data_ready = mbox_data_ready;
	return data_ready;
}
static ssize_t mbox_test_message_read(struct file *filp, char __user *userbuf,size_t count, loff_t *ppos){
	touser = kzalloc(MBOX_HEXDUMP_MAX_LEN + 1, GFP_KERNEL);
	
	// 不断的查询 mbox 消息是下否已经到达
	add_wait_queue(&tdev->waitq, &wait);
	do {
		__set_current_state(TASK_INTERRUPTIBLE);
		if (mbox_test_message_data_ready(tdev)) break;
		signal_pending(current);
		schedule();
	} while (1);

	// 通过simple_read_from_buffer() 从mbox开始读取消息
	ptr = tdev->rx_buffer;
	memset(tdev->rx_buffer, 0, MBOX_MAX_MSG_LEN);
	mbox_data_ready = false;
	ret = simple_read_from_buffer(userbuf, count, ppos, touser, MBOX_HEXDUMP_MAX_LEN);
	__set_current_state(TASK_RUNNING);
}
static unsigned int mbox_test_message_poll(struct file *filp, struct poll_table_struct *wait){
	struct mbox_test_device *tdev = filp->private_data;
	poll_wait(filp, &tdev->waitq, wait);
	if (mbox_test_message_data_ready(tdev))
		return POLLIN | POLLRDNORM;
	return 0;
}
static const struct file_operations mbox_test_message_ops = {
	.write	= mbox_test_message_write,
	.read	= mbox_test_message_read,
	.fasync	= mbox_test_message_fasync,
	.poll	= mbox_test_message_poll,
	.open	= simple_open,
	.llseek	= generic_file_llseek,
};

static int mbox_test_add_debugfs(struct platform_device *pdev, struct mbox_test_device *tdev){
	// 创建 mailbox debugfs 目录
	root_debugfs_dir = debugfs_create_dir("mailbox", NULL);
	// 创建mailbox/message 节点,绑定的是mbox_test_message_ops 操作函数结构体
	debugfs_create_file("message", 0600, root_debugfs_dir,  tdev, &mbox_test_message_ops);
	// 创建mailbox/signal 节点,绑定的是mbox_test_signal_ops 操作函数结构体
	debugfs_create_file("signal", 0200, root_debugfs_dir,tdev, &mbox_test_signal_ops);
}
-----------------------------------------------------------------------------------------------------------------------------

static void mbox_test_receive_message(struct mbox_client *client, void *message)
{
	if (tdev->rx_mmio) {
		memcpy_fromio(tdev->rx_buffer, tdev->rx_mmio, MBOX_MAX_MSG_LEN);
		print_hex_dump_bytes("Client: Received [MMIO]: ", DUMP_PREFIX_ADDRESS, tdev->rx_buffer, MBOX_MAX_MSG_LEN);
	} else if (message) {
		sd_msghdr_t *msg = message;
		print_hex_dump_bytes("Client: Received [API]: ", DUMP_PREFIX_ADDRESS, msg, msg->dat_len);
		memcpy(tdev->rx_buffer, msg->data, msg->dat_len - MB_MSG_HDR_SZ);
	}
	mbox_data_ready = true;
	wake_up_interruptible(&tdev->waitq);
	kill_fasync(&tdev->async_queue, SIGIO, POLL_IN);
}

static void mbox_test_prepare_message(struct mbox_client *client, void *message)
{
	struct mbox_test_device *tdev = dev_get_drvdata(client->dev);
	if (tdev->tx_mmio) {
		if (tdev->signal)
			memcpy_toio(tdev->tx_mmio, tdev->message, MBOX_MAX_MSG_LEN);
		else
			memcpy_toio(tdev->tx_mmio, message, MBOX_MAX_MSG_LEN);
	}
}

static void mbox_test_message_sent(struct mbox_client *client,void *message, int r){
}

static struct mbox_chan *mbox_test_request_channel(struct platform_device *pdev, const char *name)
{
	struct mbox_client *client;
	struct mbox_chan *channel;

	client = devm_kzalloc(&pdev->dev, sizeof(*client), GFP_KERNEL);
	client->dev		= &pdev->dev;
	client->rx_callback	= mbox_test_receive_message;
	client->tx_prepare	= mbox_test_prepare_message;
	client->tx_done		= mbox_test_message_sent;
	client->tx_block	= true;
	client->knows_txdone	= false;
	client->tx_tout		= 500;

	channel = mbox_request_channel_byname(client, name);
	return channel;
}

static int mbox_test_probe(struct platform_device *pdev)
{
	struct mbox_test_device *tdev;
	tdev = devm_kzalloc(&pdev->dev, sizeof(*tdev), GFP_KERNEL);

	
	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
	tdev->tx_mmio = devm_ioremap_resource(&pdev->dev, res);
	
	
	res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
	tdev->rx_mmio = devm_ioremap_resource(&pdev->dev, res);

	tdev->tx_channel = mbox_test_request_channel(pdev, "tx");
	tdev->rx_channel = mbox_test_request_channel(pdev, "rx");
	tdev->rx_buffer = devm_kzalloc(&pdev->dev, MBOX_MAX_MSG_LEN, GFP_KERNEL);
	
	ret = mbox_test_add_debugfs(pdev, tdev);
	return 0;
}

static int mbox_test_remove(struct platform_device *pdev)
{
	struct mbox_test_device *tdev = platform_get_drvdata(pdev);
	debugfs_remove_recursive(root_debugfs_dir);
	if (tdev->tx_channel)	mbox_free_channel(tdev->tx_channel);
	if (tdev->rx_channel)	mbox_free_channel(tdev->rx_channel);
	return 0;
}

static const struct of_device_id mbox_test_match[] = {
	{ .compatible = "mailbox-test" },
};
MODULE_DEVICE_TABLE(of, mbox_test_match);

static struct platform_driver mbox_test_driver = {
	.driver = {
		.name = "mailbox_test",
		.of_match_table = mbox_test_match,
	},
	.probe  = mbox_test_probe,
	.remove = mbox_test_remove,
};
module_platform_driver(mbox_test_driver);



三、MailBox 数据收发链路源码分析 1、mbox_controller_register() 注册 controller 驱动
2、client 通过 mbox_test_request_channel() 申请 channel 通路
3、client 通过 mbox_send_message() 发送数据,并等待 tx_done 回调
4、controller 收到remote中断调用 mbox_chan_received_data() 将数据拷贝到 client->rx_buffer 中




参考:《Linux Kernel编程 — Mailbox驱动框架》

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/939455.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号