本文介绍: 是Linux 驱动中最基本的一类设备驱动字符设备就是一个一个字节,按照字节流进行读写操作设备读写数据是分先后顺序的。比如我们常见的点灯、按键、IIC、SPI,LCD 等等都是字符设备,这些设备的驱动就叫做字符设备驱动。

目录

一、字符设备驱动简介

二、chrdevbase 字符设备驱动开发实验 

1.创建驱动程序的目录

2.创建vscode工程 

3.编写实验程序

4.编译驱动程序和测试APP代码

(1)加载驱动模块

(2)创建设备节点文件

(3)chrdevbase 设备操作测试

(4)卸载驱动模块


一、字符设备驱动简介

        字符设备是Linux 驱动中最基本的一类设备驱动,字符设备就是一个一个字节,按照字节流进行读写操作的设备,读写数据是分先后顺序的。比如我们常见的点灯、按键、IIC、SPI,LCD 等等都是字符设备,这些设备的驱动就叫做字符设备驱动。

二、chrdevbase 字符设备驱动开发实验 

        本节我们就以chrdevbase 这个虚拟设备为例完整编写个字符设备驱动模块chrdevbase 不是实际存在一个设备,是为了方便讲解字符设备的开发引入的一个虚拟设备。chrdevbase 设备有两个缓冲区一个为读缓冲区,一个为写缓冲区,这两个缓冲区大小都为100 字节。在应用程序可以chrdevbase 设备的写缓冲区中写数据,从读缓冲区读取数据chrdevbase 这个虚拟设备的功能简单,但是它包含了字符设备的最基本功能

1.创建驱动程序目录

2.创建vscode工程 

 将工作区另存为chrdevbase

使用命令code . 进入vscode

因为是编写Linux 驱动,因此会用到Linux 源码中的函数我们需要在VSCode添加Linux
源码中的头文件路径打开VSCode按下“Crtl+Shift+P”打开VSCode控制台然后输入
“C/C++: Edit configurations(UI) ”,打开C/C++编辑配置文件,如下图所示

 

打开以后会自动在.vscode 目录生成一个名为c_cpp_properties.json文件,此文件修改如下所示如下所示: 

{
    "configurations": [
        {
            "name": "Linux",
            "includePath": [
                "${workspaceFolder}/**",
                "/home/ssz/linux/IMX6ULL/linux/temp/linux-imx-rel_imx_4.1.15_2.1.0_ga_alientek/include",
                "/home/ssz/linux/IMX6ULL/linux/temp/linux-imx-rel_imx_4.1.15_2.1.0_ga_alientek/arch/arm/include",
                "/home/ssz/linux/IMX6ULL/linux/temp/linux-imx-rel_imx_4.1.15_2.1.0_ga_alientek/arch/arm/include/generated"
            ],
            "defines": [],
            "compilerPath": "/usr/bin/gcc",
            "cStandard": "c11",
            "cppStandard": "c++17",
            "intelliSenseMode": "clang-x64"
        }
    ],
    "version": 4
}

上面includePath 表示头文件路径需要将Linux 源码里面头文件路径添加进来,也就是我们前面移植的Linux 内核源码中的头文件路径。

3.编写实验程序

创建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>


#define CHRDEVBASE_MAJOR 200 //主设备号
#define CHRDEVBASE_NAME "chrdevbase" //设备名

static char readbuf[100]; //读缓冲区
static char writebuf[100]; //写缓冲区
static char kerneldata[] = {"kernel data!"};

//打开设备
static int chardevbase_open(struct inode *inode, struct file *filp)
{
    printk("chrdevbase open!rn");
    return 0;
}
//向设备读取数据
static ssize_t chrdevbase_read(struct file *filp, char __user *buf, size_t cnt, loff_t *off_t)
{
    int retvalue = 0;

    //向用户空间发送数据
    memcpy(readbuf, kerneldata, sizeof(kerneldata));
    //内核用户
    retvalue = copy_to_user(buf, readbuf, cnt);
    if(retvalue == 0)
    {
        printk("kernel sendata ok!rn");
    }
    else
    {
        printk("kernel senddata failed!rn");
    }

    //printk("chrdevbase read!rn");
    return 0; 
}
//向设备写数据
static ssize_t chrdevbase_write(struct file *filp, const char __user *buf, size_t cnt, loff_t *offt)
{
    int retvalue = 0;
    //接收用户空间传递内核数据并且打印出来
    //用户内核
    retvalue = copy_from_user(writebuf, buf, cnt);
    if(retvalue == 0)
    {
        printk("kernel recevdata:%srn",writebuf);
    }
    else
    {
        printk("kernel recedata failed!rn");
    }

    //printk("chrdevbase write!rn");
    return 0;
}
//关闭/释放设备
static int chrdevbase_release(struct inode *inode, struct file *filp)
{
    //printk("chrdevbase release!rn");
    return 0;
}


static struct file_operations chrdevbase_fops = {
    .owner = THIS_MODULE,
    .open = chardevbase_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,&amp;chrdevbase_fops);
    if(retvalue < 0)
    {
        //字符设备注失败,自行处理
        printk("chrdevbase driver register failedrn");
    }
    printk("chrdevbase_init()rn");
    return 0;
}
static void __exit chrdevbase_exit(void)
{
    //注销字符设备驱动
    unregister_chrdev(CHRDEVBASE_MAJOR, CHRDEVBASE_NAME);
    printk("chrdevbase_exit()rn");
}
/*
驱动模块入口与出口
*/
module_init(chrdevbase_init);//入口
module_exit(chrdevbase_exit);//出口

//LICENSE和作者信息
MODULE_LICENSE("GPL");
MODULE_AUTHOR("ssz");

创建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 usrdate[] = {"usr data!"};

int main(int argc, char *argv[])
{
    int fd,retvalue;
    char *filename;
    char readbuf[100],writebuf[100];

    if(argc != 3)
    {
        printf("Error Usage!rn");
        return -1;
    }

    filename = argv[1];

    //打开驱动文件
    fd = open(filename, O_RDWR);
    if(fd < 0)
    {
        printf("Cant't open file %srn",filename);
        return -1;
    }
    if(atoi(argv[2]) == 1)
    {
        //从驱动文件读取数据
        retvalue = read(fd, readbuf, 50);
        if(retvalue < 0)
        {
            printf("read file %s failed!rn",filename);
        }
        else
        {
            //读取成功,打印读取成功的数据
           // printf("1111n");
            printf("read data: %s rn",readbuf);
        }  
    }
    if(atoi(argv[2]) == 2)
    {
        //向设备驱动写数据
        memcpy(writebuf, usrdate, sizeof(usrdate));
        retvalue = write(fd, writebuf, 50);
        if(retvalue < 0)
        {
            printf("write file %s failed!rn",filename);
        }
    }
    retvalue = close(fd);
    if(retvalue < 0)
    {
        printf("Can't close file %srn",filename);
        return -1;
    }
    return 0;
}

编译驱动程序,也就是chrdevbase.c 这个文件我们需要将其编译为.ko 模块创建
Makefile 文件然后在其中输入如下内容

KERNELDIR := /home/ssz/linux/IMX6ULL/linux/temp/linux-imx-rel_imx_4.1.15_2.1.0_ga_alientek
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

4.编译驱动程序测试APP代码

编译驱动程序输入make命令,会生成chrdevbase.ko文件

编译测试APP:

使用file命令,chrdevbaseApp这个可执行文件是32 位LSB 格式,ARM 版本的,因此chrdevbaseApp只能在ARM 芯片运行

 运行测试

(1)加载驱动模块

驱动模块chrdevbase.ko 和测试软件chrdevbaseAPP 都已经准备好了,接下来就是运行测试。为了方便测试,Linux 系统选择通过TFTP 从网络启动,并且使用NFS 挂载网络文件系统,确保ubootbootcmdbootargs环境变量的值为(确保电脑开发板通过网线连接):

setenv bootcmd 'tftp 80800000 zImage;tftp 83000000 imx6ull-alientek-emmc.dtb;bootz 80800000 - 83000000'
setenv bootargs 'console=ttymxc0,115200 root=/dev/nfs rw nfsroot=192.168.1.66:/home/ssz/linux/nfs/
rootfs ip=192.168.1.66:192.168.1.55:192.168.1.1:255.255.255.0::eth0:off'
saveenv

设置好以后启动Linux 系统检查开发板文件系统中有没有“/lib/modules/4.1.15”这个目录,如果没有的话自行创建。注意,“/lib/modules/4.1.15”这个目录用来存放驱动模块使用modprobe 命令加载驱动模块时候,驱动模块存放在此目录下。“/lib/modules”是通用的,不管你用的什么板子、什么内核,这部分是一样的。不一样的是后面的“4.1.15”,这里要根据你所使用的Linux 内核版本设置比如ALPHA 开发板现在用的是4.1.15 版本的Linux 内核,因此就是“/lib/modules/4.1.15”。如果你使用的其他版本内核比如5.14.31,那么就应该创建“/lib/modules/5.14.31”目录,否则modprobe 命令无法加载驱动模块。因为是通过NFS 将Ubuntu 中的rootfs(第三十八章制作好的根文件系统)目录挂载为根文件系统,所以可以很方便的将chrdevbase.ko 和chrdevbaseAPP 复制rootfs/lib/modules/4.1.15 目录中,命令如下

拷贝完成以后就会在开发板的/lib/modules/4.1.15 目录下存在chrdevbase.ko 和chrdevbaseAPP 这两个文件,如图所示: 

输入如下命令加载chrdevbase.ko 驱动文件: 

insmod chrdevbase.ko
或者
modprobe chrdevbase.ko

从上图 可以看出,modprobe 提示无法打开“modules.dep”这个文件,因此驱动挂载失败了。我们不用手动创建modules.dep 这个文件,直接输入depmod 命令即可自动生成modules.dep,有些根文件系统可能没有depmod 这个命令,如果没有这个命令就只能重新配置busybox,使能此命令,然后重新编译busybox输入“depmod”命令以后会自动生成modules.alias、modules.symbols 和modules.dep 这三个文件,如下图所示

驱动加载成功:

输入lsmod”命令即可查看当前系统中存在的模块,结果下图所示: 

 

从上图可以看出,当前系统只有“chrdevbase”这一个模块。输入如下命令查看当前系统中有没有chrdevbase 这个设备:

从上图可以看出,当前系统存在chrdevbase 这个设备,主设备号为200,跟我们设置
的主设备号一致。 

(2)创建设备节点文件

驱动加载成功需要在/dev 目录下创建一个与之对应的设备节点文件,应用程序就是通过操作这个设备节点文件来完成对具体设备的操作。输入如下命令创建/dev/chrdevbase 这个设备节点文件: 

其中“mknod”是创建节点命令,“/dev/chrdevbase”是要创建的节点文件,“c”表示这是个字符设备,“200”是设备的主设备号,“0”是设备的次设备号。创建完成以后就会存在/dev/chrdevbase 这个文件,可以使用“ls /dev/chrdevbase -l”命令查看结果如上图所示

如果chrdevbaseAPP 想要读写chrdevbase 设备,直接对/dev/chrdevbase 进行读写操作即可。相当于/dev/chrdevbase 这个文件是chrdevbase 设备在用户空间中的实现前面一直说Linux 下一切皆文件,包括设备也是文件, 

(3)chrdevbase 设备操作测试

读写操作测试

 

 

(4)卸载驱动模块

如果不再使用某个设备的话可以将其驱动卸载掉,比如输入如下命令卸载掉chrdevbase 这个设备:  

从上图可以看出,此时系统已经没有任何模块了,chrdevbase 这个模块也不存在了,说明模块卸载成功。至此,chrdevbase 这个设备的整个驱动就验证完成了,驱动工作正常。

原文地址:https://blog.csdn.net/ssz__/article/details/134753854

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任

如若转载,请注明出处:http://www.7code.cn/show_30102.html

如若内容造成侵权/违法违规/事实不符,请联系代码007邮箱suwngjj01@126.com进行投诉反馈,一经查实,立即删除

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注