(一)内核模块的驱动程序框架
//1,头文件
1 #include "linux/init.h"
2 #include "linux/module.h"
3
4 // 2,模块加载函数
5 static int __init drv_hello_init(void)
6 {
7 printk("------------%s--------------\n",__FUNCTION__);
8 return 0;
9 }
10 //3,模块的卸载函数
11 static void __exit drv_hello_exit(void)
12 {
13 printk("------------%s--------------\n",__FUNCTION__);
14 }
15//4,模块的认证与声明
16 module_init(drv_hello_init); //模块入口函数声明
17 module_exit(drv_hello_exit); //模块出口函数声明
18 MODULE_LICENSE("GPL");
编译内核模块:Makefile
#指定内核源码的路径
1 KERNEL_DIR =/home/andrew/S5pv210/kernel/linux-3.0.8
#你需要编译的文件所在的路径
2 CUR_DIR =$(shell pwd)
3
4 all:#将内核源码和目录中的源文件一起编译,编译生成.ko的模块文件
5 make -C $(KERNEL_DIR) M=$(CUR_DIR) modules
arm-none-linux-gnueabi-gcc $(Myapp).c -o $(Myapp)
6 clean:#将内核源码和目录中的源文件一起编译,编译生成.ko的模块文件删除
7 make -C $(KERNEL_DIR) M=$(CUR_DIR) clean
8 install:#复制生成的.ko的文件到文件系统中
9 cp ./*.ko /opt/rootfs/drv_module */
#指定要编译的内核模块文件的名称
10 obj-m = drv_hello_v1.o
obj-m +=
[root@farsight /drv_module]# insmod drv_hello_v1.ko //在开发板中加载某一个模块
------------drv_hello_init--------------
[root@farsight /drv_module]# rmmod drv_hello_v1//在开发板中卸载某一个模块
------------drv_hello_exit--------------
[root@farsight /drv_module]# lsmod //列出加载的模块
drv_hello_v1 763 0 - Live 0x7f004000
(二)一个完整的驱动的组成
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:参数1为0,动态申请设备号
// 参数1大于,静态指定一个主设备号
//参数2:字符串,自定义,设备描述信息
//参数3:设备操作接口
//返回值:参数1为0,调用成功,返回主设备号,调用失败返回错误码
//参数1大于,调用成功,返回0,调用失败返回错误码
3>unregister_chrdev(major,"drv_hello");//卸载设备号
4>[root@farsight /drv_module]# cat /proc/devices //参看申请的设备号
Character devices:
1 mem
2 pty
3 ttyp
4 /dev/vc/0
4 tty
4 ttyS
5 /dev/tty
5 /dev/console
5 /dev/ptmx
7 vcs
10 misc
13 input
21 sg
128 ptm
136 pts
198 drv_hello
2》创建设备文件(节点):应用层根据设备文件可以操作对应的设备
1>手动创建
mknod 设备文件的名称 类型 主设备号 次设备号
[root@farsight /drv_module]# mknod /dev/drv_hello c 254 5
[root@farsight /drv_module]# ls -l /dev/drv_hello //参看设备文件
crw-r--r-- 1 0 0 254, 5 Jan 1 01:17 /dev/drv_hello
//测试:设备文件是否能被应用层打开
1 #include "stdio.h"
2 #include <sys/types.h>
3 #include <sys/stat.h>
4 #include <fcntl.h>
5 #include <stdlib.h>
6 #include <unistd.h>
7 int main(void)
8 {
9 int fd;
10 fd=open("/dev/drv_hello",O_RDWR);
11 if(fd<0){
12 perror("open");
13 exit(1);
14 }
15 close(fd);
16 return 0;
17 }
实现设备接口:
int drv_hello_open(struct inode *inode, struct file *filp)
{
printk("------------%s--------------\n",__FUNCTION__);
return 0;
}
int drv_hello_close(struct inode *inode, struct file *filp)
{
printk("------------%s--------------\n",__FUNCTION__);
return 0;
}
const struct file_operations fops={
.open=drv_hello_open,
.release=drv_hello_close,
};
2>自动创建设备节点
2.1>创建类
struct class *class_create(struct module *owner, const char *name)
//参数1:当前模块-----THIS_MODULE
//参数2: 字符串----类的名称
//返回值:成功----struct class的地址,失败---NULL
cls=class_create(THIS_MODULE,"drv_hello");
if(IS_ERR(cls)){//专门用来判断指针,是否为NULL指针和野指针,如果为NULL指针和野指针,返回值为真
printk("class_create is error\n");
ret=PTR_ERR(cls);//获取NULL指针和野指针的错误码
goto register_err;
}
//rmmod命令时需要卸载
class_destroy(cls);//卸载类
2.2>创建设备节点
extern struct device *device_create(struct class *cls, struct device *parent,
dev_t devt, void *drvdata,
const char *fmt, ...)
//参数1: struct class结构体指针
//参数2:父类;一般为NULL
//参数3:设备号(包含主设备和次设备号)
#define MINORBITS 20
#define MINORMASK ((1U << MINORBITS) - 1)
#define MAJOR(dev) ((unsigned int) ((dev) >> MINORBITS))//得到主设备号
#define MINOR(dev) ((unsigned int) ((dev) & MINORMASK))//得到次设备号
ma:主设备号
mi:此设备号
#define MKDEV(ma,mi) (((ma) << MINORBITS) | (mi))//得到设备号
//参数4:私有数据。一般为NULL
//参数5:设备节点的名称
//参数6:变参,和参数5一起使用,来表示设备节点的名称
//返回值:struct device *指向设备文件的结构体
// 2.2 创建设备节点
devi=device_create(cls,NULL,MKDEV(major,5),NULL,"drv_hello");
if(IS_ERR(devi)){//专门用来判断指针,是否为NULL指针和野指针,如果为NULL指针和野指针,返回值为真
printk("device_create is error\n");
ret=PTR_ERR(devi);//获取NULL指针和野指针的错误码
goto class_err;
}
device_destroy(cls,MKDEV(major,5));//卸载设备文件