提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档
文章目录
前言
学习linux内核spi驱动,发现结构体中函数指针指来指去让人很绕,总是找不到调用函数,网上看到有大佬分享linux下trace可查看函数调用关系,故总结此文章,通过trace工具分析spi_sync()和spi_async()的调用过程。
spi驱动主要涉及文件如下:
spi-imx.c
spi-bitbang.c
spidev.c
一、linux下trace功能开启
1.1menuconfig操作
进入linux下menuconfig,tracers配置如下图所示:
配置完tracers后,debug filesystem是默认开启的,但是最好也检查一下,配置界面如下:
1.2编译内核
编译内核和设备树。
make
将新编译的内核和根文件系统添加入tftpboot目录下,重启正点原子IMX6ULL开发板。
二、trace使用步骤
2.1spi_sync同步模式调用关系查询
2.1.1spi驱动加载
spi驱动加载参考《【正点原子】I.MX6U嵌入式Linux驱动开发指南V1.6.pdf》下第62章节。
正点原子的spi驱动是使用的同步模式,调用的spi_sync()函数,具体代码如下:
static s32 icm20608_write_regs(struct icm20608_dev *dev, u8 reg, u8 *buf, u8 len)
{
int ret = -1;
unsigned char *txdata;
struct spi_message m;
struct spi_transfer *t;
struct spi_device *spi = (struct spi_device *)dev->private_data;
t = kzalloc(sizeof(struct spi_transfer), GFP_KERNEL); /* 申请内存 */
if(!t) {
return -ENOMEM;
}
txdata = kzalloc(sizeof(char)+len, GFP_KERNEL);
if(!txdata) {
goto out1;
}
/* 一共发送len+1个字节的数据,第一个字节为
寄存器首地址,len为要写入的寄存器的集合,*/
*txdata = reg & ~0x80; /* 写数据的时候首寄存器地址bit8要清零 */
memcpy(txdata+1, buf, len); /* 把len个寄存器拷贝到txdata里,等待发送 */
t->tx_buf = txdata; /* 要发送的数据 */
t->len = len+1; /* t->len=发送的长度+读取的长度 */
spi_message_init(&m); /* 初始化spi_message */
spi_message_add_tail(t, &m);/* 将spi_transfer添加到spi_message队列 */
ret = spi_sync(spi, &m); /* 同步发送 */
if(ret) {
goto out2;
}
out2:
kfree(txdata); /* 释放内存 */
out1:
kfree(t); /* 释放内存 */
return ret;
}
我们在板子串口输入如下命令加载驱动:
depmod //第一次加载驱动的时候需要运行此命令
modprobe icm20608.ko //加载驱动模块
2.1.2trace调用函数追踪
我们在板子串口输入如下命令:
mount -t debugfs none /sys/kernel/debug/
cd /sys/kernel/debug/tracing/
echo 0 > tracing_on
echo function_graph > current_tracer
//同步模式查询
echo spi_sync > set_graph_function
echo *spi* > set_ftrace_filter
echo *dma* >> set_ftrace_filter
echo *spin* >> set_ftrace_notrace
echo 1 > tracing_on
/lib/modules/4.1.15/icm20608App /dev/icm20608
echo 0 > tracing_on
cat trace
注意:
echo dma >> set_ftrace_filter 的>>表示在此文件中append添加,如果是>则是覆盖。
2.1.3spi_sync调用关系
trace中打印如下:
0) | spi_sync() {
0) | __spi_sync() {
0) 2.667 us | __spi_validate();
0) 1.667 us | __spi_queued_transfer();
0) | __spi_pump_messages() {
0) 1.667 us | spi_bitbang_prepare_hardware();
0) + 11.667 us | spi_imx_prepare_message();
0) 1.333 us | spi_imx_can_dma();
0) | spi_bitbang_transfer_one() {
0) | spi_imx_setupxfer() {
0) 4.000 us | mx51_ecspi_config();
0) + 11.000 us | }
0) 5.000 us | spi_imx_chipselect();
0) | spi_imx_transfer() {
0) | spi_imx_push() {
0) 1.000 us | spi_imx_buf_tx_u8();
0) 1.000 us | spi_imx_buf_tx_u8();
0) 1.000 us | spi_imx_buf_tx_u8();
0) 0.667 us | spi_imx_buf_tx_u8();
0) 0.667 us | spi_imx_buf_tx_u8();
0) 1.000 us | spi_imx_buf_tx_u8();
0) 1.000 us | spi_imx_buf_tx_u8();
0) 1.000 us | spi_imx_buf_tx_u8();
0) 0.667 us | spi_imx_buf_tx_u8();
0) 0.667 us | spi_imx_buf_tx_u8();
0) 1.000 us | spi_imx_buf_tx_u8();
0) 0.667 us | spi_imx_buf_tx_u8();
0) 1.000 us | spi_imx_buf_tx_u8();
0) 1.000 us | spi_imx_buf_tx_u8();
0) 1.000 us | spi_imx_buf_tx_u8();
0) 1.334 us | mx51_ecspi_trigger();
0) ! 106.000 us | }
0) 1.333 us | mx51_ecspi_intctrl();
0) ! 188.666 us | }
0) 3.334 us | spi_imx_chipselect();
0) | spi_finalize_current_message() {
0) 0.667 us | spi_imx_can_dma();
0) 7.000 us | spi_imx_unprepare_message();
0) 1.667 us | spi_complete();
0) + 69.000 us | }
0) ! 314.667 us | }
0) ! 356.334 us | }
这就是spi_sync()的整体调用关系,我们可根据上述函数学习spi驱动源码。
2.2spi_async异步模式调用关系查询
2.2.1spidev驱动加载
2.2.1.1源码学习
linux内核源码中spidev.c是调用的spi_async()函数,正好可以供我们分析,源码如下:
static ssize_t
spidev_sync(struct spidev_data *spidev, struct spi_message *message)
{
DECLARE_COMPLETION_ONSTACK(done);
int status;
message->complete = spidev_complete;
message->context = &done;
spin_lock_irq(&spidev->spi_lock);
if (spidev->spi == NULL)
status = -ESHUTDOWN;
else
status = spi_async(spidev->spi, message);
spin_unlock_irq(&spidev->spi_lock);
if (status == 0) {
wait_for_completion(&done);
status = message->status;
if (status == 0)
status = message->actual_length;
}
return status;
}
如果我们想将正点原子的icm20608.c里的spi同步发送改成异步发送,可以参考上述源码进行修改。
2.2.1.2menuconfig配置
进入menuconfig下,使能spidev功能,勾选下图黄框中功能。
2.2.1.3修改设备树
spidev.c源码中,compatible属性为"rohm,dh2228fv",如下所示,所以我们需要修改匹配值。
static const struct of_device_id spidev_dt_ids[] = {
{ .compatible = "rohm,dh2228fv" },
{},
};
基于正点原子第62章节的设备树,屏蔽spidev: icm20608@0 内容,添加spidevtest: test@0内容,具体修改为如下:
&ecspi3 {
fsl,spi-num-chipselects = <1>;
cs-gpios = <&gpio1 20 GPIO_ACTIVE_LOW>;
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_ecspi3>;
status = "okay";
spidevtest: test@0 {
compatible = "rohm,dh2228fv";
spi-max-frequency = <8000000>;
reg = <0>;
};
/*spidev: icm20608@0 {
compatible = "alientek,icm20608";
spi-max-frequency = <8000000>;
reg = <0>;
};*/
};
注意,compatible = “rohm,dh2228fv”;一定要内核和设备树对应上。
2.2.1.4编译源码和设备树
编译源码和设备树,并tftpboot重启开发板。
开发板串口下,spidev显示如下,表示驱动加载成功。
/dev # ls /dev/spi*
/dev/spidev2.0
/dev #
其中,前面的2代表的是&ecspi3(详见imx6ull.dtsi中spi2 = &ecspi3;),后面的0指代spidevtest: test@0。
/ {
aliases {
spi0 = &ecspi1;
spi1 = &ecspi2;
spi2 = &ecspi3;
spi3 = &ecspi4;
};
至此,我们spidev驱动配置完成。
2.2.2spi_async异步模式调用关系查询
下面的trace过程是基于修改正点原子icm20608.c源码的spi_sync为spi_async后进行的操作。
我们在板子串口输入如下命令:
mount -t debugfs none /sys/kernel/debug/
cd /sys/kernel/debug/tracing/
echo 0 > tracing_on
echo function_graph > current_tracer
//异步模式查询
echo spi_async > set_graph_function
echo *spi* > set_ftrace_filter
echo *dma* >> set_ftrace_filter
echo *spin* >> set_ftrace_notrace
echo 1 > tracing_on
/lib/modules/4.1.15/icm20608App /dev/icm20608
echo 0 > tracing_on
cat trace
2.2.3spi_sync调用关系
trace中打印如下:
0) | spi_async() {
0) 4.000 us | __spi_validate();
0) | __spi_async() {
0) | spi_queued_transfer() {
0) + 13.667 us | __spi_queued_transfer();
0) + 20.667 us | }
0) + 27.334 us | }
0) ! 117.000 us | }
上述打印中到__spi_queued_transfer()这一层就不再显示了,如果想看中断中的信息,需要将下属两句话在配置时去掉。
echo *spi* > set_ftrace_filter
echo *dma* >> set_ftrace_filter
去除掉spin后具体调用关系如下:
0) | spi_async() {
0) 2.667 us | __spi_validate();
0) 1.000 us | preempt_count_add();
0) | __spi_async() {
0) | spi_queued_transfer() {
0) | __spi_queued_transfer() {
0) 0.667 us | preempt_count_add();
0) | queue_kthread_work() {
0) 0.666 us | preempt_count_add();
0) | insert_kthread_work.part.0() {
0) | wake_up_process() {
0) | try_to_wake_up() {
0) 0.666 us | preempt_count_add();
0) 1.000 us | task_waking_fair();
0) | select_task_rq_fair() {
0) 0.667 us | __rcu_read_lock();
0) 0.666 us | idle_cpu();
0) 0.667 us | __rcu_read_unlock();
0) + 20.000 us | }
0) 0.666 us | preempt_count_add();
0) | ttwu_do_activate.constprop.28() {
0) | activate_task() {
0) | enqueue_task() {
0) 1.000 us | update_rq_clock.part.16();
0) | enqueue_task_fair() {
0) | update_curr() {
0) 2.334 us | update_min_vruntime();
0) 8.667 us | }
0) 1.000 us | __compute_runnable_contrib();
0) 1.000 us | __update_entity_load_avg_contrib();
0) 1.000 us | __update_entity_utilization_avg_contrib();
0) 1.000 us | update_cfs_rq_blocked_load();
0) 1.333 us | __enqueue_entity();
0) 0.667 us | hrtick_update();
0) + 56.334 us | }
0) + 68.333 us | }
0) + 73.667 us | }
0) | ttwu_do_wakeup() {
0) | check_preempt_curr() {
0) | check_preempt_wakeup() {
0) 0.667 us | update_curr();
0) 1.000 us | wakeup_preempt_entity();
0) 0.667 us | resched_curr();
0) + 19.333 us | }
0) + 26.667 us | }
0) + 33.667 us | }
0) ! 120.000 us | }
0) 0.666 us | preempt_count_sub();
0) 0.666 us | preempt_count_sub();
0) ! 183.667 us | }
0) ! 189.334 us | }
0) ! 195.333 us | }
0) 0.667 us | preempt_count_sub();
0) ! 215.000 us | }
0) 0.667 us | preempt_count_sub();
0) ! 235.333 us | }
0) ! 240.666 us | }
0) ! 246.667 us | }
0) | preempt_count_sub() {
0) | __schedule() {
0) 0.666 us | preempt_count_add();
0) | rcu_note_context_switch() {
0) 0.667 us | rcu_sched_qs();
0) 0.667 us | rcu_preempt_qs();
0) + 12.667 us | }
0) 0.667 us | preempt_count_add();
0) | pick_next_task_fair() {
0) | put_prev_task_fair() {
0) 0.666 us | update_curr();
0) 0.666 us | __enqueue_entity();
0) 0.666 us | __compute_runnable_contrib();
0) 1.000 us | __update_entity_load_avg_contrib();
0) 0.667 us | __update_entity_utilization_avg_contrib();
0) + 33.334 us | }
0) 1.000 us | wakeup_preempt_entity();
0) 0.667 us | preempt_count_sub();
0) ! 215.000 us | } /* queue_kthread_work */
0) 0.667 us | preempt_count_sub();
0) ! 235.333 us | } /* __spi_queued_transfer */
0) ! 240.666 us | } /* spi_queued_transfer */
0) ! 246.667 us | } /* __spi_async */
0) | preempt_count_sub() {
0) | __schedule() {
0) 0.666 us | preempt_count_add();
0) | rcu_note_context_switch() {
0) 0.667 us | rcu_sched_qs();
0) 0.667 us | rcu_preempt_qs();
0) + 12.667 us | }
0) 0.667 us | preempt_count_add();
0) | pick_next_task_fair() {
0) | put_prev_task_fair() {
0) 0.666 us | update_curr();
0) 0.666 us | __enqueue_entity();
0) 0.666 us | __compute_runnable_contrib();
0) 1.000 us | __update_entity_load_avg_contrib();
0) 0.667 us | __update_entity_utilization_avg_contrib();
0) + 33.334 us | }
0) 1.000 us | wakeup_preempt_entity();
0) 1.000 us | clear_buddies();
0) 1.333 us | set_next_entity();
0) + 58.000 us | }
0) | atomic_notifier_call_chain() {
0) 0.667 us | __rcu_read_lock();
0) | notifier_call_chain() {
0) 0.667 us | vfp_notifier();
0) 6.667 us | }
0) 0.667 us | __rcu_read_unlock();
0) + 25.667 us | }
0) | finish_task_switch() {
0) ==========> |
0) | gic_handle_irq() {
0) | __handle_domain_irq() {
0) | irq_enter() {
0) 1.000 us | rcu_irq_enter();
0) 0.667 us | preempt_count_add();
0) + 12.334 us | }
0) 2.333 us | irq_find_mapping();
0) | generic_handle_irq() {
0) | handle_fasteoi_irq() {
0) 0.667 us | preempt_count_add();
0) 0.667 us | irq_may_run();
0) | handle_irq_event() {
0) 0.667 us | preempt_count_sub();
0) | handle_irq_event_percpu() {
0) | spi_imx_isr() {
0) 1.000 us | mx51_ecspi_rx_available();
0) 1.000 us | spi_imx_buf_rx_u8();
0) 1.000 us | mx51_ecspi_rx_available();
0) 1.000 us | spi_imx_buf_rx_u8();
0) 1.000 us | mx51_ecspi_rx_available();
0) 1.000 us | spi_imx_buf_rx_u8();
0) 1.000 us | mx51_ecspi_rx_available();
0) 1.000 us | spi_imx_buf_rx_u8();
0) 1.000 us | mx51_ecspi_rx_available();
0) 1.000 us | spi_imx_buf_rx_u8();
0) 1.000 us | mx51_ecspi_rx_available();
0) 1.000 us | spi_imx_buf_rx_u8();
0) 1.000 us | mx51_ecspi_rx_available();
0) 1.000 us | spi_imx_buf_rx_u8();
0) 0.667 us | mx51_ecspi_rx_available();
0) 1.000 us | spi_imx_buf_rx_u8();
0) 1.000 us | mx51_ecspi_rx_available();
0) 1.000 us | spi_imx_buf_rx_u8();
0) 0.667 us | mx51_ecspi_rx_available();
0) 0.667 us | spi_imx_buf_rx_u8();
0) 1.000 us | mx51_ecspi_rx_available();
0) 1.000 us | spi_imx_buf_rx_u8();
0) 1.000 us | mx51_ecspi_rx_available();
0) 1.000 us | spi_imx_buf_rx_u8();
0) 1.000 us | } /* mx51_ecspi_rx_available */
0) 1.000 us | spi_imx_buf_rx_u8();
0) 1.000 us | mx51_ecspi_rx_available();
0) 1.000 us | spi_imx_buf_rx_u8();
0) 1.000 us | mx51_ecspi_rx_available();
0) 1.000 us | spi_imx_buf_rx_u8();
0) 1.000 us | mx51_ecspi_rx_available();
0) 1.000 us | spi_imx_buf_rx_u8();
0) 1.000 us | mx51_ecspi_rx_available();
0) 1.000 us | spi_imx_buf_rx_u8();
0) 1.000 us | mx51_ecspi_rx_available();
0) 1.000 us | spi_imx_buf_rx_u8();
0) 1.000 us | mx51_ecspi_rx_available();
0) 1.000 us | spi_imx_buf_rx_u8();
0) 0.667 us | mx51_ecspi_rx_available();
0) 1.000 us | spi_imx_buf_rx_u8();
0) 1.000 us | mx51_ecspi_rx_available();
0) 1.000 us | spi_imx_buf_rx_u8();
0) 0.667 us | mx51_ecspi_rx_available();
0) 0.667 us | spi_imx_buf_rx_u8();
0) 1.000 us | mx51_ecspi_rx_available();
0) 1.000 us | spi_imx_buf_rx_u8();
0) 1.000 us | mx51_ecspi_rx_available();
0) 1.000 us | spi_imx_buf_rx_u8();
0) 1.000 us | mx51_ecspi_rx_available();
0) 1.000 us | spi_imx_buf_rx_u8();
0) 1.000 us | mx51_ecspi_rx_available();
0) 0.667 us | spi_imx_buf_rx_u8();
0) 1.000 us | mx51_ecspi_rx_available();
0) 1.000 us | spi_imx_buf_rx_u8();
0) 1.000 us | mx51_ecspi_rx_available();
0) 1.000 us | mx51_ecspi_intctrl();
0) | complete() {
0) 1.000 us | preempt_count_add();
0) | __wake_up_locked() {
0) | __wake_up_common() {
0) | default_wake_function() {
0) | try_to_wake_up() {
0) 1.000 us | preempt_count_add();
0) 1.000 us | task_waking_fair();
0) | select_task_rq_fair() {
0) 0.666 us | __rcu_read_lock();
0) 1.000 us | idle_cpu();
0) 0.667 us | __rcu_read_unlock();
0) + 22.000 us | }
0) 0.667 us | preempt_count_add();
0) | ttwu_do_activate.constprop.28() {
0) | activate_task() {
0) | enqueue_task() {
0) 1.000 us | update_rq_clock.part.16();
0) | enqueue_task_fair() {
0) | update_curr() {
0) 0.666 us | update_min_vruntime();
0) 9.334 us | }
0) 0.667 us | update_cfs_rq_blocked_load();
0) 1.000 us | __enqueue_entity();
0) 0.666 us | hrtick_update();
0) + 38.667 us | }
0) + 54.333 us | }
0) 1.000 us | mx51_ecspi_rx_available();
0) 1.000 us | mx51_ecspi_intctrl();
0) | complete() {
0) 1.000 us | preempt_count_add();
0) | __wake_up_locked() {
0) | __wake_up_common() {
0) | default_wake_function() {
0) | try_to_wake_up() {
0) 1.000 us | preempt_count_add();
0) 1.000 us | task_waking_fair();
0) | select_task_rq_fair() {
0) 0.666 us | __rcu_read_lock();
0) 1.000 us | idle_cpu();
0) 0.667 us | __rcu_read_unlock();
0) + 22.000 us | }
0) 0.667 us | preempt_count_add();
0) | ttwu_do_activate.constprop.28() {
0) | activate_task() {
0) | enqueue_task() {
0) 1.000 us | update_rq_clock.part.16();
0) | enqueue_task_fair() {
0) | update_curr() {
0) 0.666 us | update_min_vruntime();
0) 9.334 us | }
0) 0.667 us | update_cfs_rq_blocked_load();
0) 1.000 us | __enqueue_entity();
0) 0.666 us | hrtick_update();
0) + 38.667 us | }
0) + 54.333 us | }
0) + 61.667 us | }
0) | ttwu_do_wakeup() {
0) | check_preempt_curr() {
0) | check_preempt_wakeup() {
0) 0.667 us | update_curr();
0) 0.666 us | wakeup_preempt_entity();
0) 0.667 us | resched_curr();
0) + 23.000 us | }
0) + 30.333 us | }
0) + 38.000 us | }
0) ! 112.000 us | }
0) 0.667 us | preempt_count_sub();
0) 0.667 us | preempt_count_sub();
0) ! 184.667 us | }
0) ! 193.334 us | }
0) ! 199.333 us | }
0) ! 206.000 us | }
0) 0.667 us | preempt_count_sub();
0) ! 226.333 us | }
0) ! 435.334 us | }
0) | add_interrupt_randomness() {
0) | read_current_timer() {
0) 1.000 us | imx_read_current_timer();
0) 8.000 us | }
0) + 14.667 us | }
0) 0.667 us | note_interrupt();
0) ! 469.334 us | }
0) 0.667 us | preempt_count_add();
0) ! 491.333 us | }
0) | irq_chip_eoi_parent() {
0) 0.667 us | gic_eoi_irq();
0) + 12.000 us | }
0) 2.000 us | preempt_count_sub();
0) ! 193.334 us | } /* default_wake_function */
0) ! 199.333 us | } /* __wake_up_common */
0) ! 206.000 us | } /* __wake_up_locked */
0) 0.667 us | preempt_count_sub();
0) ! 226.333 us | } /* complete */
0) ! 435.334 us | } /* spi_imx_isr */
0) | add_interrupt_randomness() {
0) | read_current_timer() {
0) 1.000 us | imx_read_current_timer();
0) 8.000 us | }
0) + 14.667 us | }
0) 0.667 us | note_interrupt();
0) ! 469.334 us | } /* handle_irq_event_percpu */
0) 0.667 us | preempt_count_add();
0) ! 491.333 us | } /* handle_irq_event */
0) | irq_chip_eoi_parent() {
0) 0.667 us | gic_eoi_irq();
0) + 12.000 us | }
0) 2.000 us | preempt_count_sub();
0) ! 534.666 us | }
0) ! 541.333 us | }
0) | irq_exit() {
0) 0.667 us | preempt_count_sub();
0) 1.000 us | idle_cpu();
0) 1.000 us | rcu_irq_exit();
0) + 22.667 us | }
0) ! 603.333 us | }
0) ! 610.667 us | }
0) <========== |
0) 1.000 us | preempt_count_sub();
0) ! 625.667 us | }
0) 0.666 us | preempt_count_sub();
0) ! 874.000 us | }
0) | __schedule() {
0) 0.667 us | preempt_count_add();
0) | rcu_note_context_switch() {
0) 1.000 us | rcu_sched_qs();
0) 1.000 us | rcu_preempt_qs();
0) + 15.000 us | }
0) 0.667 us | preempt_count_add();
0) | pick_next_task_fair() {
0) | put_prev_task_fair() {
0) 0.667 us | update_curr();
0) 1.000 us | __enqueue_entity();
0) + 15.000 us | }
0) 0.667 us | wakeup_preempt_entity();
0) 0.667 us | clear_buddies();
0) 2.334 us | set_next_entity();
0) + 43.666 us | }
0) | atomic_notifier_call_chain() {
0) 0.666 us | __rcu_read_lock();
0) | notifier_call_chain() {
0) 1.000 us | vfp_notifier();
0) 7.666 us | }
0) 0.667 us | __rcu_read_unlock();
0) + 27.333 us | }
0) | finish_task_switch() {
0) 1.000 us | preempt_count_sub();
0) 8.666 us | }
0) 0.666 us | preempt_count_sub();
0) ! 247.333 us | }
0) 1.000 us | }
0) # 1413.666 us | }
总结
上述内容就是我基于正点原子imx6ull开发板研究linux内核spi驱动调用关系的分析方法,可以类推查看i2c、usart等驱动。
本文参考文档如下:
trace参考:
https://blog.csdn.net/u013836909/article/details/129894966?spm=1001.2014.3001.5506
https://mp.weixin.qq.com/s/thJRf7QFt0gK_4F2jWCW6g
spidev参考:
https://blog.csdn.net/yyz_1987/article/details/131918983?spm=1001.2014.3001.5506