2.IMX6ULL LINUX驱动之字符驱动框架

216 阅读2分钟

一、驱动框架搭建

首先是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;
}

\