一篇文章讲明白LinuxKernel编程

简介: 一篇文章讲明白LinuxKernel编程

转自:

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-(CONFIGMAILBOX)+=mailbox.oobj?(CONFIGMAILBOX)+=mailbox.oobj?(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/-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; //代码效果参考:http://www.zidongmutanji.com/zsjx/182891.html

// 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: 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 = , / IPC_S /

; / Mailbox /

interrupt-parent = ;

interrupts = ;

#mbox-cells = ;

};

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

#clock-cells = ;

mbox-names = "mbox-tx", "mbox-rx";

mboxes = , ;

};

Example:

kernel4.14/arch/arm64/boot/dts/hisilicon/hi6220.dtsi

mailbox: mailbox@f7510000 {

compatible = "hisilicon,hi6220-mbox";

reg = , / IPC_S /

; / Mailbox /

interrupt-parent = ;

interrupts = ;

#mbox-cells = ;

};

Example:

stub_clock: stub_clock {

compatible = "hisilicon,hi6220-stub-clk";

hisilicon,hi6220-clk-sram = ;

#clock-cells = ;

mbox-names = "mbox-tx", "mbox-rx";

mboxes = , ;

};

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 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);//读取数据

n

相关文章
|
5月前
|
Java 数据库连接
一篇文章讲明白Erlangpoolmanagement
一篇文章讲明白Erlangpoolmanagement
33 2
|
5月前
|
人工智能 数据挖掘 大数据
成为程序员后你都明白了什么?
成为程序员后你都明白了什么?
68 1
|
5月前
|
消息中间件 Linux API
一篇文章讲明白LinuxKernel编程
一篇文章讲明白LinuxKernel编程
48 0
|
5月前
|
JSON Java 测试技术
一篇文章讲明白JGit学习
一篇文章讲明白JGit学习
144 0
|
5月前
|
流计算 内存技术
一篇文章讲明白FreescaleKibbletest
一篇文章讲明白FreescaleKibbletest
25 0
|
5月前
|
人工智能 Java BI
一篇文章讲明白MartianAddition
一篇文章讲明白MartianAddition
27 0
|
5月前
|
JSON 网络协议 Shell
一文搞懂:【Day29】Soket编程
一文搞懂:【Day29】Soket编程
35 0
|
5月前
|
存储 Java API
一篇文章讲明白luauserdata
一篇文章讲明白luauserdata
185 0
|
5月前
|
druid 数据库
一篇文章讲明白HearthBuddy卡组
一篇文章讲明白HearthBuddy卡组
175 0
编程要搞明白的东西(一)
编程要搞明白的东西(一)
85 0