Linux驱动

内容

​ 1、内核模块框架

​ 2、一个完完整驱动的组成

​ 3、led设备驱动

准备工作:

1、将linux源码拷贝一份到Windows下

2、在Windows下的linux源码目录下新建一个 source insight 文件夹

3、打开source insight 软件,新建一个工程,工程保存到刚新建的source insight 文件夹下

4、将Linux源码导入到source insight的工程中,并同步
同步方法如下:
在右边project文件栏下按鼠标右键-》点击Synchronize files-》勾选force all files to be re-parsed-》点击start

一)内核模块框架

1》内核模块组成

​ 1、在fs_mp157a/driver/2308/1day/新建一个drv_hello.c 文件
​ 2、头文件
​ #include <linux/init.h>
​ #include <linux/module.h>
​ 3、加载函数——模块加载后运行的第一个函数
​ static int __init drv_hello_init(void)//加载函数
​ {
​ int ret=0;
​ printk(“—%s—\r\n”,FUNCTION);
​ return ret;
​ }
​ 4、卸载函数——模块卸载时,会调用这个函数
​ static void __exit drv_hello_exit(void)//卸载函数
​ {
​ printk(“—%s—\r\n”,FUNCTION);
​ }
​ 5、函数声明和认证
​ module_init(drv_hello_init);
​ module_exit(drv_hello_exit);
​ MODULE_LICENSE(“GPL”);

2》编写Makefile文件

​ //指定内核源码路径

​ KERNEL_DIR = /home/linux/fs_mp157a/kernel/stm32mp1-openstlinux-5.4-dunfell-mp1-20-06-24/sources/arm-ostl-linux-gnueabi/linux-stm32mp-5.4.31-r0/linux-5.4.31

​ CUR_DIR = ${shell pwd}

​ all:

​ //将当前目录下的源码和内核源码一起编译,编译成.ko文件

​ make -C $(KERNEL_DIR) M=$(CUR_DIR) modules

​ clean:

​ //清除将上面编译生成的文件

​ make -C $(KERNEL_DIR) M=$(CUR_DIR) clean

​ install:

​ //将当前目录下的所有.ko文件拷贝到根文件系统中

​ sudo cp *.ko /opt/rootfs/drivers

​ //指定要编译的源文件

​ obj-m = drv_hello.o

3》编译 make

​ make -C /home/linux/fs_mp157a/kernel/stm32mp1-openstlinux-5.4-dunfell-mp1-20-06-24/sources/arm-ostl-linux-gnueabi/linux-stm32mp-5.4.31-r0/linux-5.4.31 M=/home/linux/fs_mp157a/drivers/2308/1day modules

​ make[1]: Entering directory ‘/home/linux/fs_mp157a/kernel/stm32mp1-openstlinux-5.4-dunfell-mp1-20-06-24/sources/arm-ostl-linux-gnueabi/linux-stm32mp-5.4.31-r0/linux-5.4.31’

​ CC [M] /home/linux/fs_mp157a/drivers/2308/1day/drv_hello.o

​ Building modules, stage 2.

​ MODPOST 1 modules

​ CC [M] /home/linux/fs_mp157a/drivers/2308/1day/drv_hello.mod.o

​ LD [M] /home/linux/fs_mp157a/drivers/2308/1day/drv_hello.ko

​ make[1]: Leaving directory ‘/home/linux/fs_mp157a/kernel/stm32mp1-openstlinux-5.4-dunfell-mp1-20-06-24/sources/arm-ostl-linux-gnueabi/linux-stm32mp-5.4.31-r0/linux-5.4.31’

4》加载内核模块

​ 1、在/opt/rootfs/下新建一个drivers文件夹

​ 2、执行 make install 将.ko文件拷贝到根文件系统中

​ 3、打开开发板,加载内核模块 (insmod 加载函数,rmmod 卸载函数,lsmod 查看)

​ [root@fsmp1a ]# cd drivers/ //进入模块文件夹

​ [root@fsmp1a drivers]# ls //查看文件夹内容

​ drv_hello.ko

​ [root@fsmp1a drivers]# insmod drv_hello.ko//加载内核模块

​ [ 8213.458054] —drv_hello_init—

​ [root@fsmp1a drivers]# lsmod//查看系统中加载的模块

​ drv_hello 16384 0 - Live 0xbf000000 (OE)

​ [root@fsmp1a drivers]# rmmod drv_hello.ko//卸载已经加载的模块

​ [ 8338.518719] —drv_hello_exit—

(二)一个完整驱动的组成

1》设备号

1、设备号的概念

​ 是用一个32位的正数表示,分成两部分: 主设备号和次设备号

​ 主设备号:用32位正数的高12位表示,表示一类设备(一类设备)

​ 次设备号:用32位正数的低20位表示,表示具体的设备编号(具体的某一个)

2、申请设备号:

​ static inline int register_chrdev(unsigned int major, const char *name,const struct file_operations *fops)

​ //参数1 申请设备号的方式:major=0,表示动态申请主设备号

​ major>0,表示静态指定一个主设备号

​ //参数2 字符串,表示描述信息,自定义

​ //参数3 struct file_operations结构体指针

​ //返回指 如果参数1为0,成功返回主设备号,失败返回错误码

​ 如果参数1不为0,成功返回0,失败返回错误码

static inline void unregister_chrdev(unsigned int major, const char *name) // 释放设备号.

3、编译,在开发板中加载驱动,并查看主设备号

​ [root@fsmp1a drivers]# insmod drv_hello.ko

​ [ 1985.156434] —drv_hello_init—

​ [root@fsmp1a drivers]# cat /proc/devices

​ Character devices:

​ 1 mem

​ 2 pty

​ 3 ttyp

​ 4 /dev/vc/0

​ 4 tty

​ 5 /dev/tty

​ 5 /dev/console

​ 5 /dev/ptmx

​ 5 ttyRPMSG

​ 7 vcs

​ 10 misc

​ 13 input

​ 21 sg

​ 29 fb

​ 89 i2c

​ 90 mtd

​ 100 drv_hello //加载驱动申请的主设号

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
#include <linux/init.h>//头文件固定的两个
#include <linux/module.h>
#include <linux/fs.h>

int drv_hello_open (struct inode * node, struct file * fp)
{
int ret = 0;
printk("---%s---",__FUNCTION__);
return ret;
}
int drv_hello_release (struct inode * node, struct file * fp)
{
int ret = 0;L
printk("---%s---",__FUNCTION__);
return ret;
}

//结构体指针
struct file_operations fopt =
{
.open = drv_hello_open,
.release = drv_hello_release,
};



//加载函数
static int __init drv_hello_init(void)
{
int ret = 0;
printk("---%s---", __FUNCTION__);
//申请设备号
ret = register_chrdev(111, "hello", &fopt);
if (ret != 0)
{
//如果没有
printk("register_chrdev erro");
return ret;
}
return ret;
}

//卸载函数
static void __exit drv_hello_exit(void)
{
printk("---%s---", __FUNCTION__);
}

//函数声明和认证
module_init(drv_hello_init);
module_exit(drv_hello_exit);
MODULE_LICENSE("GPL");//必须要加,表示开源的意思s