1,编写一个空壳驱动,所需函数
(1)模块安装函数 __init 和卸载函数 __exit : #include <linux/init.h>
(2)如何调用上述的注册 : module_init 和 module_exit
(3)register_chrdev(函数参数参考内核源码);向内核使用 file_operations 结构体注册自己的驱动
成功返回0,失败返回其它
major : 设备驱动的号(标志当前设备的编号 正数 1~ 255)
*name : 设备名字 (当前设备驱动的,名字)
*fops : 指向file_operations结构体
(4)unregister_chrdev; 宏在 模块退出时,要记得回收
static inline void unregister_chrdev(unsigned int major, const char *name)
(5)file_operations ; 结构体里封装的是 应用层的函数调用(函数指针)
示例:
static const struct file_operations test_fops = {
.owner = THIS_MODULE,
.open = test_chardev_open, //打开文件
.release = test_chrdev_release, //关闭文件,内核中close就是(release)
};
2, 如何让内核给我们自动分配设备号
register_chrdev;函数注册的时候 major 给的值为0 即可
3,设备文件的创建 (应用层读写的是 安装模块的 设备文件)
(1)查看所安装模块分配的设备号 : cat /proc/devices
(2)mknod /dev/test c 250 0
mknod /dev/xxx c(字符设备文件) 主设备号 次设备号
4,应用和驱动之间的数据交换
PS :应用层的数据不能直接传输到内核驱动当中,内核有提供到函数。主要思想:复制
(1)copy_from_user : 将用户空间数据,传输到内核空间
原型:copy_from_user(void *to, const void __user *from, unsigned long n);
*to : 内核buf空间
*from : 用户buf空间
unsigned long n : 多个个字节
(2)copy_to_user : 将内核空间数据,传输到内核空间
5,简单驱动实例
驱动:
#include <linux/module.h>
#include <linux/init.h>
#include <linux/fs.h>
#define TESTNAME "chrdev"
static int test_chardev_open(struct inode *inode, struct file *file);
static int test_chrdev_release(struct inode *inode, struct file *file);
int major; //用来保存自动分配的驱动设备号
//1,自定义一个 file_operations test_fops 结构体变量,自己去填充
static const struct file_operations test_fops = {
.owner = THIS_MODULE,
.open = test_chardev_open, //打开文件
.release = test_chrdev_release, //关闭文件,内核中close就是(release)
};
//2,注册驱动模块
static int __init chrdev_init(void)
{
printk(KERN_INFO"test_chrdev_init is OK!\n");
major = register_chrdev(0,TESTNAME,&test_fops); //注册设备号
if (major < 0)
{
printk(KERN_ERR "register_chrdev fail\n");
return -EINVAL; //内核中的宏,用来知晓出错是怎样的
}
printk(KERN_INFO"register_chrdev success..\n");
return 0;
}
//3,模块卸载
static void __exit chrdev_exit(void)
{
unregister_chrdev( major,TESTNAME);
printk(KERN_INFO"test_chrdev_exit is OK!\n");
}
//4,open函数实现
static int test_chardev_open(struct inode *inode, struct file *file)
{
printk(KERN_INFO"test_chardev_open is OK\n");
return 0;
}
//5,close 关闭函数实现
static int test_chrdev_release(struct inode *inode, struct file *file)
{
printk(KERN_INFO "test_chrdev_release is OK\n");
return 0;
}
module_init(chrdev_init);
module_exit(chrdev_exit);
MODULE_LICENSE("GPL"); // 描述模块的许可证
MODULE_AUTHOR("gj"); // 描述模块的作者
MODULE_DESCRIPTION("module test"); // 描述模块的介绍信息
MODULE_ALIAS("xxx"); // 描述模块的别名信息
应用 :
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#define FILE "/dev/test" // 刚才mknod创建的设备文件名
int main(void)
{
int fd = -1;
fd = open(FILE, O_RDWR);
if (fd < 0)
{
printf("open %s error.\n", FILE);
return -1;
}
printf("open %s success..\n", FILE);
// 读写文件
// 关闭文件
close(fd);
return 0;
}
Makefile :
# 开发板的linux内核的源码树目录
KERN_DIR = /kernel
obj-m += linux.o
all:
make -C $(KERN_DIR) M=`pwd` modules
arm-linux-gnueabi-gcc app.c -o app #应用程序要在开发板上运行,用gcc交叉编译链
cp:
cp *.ko /home/aston/rootfs/rootfs/driver_test
cp app /home/aston/rootfs/rootfs/driver_test
.PHONY: clean
clean:
make -C $(KERN_DIR) M=`pwd` modules clean
现象 :上述准确无误后
(1)lsmod 安装模块和 rmmod 卸载模块,会看到正确打印的信息。
(2)mknod 创建完成设备文件之后,执行 应用程序,也会看到正确的打印信息。