5_编写一个Linux驱动模板

编写最简单的驱动模板

Posted by Jerry Chen on June 18, 2019

准备工作

  • 查看总线的命令: ls /sys/bus

  • 查看注册到虚拟总线的设备的命令: ls /sys/devices/platform

  • 查看设备号(主设备号)的命令: cat /proc/devices

  • 查看杂项设备(主设备号10)设备号的命令: cat /proc/misc

注:/proc 文件系统是一种内核和内核模块用来向进程(process) 发送信息的机制(所以叫做/proc)。

注册设备信息

关注的信息

关注一个结构体:platform_device(…/include/linux/platform_device.h文件中有定义)

结构体关键元素:name,驱动注册时会和驱动name进行匹配

结构体关键元素:id,如果是-1则注册到“/sys/devices/platform”中的就是name,如果非-1,比如0则注册的结果如hello.0,比如1则注册的结果如hello.1

注册hello设备信息

接下来在platform总线中注册“hello”设备信息,直接改平台文件以添加到内核的方式。

  1. 打开目标平台文件mach-itop4412.c(地址是:.../arch/arm/mach-exynos/mach-itop4412.c

  2. 模仿LEDS设备结构体变量声明进行编写。name写为”hello”,id这里故意写出-2,看接下来的效果

  3. 然后将声明的结构体变量的地址加入到注册用的设备遍历的结构体指针数组smdk4x12_devices中。该数组是platform_add_devices()函数的一个传参。

  4. 加入相应位置Kconfig文件的CONFIG_HELLO,进行make menuconfig使能配置,然后编译make zImage

    一般Kconfig文件和驱动在同一目录,比如itop4412_leds.c在目录.../drivers/char/

最后的结果是设备注册成功,在“/sys/devices/platform”下是预期的设备名“hello.-2”

注册驱动

流程是:首先驱动Init把驱动注册总线上,总线驱动会扫描总线上的设备,看是否有设备与这个驱动匹配,如果匹配就调用Probe接口。

注意一个结构体:platform_driver(在…/include/linux/platform_device.h有定义)

结构体关键元素:probe,和设备name匹配了才会调用驱动probe函数

结构体关键元素:结构体driver,包含子元素name(用于和设备name匹配),以及子元素owner(一般直接等于THIS_MODULE)

模板hello_driver.c

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
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
#include <linux/init.h>
#include <linux/module.h>
#include <linux/platform_device.h>

MODULE_LICENSE("Dual BSD/GPL");
MODULE_AUTHOR("jerry");

#define DRIVER_NAME "hello" // 只和设备结构体中name匹配,不管设备结构体id

static int hello_probe(struct platform_device *pdv){
	printk(KERN_EMERG "hello_probe ok!\n");
	return 0;
}
static int hello_remove(struct platform_device *pdv){
	printk(KERN_EMERG "hello_remove ok!\n");
	return 0;
}
static void hello_shutdown(struct platform_device *pdv){
	printk(KERN_EMERG "hello_shutdown ok!\n");
}
static int hello_suspend(struct platform_device *pdv, pm_message_t state){
	printk(KERN_EMERG "hello_suspend ok!\n");
	return 0;
}
static int hello_resume(struct platform_device *pdv){
	printk(KERN_EMERG "hello_resume ok!\n");
	return 0;
}

static struct platform_driver hello_driver = {
	.probe = hello_probe,
	.remove = hello_remove,
	.shutdown = hello_shutdown,
	.suspend = hello_suspend,
	.resume = hello_resume,
	.driver = {
		.name = DRIVER_NAME,
		.owner = THIS_MODULE,
	},
};

static int hello_init(void){
	int DriverState;
	printk(KERN_EMERG "hello_init ok!\n");
	
	DriverState = platform_driver_register(&hello_driver);
	printk(KERN_EMERG "Driver registration return value is %d\n",DriverState);
	return 0;
}

static void hello_exit(void){
	printk(KERN_EMERG "hello_exit ok!\n");
	platform_driver_unregister(&hello_driver);
}

module_init(hello_init);
module_exit(hello_exit);

模板Makefile

使用Makefile进行编译前,先确定内核的编译工具 CROSS_COMPILE 已经 配置为交叉编译工具

1
2
3
4
5
6
7
8
9
10
11
12
13
14
#!/bin/bash

obj-m += probe_linux_module.o

KDIR := /home/jerry/Projects/iTop4412_Kernel_3.0

PWD ?= $(shell pwd)

all:
	make -C $(KDIR) M=$(PWD) modules

clean:
	rm -rf *.o

执行结果

name匹配的情况:

下面是name不匹配时的情况,可以看到驱动并未执行probe和remove函数: