一、驱动框架搭建
首先是c文件,其次是Makefile和vscode的设置json文件。
c文件
#include<linux/module.h>
#include<linux/init.h>
static int __init chrdev_init(void)
{
printk(KERN_DEBUG,"-----chrdev_init-----\n");
return 0;
}
static void __exit chrdev_exit(void)
{
printk(KERN_DEBUG,"-----chrdev_exit-----\n");
}
/*
模块入口与出口函数
*/
module_init(chrdev_init);
module_exit(chrdev_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Pintitus");
Makefile
ROOTFS_DIR = /nfs/rootfs
MODULE_NAME = hello
APP_NAME = helloapp
CROSS_COMPILE = /home/linux/gcc-4.6.4/bin/arm-arm1176jzfssf-linux-gnueabi-
CC = $(CROSS_COMPILE)gcc
ifeq ($(KERNELRELEASE), )
KERNEL_DIR = /home/linux/IMX6ULL
CUR_DIR = $(shell pwd)
all :
make -C $(KERNEL_DIR) M=$(CUR_DIR) modules
$(CC) $(APP_NAME).c -o $(APP_NAME)
clean :
make -C $(KERNEL_DIR) M=$(CUR_DIR) clean
rm -rf $(MODULE_NAME)
install:
cp *.ko $(MODULE_NAME) $(APP_NAME) $(ROOTFS_DIR)/mydrivers
else
obj-m += $(MODULE_NAME).o
endif
json
{
"configurations":[
{
"name":"Linux",
"includePath":[
"${workspaceFolder}/**",
"/home/linux/IMX6ULL/linux/include",
"/home/linux/IMX6ULL/linux/arch/arm/include",
"/home/linux/IMX6ULL/linux/arch/arm/include/generated"
],
"defines":[],
"compilerPath": "/usr/bin/clang",
"cStandard": "c11",
"cppStandard": "c++17",
"intelliSenseMode": "clang-x64"
}
],
"version":4
}
二、加载与卸载模块
1.加载模块
加载模块有Insmod与modprobe命令。
其中insmod是用于直接加载简单的模块,而modprobe是用于加载具有依赖模块项的模块。
insmod xxx.ko
modprobe xxx.ko #这样会失败,会提示缺少modules.dep文件
缺少modules.dep文件是不可以手动创建的,需要在根文件系统下使用depmod命令会生成modules.dep文件。
2.移除模块
rmmod命令移除模块
*命令总结
insmod #注册驱动
modprobe #注册驱动
rmmod #注销驱动
lsmod #查询设备驱动
depmod #构建modprobe缺失文件
mknod /dev/hello c 200 #创建设备名称为hello,主设备号为200的设备结点
三、完善框架
1.创建设备号
注册和注销设备号的函数register_chrdev,unregister_chrdev。
dev_t是跟踪是unsigned int的别称。设备号是一个32位数据。高12位是主设备号,低20位为低设备号。通常使用kdev_t.h文件下的宏,见下。
name是设备名。
register_chrdev(dev_t, const char *name, const struct file_operations *fops);//注册设备号
unregister_chrdev(unsigned int major, const char *name);//注销设备号
//设备号的宏
#define MAJOR(dev) ((unsigned int) ((dev) >> MINORBITS))
#define MINOR(dev) ((unsigned int) ((dev) & MINORMASK))
#define MKDEV(ma,mi) (((ma) << MINORBITS) | (mi))
2.实现fops结构体填充
fops结构体用于与应用程序对接。常用open , release , read , write。
#define CHRDEV_MAJOR 200
#define CHRDEV_NAME "hello"
/*
fileoperation的实现
*/
static int hello_open(struct inode *inode, struct file *filp){
printk("hello_open\n");
return 0;
}
int hello_release(struct inode *inode, struct file *filp){
printk("hello_release\n");
return 0;
}
ssize_t hello_read(struct file *filp, char __user *buf, size_t count, loff_t *ppos){
printk("hello_read\n");
return 0;
}
ssize_t hello_write(struct file *filp, const char __user *buf, size_t count, loff_t *ppos){
printk("hello_write\n");
return 0;
}
static const struct file_operations hello_fops = {
.owner = THIS_MODULE,
.open = hello_open,
.release = hello_release,
.read = hello_read,
.write = hello_write,
}
3.驱动read函数
使用copy_to_user函数,原型如下。
copy_to_user(void __user *to, const void *from, unsigned long n);
驱动read是将驱动的数据从内核读取到用户中。
4.驱动write函数
将用户数据写到内核中。原型如下
copy_from_user(const void *from , void __user *to, unsigned long n)
四、完整驱动框架
我们的框架没有使用程序创建设备结点,因此,每次需要在客户端上手动创建设备结点。
#include<linux/module.h>
#include<linux/init.h>
#include<linux/fs.h>
#include <asm/uaccess.h>
#define CHRDEV_MAJOR 200 //主设备号
#define CHRDEV_NAME "hello" //设备名称
static char readbuff[100];
static char writebuff[100];
static char kerneldata[]={"kernel data!"};
/*
fileoperation的实现
*/
static int hello_open(struct inode *inode, struct file *filp){
printk("hello_open\n");
return 0;
}
int hello_release(struct inode *inode, struct file *filp){
printk("hello_release\n");
return 0;
}
ssize_t hello_read(struct file *filp, char __user *buf, size_t count, loff_t *ppos){
//printk("hello_read\n");
int ret = 0;
memcpy(readbuff , kerneldata , sizeof(kerneldata));
ret = copy_to_user(buf, readbuff, count);
if(ret == 0){
printk("kernel sendata:%s \r\n" , readbuff);
}else{
}
return 0;
}
ssize_t hello_write(struct file *filp, const char __user *buf, size_t count, loff_t *ppos){
//printk("hello_write\n");
int ret = 0 ;
ret = copy_from_user(writebuff , buf , count);
if(ret == 0){
printk("kernel receivedata:%s \r\n" , writebuff);
}else{
}
return 0;
}
static const struct file_operations hello_fops = {
.owner = THIS_MODULE,
.open = hello_open,
.release = hello_release,
.read = hello_read,
.write = hello_write,
};
/*
注册注销设备
*/
static int __init chrdev_init(void)
{
int major = 0;
printk("-----chrdev_init-----\n");
/*注册设备号*/
if( (major = register_chrdev(CHRDEV_MAJOR, CHRDEV_NAME, &hello_fops)) < 0){
printk("设备号申请错误!\n");
}
/*创建设备结点*/
return 0;
}
static void __exit chrdev_exit(void)
{
printk("-----chrdev_exit-----\n");
unregister_chrdev(CHRDEV_MAJOR, CHRDEV_NAME);
}
/*
模块入口与出口函数
*/
module_init(chrdev_init);
module_exit(chrdev_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Pintitus");
五、应用程序编写
我们写好了fops框架,需要与应用程序对接,才能感受到到程序过程。
#include<stdlib.h>
#include<string.h>
#include<stdio.h>
#include<unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
static char usrdata[] = {"usr data!"};
int main(int argc, char *argv[])
{
int fd = 0;
int ret = 0;
char read_buff[10] , write_buff[100];
char *filename;
filename = argv[1];
fd = open(filename , O_RDWR);
if(fd < 0){
printf("打开设备结点%s失败\n",filename);
return -1;
}
/*read*/
ret = read(fd , read_buff ,10);
if(ret < 0){
printf("read error!\n");
}
else{
printf("read data :%s\r\n" , read_buff );
}
/*write*/
memcpy(write_buff ,usrdata ,sizeof(usrdata) );
ret = write(fd , write_buff ,50);
if(ret < 0){
printf("write error!\n");
}
/*close*/
close(fd);
return 0;
}
\