Linux字符设备驱动编程

Linux字符设备驱动编程

1. 基本介绍

linux中设备主要分为三类,字符设备,块设备,网络设备。本节我们主要进行字符设备驱动程序的编写。

2. 编写整体流程图

在这里插入图片描述

3. 源码分析

进行字符设备驱动程序的编写我们需要进行3个文件的实现

  • simple_char_driver.c 字符驱动程序的整体实现
  • Makefile 编译simple_char_driver.c文件生成.ko文件
  • app.c 对编写的字符设备驱动程序进行测试

simple_char_driver.c

#include <linux/module.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/uaccess.h>

#define DEVICE_NAME "simple_char"

static dev_t major_number;
static char buffer[256]; //内核空间的一个buf
static struct cdev simple_char_cdev;
//读操作
static ssize_t simple_char_read(struct file *filp, char __user *buf, size_t count, loff_t *f_pos)
{
    //将buffer中的数据copy到buf中。同下文写操作
    ssize_t bytes_read = simple_read_from_buffer(buf, count, f_pos, buffer, sizeof(buffer));
    return bytes_read;
}
//写操作
static ssize_t simple_char_write(struct file *filp, const char __user *buf, size_t count, loff_t *f_pos)
{
    //使用该函数将应用层的传过来的buf中的内容拷贝到(内核空间)的buffer中
    //memcpy(buffer,buf); //不行,因为两者不在一个地址空间中
    ssize_t bytes_written = simple_write_to_buffer(buffer, sizeof(buffer), f_pos, buf, count);
    //到这里我们就成功把用户空间的数据转移到内核空间了
    //真正的驱动中,数据从应用层复制到驱动中后,我们就需要根据这个数据去写硬件完成硬件的操作
    //下面应该是操作硬件的代码
    return bytes_written;
}
//打开设备 file_operatios结构体变量中填充的函数指针open的实体
static int simple_char_open(struct inode *inode, struct file *filp)
{
    //这个函数中需要放置的是打开这个设备的硬件操作代码部分
    return 0;
}
//关闭设备 file_operatios结构体变量中填充的函数指针release的实体
static int simple_char_release(struct inode *inode, struct file *filp)
{
    //这个函数中需要放置的是关闭这个设备的硬件操作代码部分
    return 0;
}
//自定义一个file_operations结构体变量,并填充
static struct file_operations fops = {
    .owner = THIS_MODULE,  //惯例,所有的驱动都有这一个,这也是结构体中唯一一个不是函数指针的元素
    .read = simple_char_read,  //将来应用read打开时实际调用的函数,下面同理
    .write = simple_char_write,
    .open = simple_char_open,
    .release = simple_char_release,
};
//模块安装函数
//在module_init宏调用函数中去注册字符设备驱动
static int __init simple_char_init(void)
{
    int ret;
    //动态分配设备号
    ret = alloc_chrdev_region(&major_number, 0, 1, DEVICE_NAME);
    printk(KERN_INFO "major number %d ,%d\n",MAJOR(major_number),MINOR(major_number));

    if (ret < 0) {
        printk(KERN_ERR "Failed to allocate major number for %s\n", DEVICE_NAME);
        return ret;
    }
    //初始化cdev
    cdev_init(&simple_char_cdev, &fops);
    //注册cdev
    ret = cdev_add(&simple_char_cdev, major_number, 1);
    if (ret < 0) {
        printk(KERN_ERR "Failed to add cdev for %s\n", DEVICE_NAME);
        unregister_chrdev_region(major_number, 1);
        return ret;
    }

    printk(KERN_INFO "Loaded %s with major number %d %d\n", DEVICE_NAME, MAJOR(major_number),MINOR(major_number));
    return 0;
}
// 模块卸载函数
static void __exit simple_char_exit(void)
{
    cdev_del(&simple_char_cdev);
    unregister_chrdev_region(major_number, 1);
    printk(KERN_INFO "Unloaded %s\n", DEVICE_NAME);
}

module_init(simple_char_init); //insmod时调用
module_exit(simple_char_exit); //rmmod时调用
//添加设备的描述信息
MODULE_LICENSE("GPL");
MODULE_AUTHOR(" Name");
MODULE_DESCRIPTION("A simple character device driver");
MODULE_VERSION("0.1");

Makefile

obj-m += simple_char_driver.o

all:
	make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules

clean: 
	make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean​​​

app.c

#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
#define FILE "/dev/simple_char"
int main(void) {
        //打开文件
        int fd = open(FILE,O_RDWR);
        if(fd < 0){
                printf("open %s error.\n",FILE);
                return -1;
        }
        printf("open %s success..\n",FILE);
        //注意:当我们写入数据之后读取数据printf("The content of the simple_char is %s \n",buf);
        //会为空,这是因为文件指针发生了偏移。所以进行读操作测试时可以将写操作代码进行注释。
        //写入数据
        const char *data = "hello_device";
        ssize_t bytes_written = write(fd,data,strlen(data));
        if(bytes_written == -1) {
                printf("Failed to write to the FIle\n");
                close(fd);
                return -1;
        }

        //读取数据
        static char buf[100];
        ssize_t bytes_read = read(fd,buf,sizeof(buf)-1);
        if(bytes_read == -1) {
                printf("Failed to read from the file\n");
                close(fd);
                return 1;
        }
        buf[bytes_read] = '\0'; //添加字符串结束符
        printf("The content of the simple_char is %s \n",buf);
        
        close(fd);
        return 0;
}

4. 操作说明

1.将上文中的源码文件名为(simple_char_driver.c和Makefile)放在Linux系统的同一路径下
2. 终端执行命令cd到文件所在位置
3. 执行make之后我们会在文件所在目录中发现一个.ko文件。
4. 执行指令 sudo insmod simple_char_driver.ko
5. 执行 sudo dmesg 查看insmod simple_char_driver.ko是否被加载到内核。
如果成功被加载到内核会出现Load simple_char with major number MAJOR MINOR

注:MAJOR MINOR 就是我们使用alloc_chrdev_region()函数后系统自动分配给我们的主设备号和从设备号。
下一步操作我们会用到。
  1. /dev目录下执行:mknod simple_char c MAJOR MINOR
    之后我们在/dev目录下使用 ls |grep simple_char进行查看设备节点是否建立成功
  2. app.c文件放在第一步操作时的路径下,进行 gcc app.c操作
  3. 进行sudo ./a.out操作
  4. 这时我们使用cat /dev/simple_char命令可以看到该文件中已有内容
  5. 测试成功后使用rm指令将设备节点删除
  • 10
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值