Linux Driver Tips

获取 ns 时间戳(已弃用)

1
2
3
4
5
6
7
#include <linux/timekeeping32.h>

struct timespec ts;
u64 ns;

getnstimeofday(&ts);
ns = timespec_to_ns(&ts);

ktime accessors — The Linux Kernel documentation

可以使用 ktime_get_ns() 替代。 如果要获取 us 时间会涉及到 64bit 除法问题,会遇到如下问题:

1
ERROR: modpost: "__aeabi_uldivmod" [common_drivers/drivers/usb/dwc_otg.ko] undefined!

调用 div_u64() 函数,需要 include <linux/math64.h>

1
u64	tm = div_u64(ktime_get_raw_ns(),1000);

Dump Stack

1
2
#include <asm/ptrace.h>
dump_stack();

Linux 中根据函数指针打印函数名

1
printk("func: %pS at address: %px\\n", func, func);

调整线程优先级

1
2
3
4
5
#include <uapi/linux/sched/types.h>

struct sched_param param = { .sched_priority = 99 };
thread_handle = kthread_run(thread_capture, audio_pcm, "dsp_cap");
sched_setscheduler(thread_handle, SCHED_FIFO, &param);

信号量

1
2
3
4
5
#include <linux/semaphore.h>

sema_init(&sem, 0);
up(&sem);
down_timeout(&sem, usecs_to_jiffies(sleep_us))

Timer

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
#include <linux/module.h>
#include<linux/timer.h>
MODULE_LICENSE("GPL");
struct tests{
int my_number;
struct timer_list my_timer
} my_data; //声明定时器全局变量

// 自定义定时器到期执行的函数,在此只有显示的功能,不做任何处理
void my_timer_function(struct timer_list *t) //这里参数有变化!
{
printk("In the my_timer_function\\n");
printk("the jiffies is :%ld\\n", jiffies); //显示当前的节拍数

struct tests *datas = from_timer(datas,t,my_timer);//在这里从 timer 找到包含其的结构体首地址!
printk("my number is :%d\\n", tests->my_number);
}

int __init setup_timer_init(void)
{
printk("my_timer will be created.\\n");
printk("the jiffies is :%ld\\n", jiffies); //显示当前的节拍数
my_data.my_timer.expires = jiffies + 1*HZ; //HZ=250,初始化字段 expires 的值

// 初始化定时器变量的 function 和 data 字段
timer_setup(&my_data.my_timer, my_timer_function, 0);//设置定时器
add_timer(&my_data.my_timer); //将定时器变量加入到合适的链表,激活定时器
printk("my_timer init.\\n");
return 0;
}

void __exit setup_timer_exit(void)
{
printk("Goodbye setup_timer\\n");
del_timer(&my_data.my_timer); //删除定时器变量
}

module_init(setup_timer_init);
module_exit(setup_timer_exit);

module param

module_param(insmod ko 文件时传递参数)

1
2
3
#define MY_MAJOR 0x09
static int global_val_test = MY_MAJOR;
module_param(global_val_test, int, 0644);

再编译模块后,再 insmod 加载模块时就可以传参数进去了,如:

1
2
3
4
5
6
[root@bogon hello_world]# insmod first_hello.ko global_val_test=5
[root@bogon hello_world]# tail /var/log/messages
May 26 14:20:08 localhost kernel: [63460.994397] global_val_test = 5
May 26 14:20:08 localhost kernel: [63460.994409] hello world enter
May 26 14:20:08 localhost kernel: global_val_test = 5
May 26 14:20:08 localhost kernel: hello world enter

同时,在模块目录下会生成 parameter 目录及参数文件,如下:

1
2
3
4
5
[root@bogon hello_world]# cat /sys/module/first_hello/
coresize holders/ initsize initstate notes/ parameters/ refcnt rhelversion sections/ srcversion taint uevent version
[root@bogon hello_world]# ls -alt /sys/module/first_hello/parameters/
total 0
-rw-r--r-- 1 root root 16384 May 26 14:54 global_val_test

MODULE_PARM_DESC

MODULE_PARM_DESC 是用来描述驱动模块的参数信息的。 例如驱动内有这么一行代码:MODULE_PARM_DESC(stacfgpath, "Get path of sta cfg"); 假设编译生成的驱动文件为 test.ko 我们使用:modinfo test.ko

1
2
3
4
5
6
7
8
9
#:modinfo test.ko
filename: /home/test/test.ko
license: GPL
srcversion:
depends:
retpoline: Y
name: test
vermagic: 4.15.0-129-generic SMP mod_unload
parm: stacfgpath:Get path of sta cfg (charp)

MODULE_PARM_DESC 内描述的信息就会在 parm 这一行参数显示出来,这样用户就知道要加载这个驱动可以传入哪些参数了。