本文为一个简单的字符设备驱动,涉及驱动编写、测试程序编写、Makefile编写、驱动加载/卸载,运行于Linux虚拟机,不涉及底层配置。撰写本文的主要目的为记录一下驱动的开发流程,参考了正点原子的驱动开发指南。
驱动代码
创建文件夹 1_chrdevbase/ ,下属 APP/ 与 Driver/ 两个文件夹,前者放测试程序,后者放驱动代码。
在 Driver/ 下创建 chrdevbase.c,驱动代码如下
#include<linux/types.h>
#include<linux/kernel.h>
#include<linux/delay.h>
#include<linux/ide.h>
#include<linux/init.h>
#include<linux/module.h>
MODULE_LICENSE("GPL");
MODULE_AUTHOR("今朝无言");
#define CHRDEVBASE_MAJOR 200
#define CHRDEVBASE_NAME "chrdevbase"
static char readbuf[100];
static char writebuf[100];
static char kerneldata[] = {"kernel data!"};
static int chrdevbase_open(struct inode *inode, struct file *filp){
printk("chrdevbase open!\n");
return 0;
}
static ssize_t chrdevbase_read(struct file *filp, char __user *buf, size_t cnt, loff_t *offt){
int retvalue = 0;
memcpy(readbuf, kerneldata, sizeof(kerneldata));
retvalue = copy_to_user(buf, readbuf, cnt);
if(retvalue == 0){
printk("kernel send data ok!\n");
}
else {
printk("kernel send data failed!\n");
}
return 0;
}
static ssize_t chrdevbase_write(struct file *filp, const char *buf, size_t cnt, loff_t *offt){
int retvalue = copy_from_user(writebuf, buf, cnt);
retvalue = copy_from_user(writebuf, buf, cnt);
if(retvalue == 0){
printk("kernel receive data: %s \n",writebuf);
}
else {
printk("kernel receive data failed!\n");
}
return 0;
}
static int chrdevbase_release(struct inode *inode, struct file *filp){
printk("chrdevbase release! \n");
return 0;
}
static struct file_operations chrdevbase_fops = {
.owner = THIS_MODULE,
.open = chrdevbase_open,
.read = chrdevbase_read,
.write = chrdevbase_write,
.release = chrdevbase_release
};
static int __init chrdevbase_init(void){
int retvalue = 0;
retvalue = register_chrdev(CHRDEVBASE_MAJOR, CHRDEVBASE_NAME, &chrdevbase_fops);
if(retvalue < 0){
printk("chrdevbase driver register failed!\n");
}
else {
printk("chrdevbase driver register success!\n");
}
return 0;
}
static void __exit chrdevbase_exit(void){
unregister_chrdev(CHRDEVBASE_MAJOR, CHRDEVBASE_NAME);
printk("chrdevbase exit!\n");
return;
}
module_init(chrdevbase_init);
module_exit(chrdevbase_exit);
驱动代码Makefile
在 Driver/ 下创建 Makefile,内容如下
KERNELDIR := /lib/modules/4.15.0-189-generic/build
#本机编译就/lib/modules/`uname -r`/build
#交叉编译就使用对应的Kernel源码目录
CURRENT_PATH := $(shell pwd)
#要生成的模块名
obj-m := chrdevbase.o
build: kernel_modules
kernel_modules:
$(MAKE) -C $(KERNELDIR) M=$(CURRENT_PATH) modules
clean:
$(MAKE) -C $(KERNELDIR) M=$(CURRENT_PATH) clean
测试程序代码
在 APP/ 下创建 chrdevbaseAPP.c,代码如下
#include<stdio.h>
#include<unistd.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>
#include<stdlib.h>
#include<string.h>
static char usrdata[] = {"usr data!"};
int main(int argc, char *argv[]){
int fd, retvalue;
char *filename;
char readbuf[100], writebuf[100];
if(argc != 3){
printf("Error Usage!\n");
return -1;
}
filename = argv[1];
fd = open(filename, O_RDWR);
if(fd < 0){
printf("Can't open file %s!\n", filename);
return -2;
}
if(atoi(argv[2]) == 1){
retvalue = read(fd, readbuf, 50);
if(retvalue < 0){
printf("read file %s failed!\n", filename);
}
else {
printf("read data: %s\n", readbuf);
}
}
if(atoi(argv[2]) == 2){
memcpy(writebuf, usrdata, sizeof(usrdata));
retvalue = write(fd, writebuf, 50);
if(retvalue < 0){
printf("write file %s failed!\n", filename);
}
else {
printf("write file success!\n");
}
}
retvalue = close(fd);
if(retvalue < 0){
printf("Can't close file %s!\n", filename);
return -3;
}
return 0;
}
测试程序Makefile
在 APP/ 下创建 Makefile,内容如下
build:
gcc chrdevbaseAPP.c -o chrdevbaseAPP
clean:
rm chrdevbaseAPP
编译测试程序
编译驱动
驱动加载
使用 insmod 命令加载刚刚生成的驱动模块
sudo insmod chrdevbase.ko
执行
cat /proc/devices
查看驱动,如下图,可以看到驱动已经加载
创建设备节点文件
使用 mkmod 命令创建驱动节点
sudo mknod /dev/chrdevbase c 200 0
则创建字符设备文件/dev/chrdevbase,对该文件进行读写操作即可使用驱动,其中 ‘c’ 表示字符设备,200为主设备号,0为次设备号。
测试
进入 APP/ 文件夹,执行
sudo ./chrdevbaseAPP /dev/chrdevbase 1
进行设备读取测试,结果如下
可以看到用户接收到了从内核传递来的数据 ‘kernel data’ 。
执行
sudo ./chrdevbaseAPP /dev/chrdevbase 2
进行设备写入测试,结果如下
查看最后6条日志消息:
dmesg | tail -6
其中前三条是前面进行读取测试的日志输出,后三条是进行写入测试的日志输出,可以看到内核接收到了用户发送来的数据 ‘usr data’ 。
驱动卸载
使用 rmmod 命令卸载驱动:
sudo rmmod chrdevbase.ko
再使用 cat /proc/devices 查看,将发现 chrdevbase 设备已被卸载。
加载/卸载模块时的日志如下:
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)