zoukankan      html  css  js  c++  java
  • Linux Kernel编程 --- Mailbox驱动框架【转】

    转自: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.o
    obj-$(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的channel
    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);

    使用完成后调用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/<vendor>-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 channel
    is unidirectional with a maximum message size of 8 words. I/O is
    performed using register access (there is no DMA) and the cell
    raises 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 = <GIC_SPI 94 IRQ_TYPE_LEVEL_HIGH>;
            #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 = <GIC_SPI 94 IRQ_TYPE_LEVEL_HIGH>;
            #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>;   <phandle channel_index dst_irq ack_irq>
        };

    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 ",
                                 __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,这样别人就可以继续使用这个channel
    void 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 mailbox
    Example:

    mailbox0: mailbox@0 {
    compatible = "st,stih407-mailbox";
    reg = <0x08f00000 0x1000>;
    interrupts = <GIC_SPI 1 IRQ_TYPE_NONE>;
    #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 ", r);
    else
    dev_info(client->dev,
    "Client: Message sent ");
    }

     
    ————————————————
    版权声明:本文为CSDN博主「huofengfeihu」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
    原文链接:https://blog.csdn.net/u010961173/article/details/96422441

    【作者】张昺华
    【大饼教你学系列】https://edu.csdn.net/course/detail/10393
    【新浪微博】 张昺华--sky
    【twitter】 @sky2030_
    【微信公众号】 张昺华
    本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利.
  • 相关阅读:
    打造系统分析师核心技能
    需求输出---一个创业团队需求卡壳的故事
    Nodejs前端服务器压缩图片
    node使用buffer生成图片
    NodeJS 常用模块积累
    ImageMagick图片服务器
    linux安装杀软 clamAV
    等保2.0与等保1.0的变化
    负载均衡的原理、分类、实现架构,以及使用场景
    等保2.0 三级基线要求项
  • 原文地址:https://www.cnblogs.com/sky-heaven/p/13357337.html
Copyright © 2011-2022 走看看