转自:https://blog.csdn.net/u010961173/article/details/96422441

1 概述(1)mailbox是一种框架,通过消息队列和中断驱动信号处理多处理器间的通讯;(2)mailbox的实现分为contoller和client。简单的说就是client 可以通过controller提供的channel发送信息给controller;(3)在drivers/mailbox下实现的都是controller的源码;具体到某个厂商的硬件,则描述如下:

Kconfig文件:内核开关,用于配置mbox模块是否编译到内核;config ARM_MHU     tristate "ARM MHU Mailbox"     depends on ARM_AMBA     help           Say Y here if you want to build the ARM MHU controller driver.  The controller has 3 mailbox channels, the last of which can be  used in Secure mode only.

Makefile文件:根据Kconfig编译相应的模块;

我们知道要实现mailbox的源文件其实只有两个obj-$(CONFIG_MAILBOX) += mailbox.oobj-$(CONFIG_ARM_MHU) += arm_mhu.o

其中,mailbox.c 是kernel提供的framework,arm_mhu.c 则是具体厂商的实现

(4)client 通过mbox_send_message给controller发送数据的时候必须指定channel;        int mbox_send_message(struct mbox_chan *chan, void *mssg)

(5)client 在通过mbox_send_message给controller发送数据的时候必须指定channel,channel可以通过以下方式获得。目前kernel提供了两种方法得到mailbox的channelstruct mbox_chan *mbox_request_channel_byname(struct mbox_client *cl,const char *name);struct mbox_chan *mbox_request_channel(struct mbox_client *cl, int index);

使用完成后调用mbox_free_channel 释放channel,这样别人就可以继续使用这个channel;

void mbox_free_channel(struct mbox_chan *chan); /* may sleep */

其中,mbox_request_channel_byname是mbox_request_channel的一个封装。

 2 基本框架

目录:

drivers/mailbox

mailbox.c/mailbox.h/mailbox-test.c/-mailbox.c  

 

3 关键数据结构struct mbox_controller {    struct device *dev;                                      // 特定mailbox驱动probe时赋值 dev = &pdev->dev    const struct mbox_chan_ops *ops;                         // mailbox channel需要实现的功能函数    struct mbox_chan *chans;                                 // mailbox channel指针数组    int num_chans;                                           // mailbox channel个数    bool txdone_irq; // 通过中断来判断上次传输是否完成    bool txdone_poll;   // 通过poll机制来判断上次传输是否完成    unsigned txpoll_period;                                  // POLL 周期, 以ms计    struct mbox_chan *(*of_xlate)(struct mbox_controller *mbox,                      const struct of_phandle_args *sp);     // 获取特定channel的回调函数    /* Internal to API */    struct hrtimer poll_hrt;    struct list_head node;}; struct mbox_chan {    struct mbox_controller *mbox;                            // contronller指针    unsigned txdone_method;                                      struct mbox_client *cl;                                  // client指针    struct completion tx_complete;                           //    void *active_req;    unsigned msg_count, msg_free;    void *msg_data[MBOX_TX_QUEUE_LEN];    spinlock_t lock; /* Serialise access to the channel */    void *con_priv;}; struct mbox_chan_ops {    int (*send_data)(struct mbox_chan *chan, void *data);         // 发送数据(需要last data sent)    int (*startup)(struct mbox_chan *chan);                       // 特定mailbox 启动    void (*shutdown)(struct mbox_chan *chan);                     // 特定mailbox 关闭    bool (*last_tx_done)(struct mbox_chan *chan);                 // 如果TXDONE_BY_POLL 该回调会被使用    bool (*peek_data)(struct mbox_chan *chan);                    // 检测是否有数据}; struct mbox_client {    struct device *dev;                                           // client 设备    bool tx_block;                                                // block until last data is all transmitted    unsigned long tx_tout;                                        // max block period for timeout    bool knows_txdone;                                            // txdone 回调,如果controller已经有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);   // 检测txdone};

 

4 函数接口4.1 mailbox controller api文件:kernel/include/linux/mailbox_controller.h

(1)注册、注销控制器int mbox_controller_register(struct mbox_controller *mbox); /* can sleep */--------probe中调用void mbox_controller_unregister(struct mbox_controller *mbox); /* can sleep */-------probe中调用

(2)(对外接口)将底层收到的数据回调给上层应用

void mbox_chan_received_data(struct mbox_chan *chan, void *data); /* atomic */

(3)通知上层当前数据已经发送完成

void mbox_chan_txdone(struct mbox_chan *chan, int r); /* atomic */

4.2 mailbox client api文件:kernel/include/linux/mailbox_client.h

(1)发送数据前,申请通道struct mbox_chan *mbox_request_channel_byname(struct mbox_client *cl,                          const char *name);struct mbox_chan *mbox_request_channel(struct mbox_client *cl, int index);

(2)数据发送

int mbox_send_message(struct mbox_chan *chan, void *mssg);void mbox_client_txdone(struct mbox_chan *chan, int r); /* atomic */

(3)数据记录bool mbox_client_peek_data(struct mbox_chan *chan); /* atomic */

(4)释放通道

void mbox_free_channel(struct mbox_chan *chan); /* may sleep */

 

 

5 Device Tree中的写法kernel4.14/Documentation/devicetree/bindings/mailbox/hisilicon,hi6220-mailbox.txt

Hisilicon Hi6220 Mailbox Driver=============================== Hisilicon Hi6220 mailbox supports up to 32 channels. Each channelis unidirectional with a maximum message size of 8 words. I/O isperformed using register access (there is no DMA) and the cellraises an interrupt when messages are received. Mailbox Device Node(Controller):(设备节点相关的设备树)==================== Required properties:--------------------- compatible:        Shall be "hisilicon,hi6220-mbox"- reg:            Contains the mailbox register address range (base            address and length); the first item is for IPC            registers, the second item is shared buffer for            slots.- #mbox-cells:        Common mailbox binding property to identify the number            of cells required for the mailbox specifier. Must be 3.            <&phandle slot_id dst_irq ack_irq>              phandle: Label name of mailbox controller              slot_id: Slot id used either for TX or RX              dst_irq: IRQ identifier index number which used by MCU              ack_irq: IRQ identifier index number with generating a                       TX/RX interrupt to application processor,                   mailbox driver uses it to acknowledge interrupt- interrupts:        Contains the interrupt information for the mailbox            device. The format is dependent on which interrupt            controller the SoCs use. Optional Properties:--------------------- hi6220,mbox-tx-noirq: Property of MCU firmware's feature, so mailbox driver            use this flag to ask MCU to enable "automatic idle            flag" mode or IRQ generated mode to acknowledge a TX            completion. Example:--------     mailbox: mailbox@f7510000 {        compatible = "hisilicon,hi6220-mbox";        reg = <0x0 0xf7510000 0x0 0x1000>, /* IPC_S */              <0x0 0x06dff800 0x0 0x0800>; /* Mailbox */        interrupt-parent = <&gic>;        interrupts = ;        #mbox-cells = <3>;    }; Mailbox client(client相关的设备树)=============== Required properties:--------------------- compatible:        Many (See the client docs).- mboxes:        Standard property to specify a Mailbox (See ./mailbox.txt)            Cells must match 'mbox-cells' (See Mailbox Device Node above). Optional Properties:--------------------- mbox-names:        Name given to channels seen in the 'mboxes' property. Example:--------     stub_clock: stub_clock {        compatible = "hisilicon,hi6220-stub-clk";        hisilicon,hi6220-clk-sram = <&sram>;        #clock-cells = <1>;        mbox-names = "mbox-tx", "mbox-rx";        mboxes = <&mailbox 1 0 11>, <&mailbox 0 1 10>;    };Example:

kernel4.14/arch/arm64/boot/dts/hisilicon/hi6220.dtsi     mailbox: mailbox@f7510000 {        compatible = "hisilicon,hi6220-mbox";        reg = <0x0 0xf7510000 0x0 0x1000>, /* IPC_S */              <0x0 0x06dff800 0x0 0x0800>; /* Mailbox */        interrupt-parent = <&gic>;        interrupts = ;        #mbox-cells = <3>;    }; Example:--------     stub_clock: stub_clock {        compatible = "hisilicon,hi6220-stub-clk";        hisilicon,hi6220-clk-sram = <&sram>;        #clock-cells = <1>;        mbox-names = "mbox-tx", "mbox-rx";        mboxes = <&mailbox 1 0 11>, <&mailbox 0 1 10>;       };

6 原理详解6.1 原理概述

(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中;

6.1.1 mailbox driver流程(1)配置controller属性:(2)申请chan,配置chan个数(3)配置of_xlate回调,获取chan(4)配置chan_ops(5)配置txdone判断方式(6)通过mailbox_controller_register 注册controller;

6.1.2 mailbox client 流程(1)通过mbox_request_channel_byname 根据"mbox-names"申请channel;(2)创建mbox设备;(3)通过mbox设备的write/read 函数访问controller; 其中,write 通过调用mbox_send_message,add_to_rbuf拷贝msg到chan->msg_data[MAX = 20],msg_submit读取msg_data[idx],放到tx_prepare中,调用具体驱动的send message写寄存器; read 通过irq驱动,irq读取寄存器得到消息,调用mailbox.c中的mbox_chan_received_data,再调用client的rx_callback将得到的数据放到client->rx_buffer中;

6.2 Mailbox Controller驱动6.2.1 Mailbox Controller驱动初始化6.2.1.1 mbox controller初始化函数

core_initcall(hi6220_mbox_init)    >>>platform_driver_register(&hi6220_mbox_driver);module_exit(hi6220_mbox_exit);    >>>platform_driver_unregister(&hi6220_mbox_driver); static struct platform_driver hi6220_mbox_driver = {    .driver = {        .name = "hi6220-mbox",        .owner = THIS_MODULE,        .of_match_table = hi6220_mbox_of_match,    },    .probe    = hi6220_mbox_probe,//mbox controller drivers 初始化函数    .remove    = hi6220_mbox_remove, //mbox controller drivers 逆初始化函数}; static const struct of_device_id hi6220_mbox_of_match[] = {    { .compatible = "hisilicon,hi6220-mbox", },    {},};

6.2.1.2 调用probe/remove 函数

probe()函数主要用于初始化mbox controller.

hi6220_mbox_probe(struct platform_device *pdev)    >>>mbox = devm_kzalloc(dev, sizeof(*mbox), GFP_KERNEL);//分配vendor设备结构体struct hi6220_mbox *mbox    >>>初始化struct hi6220_mbox *mbox中的相关成员变量            >>>mbox->mchan = devm_kzalloc(dev,mbox->chan_num * sizeof(*mbox->mchan), GFP_KERNEL);//为chan_num个struct hi6220_mbox_chan申请内存            >>>mbox->chan = devm_kzalloc(dev,mbox->chan_num * sizeof(*mbox->chan), GFP_KERNEL);//为chan_num个struct mbox_chan申请内存            >>>mbox->irq = platform_get_irq(pdev, 0);            >>>res = platform_get_resource(pdev, IORESOURCE_MEM, 0);                    mbox->ipc = devm_ioremap_resource(dev, res);            >>>    res = platform_get_resource(pdev, IORESOURCE_MEM, 1);                    mbox->base = devm_ioremap_resource(dev, res);            >>>申请中断                        err = devm_request_irq(dev, mbox->irq, hi6220_mbox_interrupt, 0,    dev_name(dev), mbox);//其中,hi6220_mbox_interrupt为中断mbox->irq对应的服务函数            >>>初始化controller                        mbox->controller.dev = dev;                        mbox->controller.chans = &mbox->chan[0];                        mbox->controller.num_chans = mbox->chan_num;                        mbox->controller.ops = &hi6220_mbox_ops;                        mbox->controller.of_xlate = hi6220_mbox_xlate;                         for (i = 0; i < mbox->chan_num; i++) {                            mbox->chan[i].con_priv = &mbox->mchan[i];                            mbox->irq_map_chan[i] = NULL;                             mbox->mchan[i].parent = mbox;                            mbox->mchan[i].slot   = i;                        }            >>>mask and clear all interrupt vectors                      writel(0x0,  ACK_INT_MSK_REG(mbox->ipc));                    writel(~0x0, ACK_INT_CLR_REG(mbox->ipc));                          >>>判断中断使用poll方式还是中断方式                    /* use interrupt for tx's ack */                    if (of_find_property(node, "hi6220,mbox-tx-noirq", NULL))                        mbox->tx_irq_mode = false;                    else                        mbox->tx_irq_mode = true;                     if (mbox->tx_irq_mode)                        mbox->controller.txdone_irq = true;                    else {                        mbox->controller.txdone_poll = true;                        mbox->controller.txpoll_period = 5;                    }            >>>注册控制器                err = mbox_controller_register(&mbox->controller);            >>>保存mbox设备数据到pdev->dev->driver_data                platform_set_drvdata(pdev, mbox); hi6220_mbox_remove(struct platform_device *pdev)        >>>struct hi6220_mbox *mbox = platform_get_drvdata(pdev);        >>>mbox_controller_unregister(&mbox->controller);

6.2.1.3 中断处理流程probe函数中注册中断,driver 收到remote的中断,读取数据调用mbox_chan_received_data将数据放到 client->rx_buffer中

static irqreturn_t hi6220_mbox_interrupt(int irq, void *p)        >>>读取中断状态(哪个子中断置位???)                   state = readl(ACK_INT_STAT_REG(mbox->ipc));        >>>    查询每个子中断的状态并进行响应                while (state) {                            >>>查询中断状态中的最高置1的位                    intr_bit = __ffs(state);//                    state &= (state - 1);                     chan = mbox->irq_map_chan[intr_bit];//每个中断位对应一个中断通道                    if (!chan) {                        dev_warn(mbox->dev, "%s: unexpected irq vector %d\n",                             __func__, intr_bit);                        continue;                    }                     mchan = chan->con_priv;         //通道私有数据                    if (mchan->dir == MBOX_TX)      //若该通道(中断)为发送方向                        mbox_chan_txdone(chan, 0);                    else {                          //若该通道(中断)为接受方向                        for (i = 0; i < MBOX_MSG_LEN; i++)                            msg[i] = readl(mbox->base +MBOX_DATA_REG(mchan->slot) + i * 4);//读取数据                         mbox_chan_received_data(chan, (void *)msg);    //将数据交给数据接受函数进行处理                    }                     /* clear IRQ source */清中断                    writel(BIT(mchan->ack_irq), ACK_INT_CLR_REG(mbox->ipc));                    mbox_set_state(mbox, mchan->slot, MBOX_STATE_IDLE);                } 

6.2.2 数据接收流程数据接收是以中断的方式进行的。

hi6220_mbox_interrupt()        >>>for (i = 0; i < MBOX_MSG_LEN; i++) msg[i] = readl(mbox->base +MBOX_DATA_REG(mchan->slot) + i * 4);//读取数据        >>>mbox_chan_received_data(chan, (void *)msg);    //讲数据交给数据接受函数进行处理                >>>chan->cl->rx_callback(chan->cl, mssg); //上层client回调 

6.2.3 数据发送流程6.2.3.1 数据发送分为三种方式

中断方式,polling方式,ACK方式#define TXDONE_BY_IRQ    BIT(0) /* controller has remote RTR irq */#define TXDONE_BY_POLL    BIT(1) /* controller can read status of last TX */#define TXDONE_BY_ACK    BIT(2) /* S/W ACK recevied by Client ticks the TX */    

 

6.2.3.2 数据发送的基本流程

1 获取mailbox的channel

1)申请通道时,client对象指定了自己的需求和能力;2)在原子上下文中不能调用;3)通道在一个client占用之后,没有释放之前,不能被其它client使用;4)分配完成之后,任何此通道上接受的数据包将通过rx_callback传递到客户端5)使用完成后调用mbox_free_channel 释放channel,这样别人就可以继续使用这个channelvoid mbox_free_channel(struct mbox_chan *chan); /* may sleep */

struct mbox_chan *mbox_request_channel_byname(struct mbox_client *cl,const char *name);struct mbox_chan *mbox_request_channel(struct mbox_client *cl, int index); struct mbox_chan *mbox_request_channel(struct mbox_client *cl, int index)函数功能:通过dts中配置的channel index申请通道        >>>of_parse_phandle_with_args(dev->of_node, "mboxes", "#mbox-cells", index, &spec)        >>>根据dts中配置的channel index获得mbox_chan                list_for_each_entry(mbox, &mbox_cons, node)                    chan = mbox->of_xlate(mbox, &spec);        >>>初始化mbox                chan->msg_free = 0;                chan->msg_count = 0;                chan->active_req = NULL;                chan->cl = cl;                init_completion(&chan->tx_complete);        >>>启动通道                ret = chan->mbox->ops->startup(chan); struct mbox_chan *mbox_request_channel_byname(struct mbox_client *cl,const char *name);函数功能:通过channel name 申请通道,调用mbox_request_channel()函数来实现 

2 使用mbox_send_message给controller发送数据

函数功能:在client提交数据到controller进而发送到目标处理器的过程中。若client设置为tx_block,此函数调用只有在远程已经完成数据接受或tx_out(超时)才能返回;若client设置为非block模式下,client的每个请求将被此API进行缓存,并返回一个非负数;若client请求没有进入queue,将返回一个负数。无论失败,还是成功,API将调用tx_done;

int mbox_send_message(struct mbox_chan *chan, void *mssg)        >>>    t = add_to_rbuf(chan, mssg);    //将要发送的msg发暂存到对应通道的rbuf中        >>>msg_submit(chan);                >>>data = chan->msg_data[idx];                >>>chan->cl->tx_prepare(chan->cl, data);   //发到缓存                >>>err = chan->mbox->ops->send_data(chan, data);  //发送数据  hi6220_mbox_send_data,                        >>>mbox_set_state(mbox, slot, MBOX_STATE_TX);                        >>>if (mbox->tx_irq_mode)                                    mbox_set_mode(mbox, slot, MBOX_ACK_IRQ);                        >>>发送数据                                for (i = 0; i < MBOX_MSG_LEN; i++)                                    writel(buf[i], mbox->base + MBOX_DATA_REG(slot) + i * 4);                        >>>/* trigger remote request */                                writel(BIT(mchan->dst_irq), DST_INT_RAW_REG(mbox->ipc));                        等待中断。。。。。。。。。。 ------------------------------------------------------                        在中断服务函数中 发送完成中断到来----------->                        >>>hi6220_mbox_interrupt                                >>>mbox_chan_txdone(chan, 0);                                        >>>tx_tick(chan, r);                                                >>>msg_submit(chan);                                                >>>chan->cl->tx_done(chan->cl, mssg, r); ------------------------------------------------------                >>>若为polling方式下,启定时器                        hrtimer_start(&chan->mbox->poll_hrt, 0, HRTIMER_MODE_REL);                        >>>若为block形式的发送                >>>设置超时时间                        wait = msecs_to_jiffies(3600000);//永不超时                        wait = msecs_to_jiffies(chan->cl->tx_tout);                                        >>>阻塞等待超时或完成                        ret = wait_for_completion_timeout(&chan->tx_complete, wait);                >>>若超时时间到                        tx_tick(chan, t);                                >>>chan->cl->tx_done(chan->cl, mssg, r);                                >>>complete(&chan->tx_complete); 6.3 Mailbox Client驱动以mailbox-test为例说明。

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;};

 

6.3.1 Client 设备树相关的配置Controller---------- Required properties:- compatible : Should be "st,stih407-mailbox"- reg : Offset and length of the device's register set- mbox-name : Name of the mailbox- #mbox-cells: : Must be 2 <&phandle instance channel direction> phandle : Label name of controller instance : Instance number channel : Channel number Optional properties- interrupts : Contains the IRQ line for a Rx mailboxExample: mailbox0: mailbox@0 { compatible = "st,stih407-mailbox"; reg = <0x08f00000 0x1000>; interrupts = ; #mbox-cells = <2>; mbox-name = "a9";}; Client------ Required properties:- compatible : Many (See the client docs)- reg : Shared (between Application and Remote) memory address- mboxes : Standard property to specify a Mailbox (See ./mailbox.txt) Cells must match 'mbox-cells' (See Controller docs above) Optional properties- mbox-names : Name given to channels seen in the 'mboxes' property. Example: mailbox_test { compatible = "mailbox-test"; reg = <0x[shared_memory_address], [shared_memory_size]>; mboxes = <&mailbox2 0 1>, <&mailbox0 2 1>; mbox-names = "tx", "rx";};6.3.2 Client初始化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); 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); >>>size = resource_size(res); >>>tdev->tx_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"); >>> 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); //调用API申请通道 >>>platform_set_drvdata(pdev, tdev); >>>spin_lock_init(&tdev->lock); >>>tdev->rx_buffer = devm_kzalloc(&pdev->dev, MBOX_MAX_MSG_LEN, GFP_KERNEL); >>>ret = mbox_test_add_debugfs(pdev, tdev); >>>init_waitqueue_head(&tdev->waitq); static int mbox_test_remove(struct platform_device *pdev) >>> debugfs_remove_recursive(root_debugfs_dir); >>> mbox_free_channel(tdev->tx_channel); >>> mbox_free_channel(tdev->rx_channel); 

6.3.3 Client数据收发

数据接收(回调函数)static void mbox_test_receive_message(struct mbox_client *client, void *message) spin_lock_irqsave(&tdev->lock, flags); if (tdev->rx_mmio) { memcpy_fromio(tdev->rx_buffer, tdev->rx_mmio, MBOX_MAX_MSG_LEN); } else if (message) { memcpy(tdev->rx_buffer, message, MBOX_MAX_MSG_LEN); } mbox_data_ready = true; spin_unlock_irqrestore(&tdev->lock, flags); 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){ if (r) dev_warn(client->dev, "Client: Message could not be sent: %d\n", r); else dev_info(client->dev, "Client: Message sent\n");}  ————————————————版权声明:本文为CSDN博主「huofengfeihu」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。原文链接:https://blog.csdn.net/u010961173/article/details/96422441

查看原文