- 一、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源码分析 系列文章汇总如下:
- 《【SemiDrive源码分析】【Yocto源码分析】01 - yocto/base目录源码分析(编译环境初始化流程)》
- 《【SemiDrive源码分析】【Yocto源码分析】02 - yocto/meta-openembedded目录源码分析》
- 《【SemiDrive源码分析】【Yocto源码分析】03 - yocto/meta-semidrive目录及Yocto Kernel编译过程分析(上)》
- 《【SemiDrive源码分析】【Yocto源码分析】04 - yocto/meta-semidrive目录及Yocto Kernel编译过程分析(下)》
- 《【SemiDrive源码分析】【Yocto源码分析】05 - 找一找Yocto Kernel编译过程中所有Task的源码在哪定义的呢?》
- 《【SemiDrive源码分析】【Yocto源码分析】06 - Kernel编译生成的Image.bin、Image_nobt.dtb、modules.tgz 这三个文件分别是如何生成的?》
- 《【SemiDrive源码分析】【Yocto源码分析】07 - core-image-base-x9h_ref_serdes.rootfs.ext4 文件系统是如何生成的》
- 《【SemiDrive源码分析】【X9芯片启动流程】08 - X9平台 lk 目录源码分析 之 目录介绍》
- 《【SemiDrive源码分析】【X9芯片启动流程】09 - X9平台系统启动流程分析》
- 《【SemiDrive源码分析】【X9芯片启动流程】10 - BareMetal_Suite目录R5 DIL.bin 引导程序源代码分析》
- 《【SemiDrive源码分析】【X9芯片启动流程】11 - freertos_safetyos目录Cortex-R5 DIL2.bin 引导程序源代码分析》
- 《【SemiDrive源码分析】【X9芯片启动流程】12 - freertos_safetyos目录Cortex-R5 DIL2.bin 之 sdm_display_init 显示初始化源码分析》
- 《【SemiDrive源码分析】【X9芯片驱动调试】13 - GPIO 配置方法》
- 《【SemiDrive源码分析】【X9芯片启动流程】14 - freertos_safetyos目录Cortex-R5 SafetyOS/RTOS工作流程分析》
- 《【SemiDrive源码分析】【X9芯片启动流程】15 - freertos_safetyos目录 R5 SafetyOS 之 tcpip_init() 代码流程分析》
- 《【SemiDrive源码分析】【X9 Audio音频模块分析】16 - 音频模块框图及硬件原理图分析》
- 《【SemiDrive源码分析】【X9芯片启动流程】17 - R5 SafetyOS 之 LK_INIT_LEVEL_PLATFORM 阶段代码流程分析(上)dcf_init 核间通信初始化》
- 《【SemiDrive源码分析】【X9芯片启动流程】18 - R5 SafetyOS 之 LK_INIT_LEVEL_PLATFORM 阶段代码流程分析(下)》
- 《【SemiDrive源码分析】【X9芯片启动流程】19 - MailBox、RPMSG、IPCC 核间通信机制介绍(理论篇)》
- 《【SemiDrive源码分析】【X9芯片启动流程】20 - MailBox、RPMSG、IPCC 核间通信机制介绍(代码分析篇)之 MailBox for RTOS 篇》
- 《【SemiDrive源码分析】【X9芯片启动流程】21 - MailBox、RPMSG、IPCC 核间通信机制介绍(代码分析篇)之 Mailbox for Linux 篇》
- 《【SemiDrive源码分析】【X9芯片启动流程】22 - MailBox、RPMSG、IPCC 核间通信机制介绍(代码分析篇)之 RPMSG-VIRTIO篇》
- 《【SemiDrive源码分析】【X9芯片启动流程】23 - MailBox、RPMSG、IPCC 核间通信机制介绍(代码分析篇)之 RPMSG-IPCC篇》
- 《【SemiDrive源码分析】【X9芯片启动流程】24 - MailBox、RPMSG、IPCC 核间通信机制介绍(代码分析篇)之 Property篇》
- 《【SemiDrive源码分析】【X9芯片启动流程】25 - MailBox、RPMSG、IPCC 核间通信机制介绍(代码分析篇)之 RPCall篇》
- 《【SemiDrive源码分析】【X9芯片启动流程】26 - MailBox、RPMSG、IPCC 核间通信机制介绍(代码分析篇)之 Notify篇》
- 《【SemiDrive源码分析】【X9芯片启动流程】27 - MailBox、RPMSG、IPCC 核间通信机制介绍(代码分析篇)之 Socket篇》
- 《【SemiDrive源码分析】【X9芯片启动流程】28 - MailBox、RPMSG、IPCC 核间通信机制介绍(代码分析篇)之 /dev/vircan篇》
- 《【SemiDrive源码分析】【X9芯片启动流程】29 - freertos_safetyos目录 R5 SafetyOS 之 LK_INIT_LEVEL_TARGET 阶段代码流程分析》
- 《【SemiDrive源码分析】【X9芯片启动流程】30 - freertos_safetyos目录 R5 SafetyOS 之 .apps 应用启动代码流程分析》
在前面文章中,我们主要从理论方面介绍了,基于MailBox的各种场景的API,
其特点如下:
| RTOS | Linux Kernel | Linux User | Dom0 Linux | DomU Linux | QNX | 特点和使用场景 | |
|---|---|---|---|---|---|---|---|
| Mailbox | 支持 | 支持 | 不支持 | 支持 | 支持 | 支持 | 中断上下文回调,低延迟,无消息队列,不推荐用户直接使用 |
| RPMSG-VIRTIO | 支持 | 支持 | 支持 | 支持 | 不支持 | 支持 | 大数据块传输,使用DDR 共享内存数据,高吞吐量,MTU 可调节 |
| RPMSG-IPCC | 支持 | 支持 | 支持 | 支持 | 不支持 | 支持 | 控制命令,事件,通知等,交互数据小,延迟较小 |
| RPCall | 支持 | 支持 | 不支持 | 支持 | 支持 | 支持 | 基于 request / reply模型的分布式数据交换方式 |
| Property | 支持 | 支持 | 支持 | 支持 | 支持 | 支持 | 一种分布式、运行时异构数据存储机制,可以保存运行状态或执行结果,方便异构OS 之间共用某些资源 |
| Notify | 支持 | 支持 | 不支持 | 支持 | 支持 | 不支持 | 短数据,低延迟,单向传输 |
| Socket | 无支持 | 支持 | 支持 | 支持 | 支持 | 不支持 | 支持 AF_RPMSG 协议,用户层使用,缺点是需要修改 Linux uapi 头文件 |
| /dev/vircan | NA | NA | 支持 | 支持 | 支持 | 不支持 | 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通路进行通信了。
工作流程如下:
- driver 通过 mbox_controller_register 注册 controller
- client 通过 mbox_request_channel 调⽤ driver->startup
- client 通过 mbox_send_message 调⽤ driver->send_data,并等待 txdone
- 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中各源码作用如下:
- 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
- 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);
- 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);
- 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);
- 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);
- 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);
- 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);
通过上面对每个文件的简要分析,其实还是比较清楚的,简单总结如下:
- mailbox.c: 实现 MailBox 框架驱动
- semidrive-mailbox.c: 注册mbdev->controller 驱动控制器 及 mbox中断
- arm_mhu.c:注册平台amba_driver,注册 mhu->mbox 驱动控制器
- platform_mhu.c:注册平台驱动platform_mhu_driver ,绑定设备树"amlogic,meson-gxbb-mhu",注册 mhu->mbox 驱动控制器
- semidrive-mailbox-front.c:注册 xenbus driver 驱动,注册 mbdev->controller 驱动控制器
- semidrive-mailbox-back.c:注册 xenbus driver驱动
- 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);
};
下面,我们依次进入各代码看看他们具体实现
在驱动代码中,匹配的字符串为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通路。
主要工作如下:
- 解析 TX 通路的 IO内存空间指针,保存在 tdev->tx_mmio中
- 解析 RX 通路的 IO内存空间指针,保存在 tdev->rx_mmio中
- 调用 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;:发送成功函数回调函数 - 申请接收缓存区 buffer大小,size是128字节
- 添加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);
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驱动框架》



