一、前言
驱动程序是外界硬件设备、操作系统、用户之间通信的桥梁。
驱动程序基本上都是使用C语言完成,且处于内核空间。
现代Linux驱动程序基本都支持动态装载。
硬件设备主要分为:字符设备、块设备、网络设备,其驱动程序略有差别。
学习参考网址:
https://embetronicx.com/tutorials/linux/
二、第一个驱动程序
首先编写如下C程序。hello_world.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
#include<linux/kernel.h>
#include<linux/init.h>
#include<linux/module.h>
/*
** Module Init function
*/
static int __init hello_world_init(void)
{
printk(KERN_INFO "Welcome to EmbeTronicX\n");
printk(KERN_INFO "This is the Simple Module\n");
printk(KERN_INFO "Kernel Module Inserted Successfully...\n");
return 0;
}
/*
** Module Exit function
*/
static void __exit hello_world_exit(void)
{
printk(KERN_INFO "Kernel Module Removed Successfully...\n");
}
module_init(hello_world_init);
module_exit(hello_world_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("author");
MODULE_DESCRIPTION("Hello world driver");
MODULE_VERSION("1:1.0");
然后进行编译:
1
2
3
4
5
6
7
8
9
10
11
obj-m += hello_world.o
KDIR = /lib/modules/$(shell uname -r)/build
all:
make -C $(KDIR) M=$(shell pwd) modules
clean:
make -C $(KDIR) M=$(shell pwd) clean
随后进行驱动程序的加载:
1
sudo insmod hello_world_module.ko
查看驱动详细信息:
1
modinfo hello_world_module.ko
卸载:
1
sudo rmmod hello_world_module
三、传递参数
在驱动程序中,如果需要进行参数的定义,不仅要先像C语言一样声明变量、赋值等,还需要用到如下一些函数进行初始化或回调,如:
1
2
3
module_param() //定义一个变量
module_param_array() //定义一个数组
module_param_cb() //如果变量被更改之后进行回调
例程为:hello_world.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
59
60
61
62
63
#include<linux/kernel.h>
#include<linux/init.h>
#include<linux/module.h>
#include<linux/moduleparam.h>
int valueETX, arr_valueETX[4];
char *nameETX;
int cb_valueETX = 0;
module_param(valueETX, int, S_IRUSR|S_IWUSR); //integer value
module_param(nameETX, charp, S_IRUSR|S_IWUSR); //String
module_param_array(arr_valueETX, int, NULL, S_IRUSR|S_IWUSR); //Array of integers
/*----------------------Module_param_cb()--------------------------------*/
int notify_param(const char *val, const struct kernel_param *kp)
{
int res = param_set_int(val, kp); // Use helper for write variable
if(res==0) {
printk(KERN_INFO "Call back function called...\n");
printk(KERN_INFO "New value of cb_valueETX = %d\n", cb_valueETX);
return 0;
}
return -1;
}
const struct kernel_param_ops my_param_ops =
{
.set = ¬ify_param, // Use our setter ...
.get = ¶m_get_int, // .. and standard getter
};
module_param_cb(cb_valueETX, &my_param_ops, &cb_valueETX, S_IRUGO|S_IWUSR );
/*-------------------------------------------------------------------------*/
/*
** Module init function
*/
static int __init hello_world_init(void)
{
int i;
printk(KERN_INFO "ValueETX = %d \n", valueETX);
printk(KERN_INFO "cb_valueETX = %d \n", cb_valueETX);
printk(KERN_INFO "NameETX = %s \n", nameETX);
for (i = 0; i < (sizeof arr_valueETX / sizeof (int)); i++) {
printk(KERN_INFO "Arr_value[%d] = %d\n", i, arr_valueETX[i]);
}
printk(KERN_INFO "Kernel Module Inserted Successfully...\n");
return 0;
}
/*
** Module Exit function
*/
static void __exit hello_world_exit(void)
{
printk(KERN_INFO "Kernel Module Removed Successfully...\n");
}
module_init(hello_world_init);
module_exit(hello_world_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Author");
MODULE_DESCRIPTION("Passing parameters to my driver");
MODULE_VERSION("1.0");
然后进行编译:
1
2
3
4
5
6
7
8
9
obj-m += hello_world_module.o
KDIR = /lib/modules/$(shell uname -r)/build
all:
make -C $(KDIR) M=$(shell pwd) modules
clean:
make -C $(KDIR) M=$(shell pwd) clean
并在之后进行驱动程序的加载和变量初始化赋值:
1
sudo insmod hello_world_module.ko valueETX=14 nameETX="EmbeTronicX" arr_valueETX=100,102,104,106
此时如果需要更改变量值,则需要输入如下命令:
1
sudo sh -c "echo 13 > /sys/module/hello_world_module/parameters/cb_valueETX"
因为我们定义了回到回调函数notify_param(),所以可以得到输出:
1
2
Call back function called...
New value of cb_valueETX = 13
最后进行驱动程序的卸载:
1
sudo rmmod hello_world_module
四、设备驱动程序的主次数字
主数字和次数字:Major Number and Minor Number
主数字代表什么类型的设备,次数字代表设备号。
其可分为静态分配和动态分配两种方式:
静态分配:
如定义一个主数字为235,次数字为0的设备。要求的连续设备编号的总数为1。设备描述为MY_Dev。
//函数原型
int register_chrdev_region(dev_t first, unsigned int count, char *name);//成功返回0
//下面是一个例子
dev_t dev = MKDEV(235, 0);
register_chrdev_region(dev, 1, "MY_Dev");
动态分配:
//函数原型
int alloc_chrdev_region(dev_t *dev, unsigned int firstminor, unsigned int count, char *name);
由于我们基本上不需要事先了解主数字编号,故通常使用动态分配,这将由系统自动进行分配。
释放:
1
void unregister_chrdev_region(dev_t first, unsigned int count);
静态分配的例程如下:hello_major_minor_static.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
#include<linux/kernel.h>
#include<linux/init.h>
#include<linux/module.h>
#include <linux/fs.h>
//creating the dev with our custom major and minor number
dev_t dev = MKDEV(235, 0);
/*
** Module Init function
*/
static int __init hello_world_init(void)
{
register_chrdev_region(dev, 1, "MY_Dev");
printk(KERN_INFO "Major = %d Minor = %d \n",MAJOR(dev), MINOR(dev));
printk(KERN_INFO "Kernel Module Inserted Successfully...\n");
return 0;
}
/*
** Module exit function
*/
static void __exit hello_world_exit(void)
{
unregister_chrdev_region(dev, 1);
printk(KERN_INFO "Kernel Module Removed Successfully...\n");
}
module_init(hello_world_init);
module_exit(hello_world_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Autuor");
MODULE_DESCRIPTION("Major and Minor number test static");
MODULE_VERSION("1:1.0");
动态分配的例程如下:hello_major_minor_dynamic.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
#include<linux/kernel.h>
#include<linux/init.h>
#include<linux/module.h>
#include<linux/kdev_t.h>
#include<linux/fs.h>
dev_t dev = 0;
/*
** Module Init function
*/
static int __init hello_world_init(void)
{
/*Allocating Major number*/
if((alloc_chrdev_region(&dev, 0, 1, "MY_Dev")) <0){
printk(KERN_INFO "Cannot allocate major number for device 1\n");
return -1;
}
printk(KERN_INFO "Major = %d Minor = %d \n",MAJOR(dev), MINOR(dev));
printk(KERN_INFO "Kernel Module Inserted Successfully...\n");
return 0;
}
/*
** Module exit function
*/
static void __exit hello_world_exit(void)
{
unregister_chrdev_region(dev, 1);
printk(KERN_INFO "Kernel Module Removed Successfully...\n");
}
module_init(hello_world_init);
module_exit(hello_world_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Autuor");
MODULE_DESCRIPTION("Major and Minor number test dynamic");
MODULE_VERSION("1:1.0");
利用如下代码可以查看输出信息:
1
cat /proc/devices | grep "MY_Dev"