|
本帖最后由 batou 于 2020-8-14 17:10 编辑
明白了字符设备驱动基本框架与相关概念之后,我们知道一个字符设备驱动如果想要像文件一样去操作,首先肯定需要有设备号,还需要有个驱动名,然后需要填充file_operations结构体,这样才能实现对设备的具体操作,可以开始写代码了,just do it!
# 接口介绍
1、写代码之前,先介绍两个宏
module_init() //这个是加载模块的时候调用的,相当于模块入口
module_exit() //这个是卸载模块的时候调用的,相当于模块出口
2、再介绍两个函数
①.注册字符设备驱动函数
用register_chrdev函数注册一个字符设备驱动,这个接口非常方便,传入些参数就一个字符设备注册完事了,虽然该函数算是老的接口了,以前认为要学就学新接口,老接口都淘汰了,学了也没啥用,其实不然,该老接口并没有弃用,并且新接口也只是对它做了拆分,走的还是它内部那些套路,其原型为:
static inline int register_chrdev(unsigned int major, const char *name, const struct file_operations *fops);
major是主设备号,当它为0时系统会给你动态分配一个,也可以你自己找一个系统中没有用到的,name是字符设备驱动名,insmod之后cat /proc/devices可以看到的名字,*fops是该设备驱动所有操作的接口
②.注销字符设备驱动函数
unregister_chrdev函数,输入需要注销的设备驱动设备号与设备名称就可以了
原型为:static inline void unregister_chrdev(unsigned int major, const char *name)
③.找到一个可用的设备号
在开发板上查看,cat /proc/devices,看哪个设备号没用,一般是0~255,从大的开始选
当然这一步也不是必须的,因为上面说到的那个register_chrdev函数可以让系统给你分配,但是在我们学习过程中,这一步是应该了解的
//驱动代码
#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/ide.h>
MODULE_LICENSE("GPL");
#define HELLO_CNT 1
#define HELLO_MAJOR 222
#define HELLO_NAME "hello"
static dev_t dev_num;
//文件打开函数
static int hello_open(struct inode *inode, struct file *file)
{
printk(KERN_INFO "\nhello_open\n");
return 0;
}
//文件释放函数
static int hello_release(struct inode *inode, struct file *file)
{
printk(KERN_INFO "\nhello_release\n");
return 0;
}
static struct file_operations chrdev_fops = {
.owner = THIS_MODULE, //这里都是这么赋值的,用来在它的操作还在被使用时阻止模块被卸载
//应用层间接调用的就是如下接口, 应用层如果调用没有定义的接口是会出错的
.open = hello_open,
.release = hello_release,
};
//模块加载函数
static int hello_init(void)
{
printk(KERN_ALERT "hello world!\n");
//register_chrdev是注册字符设备, 这个接口可以一步到位
dev_num = register_chrdev(HELLO_MAJOR, HELLO_NAME, &chrdev_fops);
if (dev_num < 0) {
printk(KERN_ERR "register_chrdev fail\n");
return -EINVAL;
}
printk(KERN_INFO "register_chrdev success...\n");
return 0;
}
//模块卸载函数
static void hello_exit(void)
{
//注销字符设备驱动
unregister_chrdev(HELLO_MAJOR, HELLO_NAME);
printk(KERN_ALERT "hello_exit, goodbye!\n");
}
module_init(hello_init);
module_exit(hello_exit);
//测试代码
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
#define FILE "/dev/hello"
int main(void)
{
int fd = -1;
int i = 0;
fd = open(FILE, O_RDWR);
if (fd < 0) {
printf("\nopen %s fail:%d\n", FILE, fd);
return -1;
}
printf("App open %s ok\n", FILE);
close(fd);
}
Makefile内容如下:
KERNELDIR := /home/mstar/ubuntu64/linux
CURRENT_PATH := $(shell pwd)
obj-m := hello_drv.o
build: kernel_modules
kernel_modules:
$(MAKE) -C $(KERNELDIR) M=$(CURRENT_PATH) modules
clean:
$(MAKE) -C $(KERNELDIR) M=$(CURRENT_PATH) clean
编译驱动与测试app
然后将hello_drv.ko与helloApp拷贝到开发板的共享盘
我是将ubuntu下的一个文件挂载到linux开发板上的,如通过如下指令:
mount -t nfs -o nolock,nfsvers=3 192.168.0.103:/home/mstar/ubuntu64/share mnt/ttt
# 开始测试
①.驱动加载
使用insmod指令加载驱动
root@ALIENTEK-IMX6U:/mnt/ttt# insmod hello_drv.ko
hello world!
register_chrdev success...
②.查看驱动
使用cat /proc/devices查看
说明驱动已经加载成功了,主设备号是222
③.创建设备文件
使用mknod指令创建设备文件,mknod 的标准形式为:mknod DEVNAME {b | c} MAJOR MINOR
如:mknod /dev/hello c 222 0 //其中/dev/hello是设备文件名,在应用程序中就可以通过该名进行访问,该名称是跟设备号绑定在一起的,后面的c代表字符设备,222是主设备号,0是次设备号,次设备号还可以有很多,代表同一种类型的不同文件!
如下:
root@ALIENTEK-IMX6U:/mnt/ttt# mknod /dev/hello c 222 0
root@ALIENTEK-IMX6U:/mnt/ttt# mknod /dev/hello1 c 222 1
于是通过ls -ll /dev查看到了两个文件
④.运行应用程序
下面打印出的字符很明显应用程序能正常运行
⑤.卸载驱动
通过rmmod指令
其中/dev下生成的hello与hello1两个文件需要手动rm删除
# 总结
一个简单的字符设备驱动,代码上的东西并不多,很多步骤都是需要我们手动去完成,像确定设备号、创建/删除设备文件等,这虽然比较麻烦,但在我们前期学习的过程中是有帮助的,我们对整个过程有了一个基本的了解,后面就算切换到由程序完成也有了一个更好的过渡!
|
本帖子中包含更多资源
您需要 登录 才可以下载或查看,没有帐号?注册
x
阿莫论坛20周年了!感谢大家的支持与爱护!!
曾经有一段真挚的爱情摆在我的面前,我没有珍惜,现在想起来,还好我没有珍惜……
|