linux 驱动——字符设备驱动(自动生成设备节点文件)

news2024/11/28 19:46:05

文章目录

    • 字符设备驱动
    • 字符设备 APP
    • 生成 dev 节点的原理
    • 配置内核自动创建设备节点
    • 模块使用

字符设备驱动

#include "linux/device/class.h"
#include "linux/export.h"
#include "linux/uaccess.h"
#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/device.h>
#include <linux/cdev.h>

#define CHRDEVBASE_NAME "chrdevbase" /* 设备名 */
#define CHRDEVBASE_NUM 1             /* 设备数目 */

static char write_buf[100];
static char read_buf[100];

static char *string_test = "kernel data this tyustli test";

typedef struct {
    dev_t dev_id;          /* 设备号 */
    struct cdev c_dev;     /* cdev */
    struct class *class;   /* 类 */
    struct device *device; /* 设备 */
    int major;             /* 主设备号 */
    int minor;             /* 次设备号 */
} new_chrdev_t;

new_chrdev_t new_chrdev;

static int chrdevbase_open(struct inode *inode, struct file *file)
{
    printk("k: chrdevbase open\r\n");

    return 0;
}

static ssize_t chrdevbase_read(struct file *file, char __user *buf,
                               size_t count, loff_t *ppos)
{
    unsigned long ret = 0;

    printk("k: chrdevbase read\r\n");
    memcpy(read_buf, string_test, strlen(string_test));

    ret = copy_to_user(buf, read_buf, count);
    if (ret == 0) {
        printk("k: read data success\r\n");
    } else {
        printk("k: read data failed ret = %ld\r\n", ret);
    }

    return ret;
}

static ssize_t chrdevbase_write(struct file *file, const char __user *buf,
                                size_t count, loff_t *ppos)
{
    unsigned long ret = 0;

    printk("k: chrdevbase write\r\n");

    ret = copy_from_user(write_buf, buf, count);
    if (ret == 0) {
        printk("k: write data success write data is: %s\r\n", write_buf);
    } else {
        printk("k: write data failed ret = %ld\r\n", ret);
    }

    return count;
}

static int chrdevbase_release(struct inode *inode, struct file *file)
{
    printk("k: chrdevbase release\r\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 err = 0;

    err = alloc_chrdev_region(&new_chrdev.dev_id, 0, CHRDEVBASE_NUM,
                              CHRDEVBASE_NAME);
    if (err < 0) {
        printk("k: alloc chrdev region failed err = %d\r\n", err);
        return -1;
    }

    /* get major and minor */
    new_chrdev.major = MAJOR(new_chrdev.dev_id);
    new_chrdev.minor = MINOR(new_chrdev.dev_id);
    printk("k: newcheled major=%d,minor=%d\r\n", new_chrdev.major,
           new_chrdev.minor);

    new_chrdev.c_dev.owner = THIS_MODULE;
    cdev_init(&new_chrdev.c_dev, &chrdevbase_fops);
    err = cdev_add(&new_chrdev.c_dev, new_chrdev.dev_id, CHRDEVBASE_NUM);
    if (err < 0) {
        printk("k: cdev add failed err = %d\r\n", err);
        goto out;
    }

    new_chrdev.class = class_create(CHRDEVBASE_NAME);
    if (IS_ERR(new_chrdev.class)) {
        printk("k: class create failed\r\n");
        goto out_cdev;
    }

    new_chrdev.device = device_create(new_chrdev.class, NULL, new_chrdev.dev_id,
                                      NULL, CHRDEVBASE_NAME);
    if (IS_ERR(new_chrdev.device)) {
        printk("k: device create failed\r\n");
        goto out_class;
    }

    printk("k: base module init\r\n");

    return 0;

out_class:
    class_destroy(new_chrdev.class);
out_cdev:
    cdev_del(&new_chrdev.c_dev);
out:
    unregister_chrdev_region(new_chrdev.dev_id, CHRDEVBASE_NUM);

    return err;
}

static void __exit chrdevbase_exit(void)
{
    device_destroy(new_chrdev.class, new_chrdev.dev_id);
    class_destroy(new_chrdev.class);
    cdev_del(&new_chrdev.c_dev);
    unregister_chrdev_region(new_chrdev.dev_id, CHRDEVBASE_NUM);

    printk("k: base module exit!\r\n");
}

module_init(chrdevbase_init);
module_exit(chrdevbase_exit);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("tyustli");
MODULE_INFO(intree, "Y"); /* loading out-of-tree module taints kernel */

字符设备 APP

#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[] = { "user data!" };

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

    if (argc != 3) {
        printf("u: error Usage!\r\n");
        return -1;
    }

    filename = argv[1];

    /* 打开驱动文件 */
    fd = open(filename, O_RDWR);
    if (fd < 0) {
        printf("u: can't open file %s\r\n", filename);
        return -1;
    }

    /* 从驱动文件读取数据 */
    if (atoi(argv[2]) == 1) {
        retvalue = read(fd, readbuf, 50);
        if (retvalue < 0) {
            printf("u: read file %s failed!\r\n", filename);
        } else {
            /*  读取成功,打印出读取成功的数据 */
            printf("u: read data:%s\r\n", readbuf);
        }
    }

    /* 向设备驱动写数据 */
    if (atoi(argv[2]) == 2) {
        memcpy(writebuf, usrdata, sizeof(usrdata));
        retvalue = write(fd, writebuf, 50);
        if (retvalue < 0) {
            printf("u: write file %s failed!\r\n", filename);
        }
    }

    /* 关闭设备 */
    retvalue = close(fd);
    if (retvalue < 0) {
        printf("u: can't close file %s\r\n", filename);
        return -1;
    }

    return 0;
}

生成 dev 节点的原理

dev 目录下的节点不是由驱动本身生成的,是由文件系统中的工具 mdev 生成的;

当系统启动后,加载完内核再去加载文件系统,执行文件系统中的脚本,脚本会执行 mdev -s 命令(即执行 mdev程序),该命令会去遍历/sys/class下的所有文件,寻找所有名为 dev的文件,dev文件保存了每个驱动的主设备号、次设备号,以及驱动名。

生成设备节点的依赖有两个

  • /sys/class 有驱动的节点信息,即 dev 文件
  • 执行 mdev -s创建 /dev 下的节点
ls /sys/class

在这里插入图片描述
这里文件是有的,这个时候执行

mdev -s

并查看 /dev/ 目录

ls /dev

在这里插入图片描述
可以看到设备节点文件自动创建了

配置内核自动创建设备节点

配置 linux 内核使之自动创建设备节点文件
在这里插入图片描述
在内核中 make menuconfig 使能 CONFIG_UEVENT_HELPER

在这里插入图片描述

模块使用

linux 自动创建设备节点之后,就不用手动创建了,可以直接查看 /dev 目录下已经生成了设备节点文件

ls /dev

模块安装

modprobe my_module

模块使用

lib/modules/6.5.7+/my_app /dev/chrdevbase 1

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1174859.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

10 DETR 论文精读【论文精读】End-to-End Object Detection with Transformers

DETR 这篇论文&#xff0c;大家为什么喜欢它&#xff1f;为什么大家说它是一个目标检测里的里程碑式的工作&#xff1f;而且为什么说它是一个全新的架构&#xff1f; 大家好&#xff0c;今天我们来讲一篇 ECC V20 的关于目标检测的论文。它的名字想必大家都不陌生&#xff0c;也…

Python---字符串中的查找方法--index()--括号里是要获取的字符串

index()方法其功能与find()方法完全一致&#xff0c;唯一的区别在于当要查找的子串没有出现在字符串中时&#xff0c;find()方法返回-1&#xff0c;而index()方法则直接 报错。 find()方法相关链接&#xff1a;Python---字符串中的查找方法--find&#xff08;&#xff09;--括…

排序——冒泡排序

冒泡排序的基本思想 从前往后&#xff08;或从后往前&#xff09;两两比较相邻元素的值&#xff0c;若为逆序&#xff08;即 A [ i − 1 ] < A [ i ] A\left [ i-1\right ]<A\left [ i\right ] A[i−1]<A[i]&#xff09;&#xff0c;则交换它们&#xff0c;直到序列…

【漏洞复现】Nginx_(背锅)解析漏洞复现

感谢互联网提供分享知识与智慧&#xff0c;在法治的社会里&#xff0c;请遵守有关法律法规 文章目录 1.1、漏洞描述1.2、漏洞等级1.3、影响版本1.4、漏洞复现1、基础环境2、漏洞扫描3、漏洞验证 1.5、深度利用GetShell 1.1、漏洞描述 这个漏洞其实和代码执行没有太大关系&…

基础Redis-Java客户端操作介绍

Java客户端操作介绍 2.基础-Redis的Java客户端a.介绍b.Jedisc.Jedis连接池d.SpringDataRedise.SpringDataRedis的序列化方式f.StringRedisTemplate 2.基础-Redis的Java客户端 a.介绍 Jedis 以Redis命令作为方法名称&#xff0c;学习成本低&#xff0c;简单实用。但是Jedis实例…

C++: 类和对象(中)

文章目录 1. 类的6个默认成员函数2. 构造函数构造函数概念构造函数特性特性1,2,3,4特性5特性6特性7 3. 析构函数析构函数概念析构函数特性特性1,2,3,4特性5特性6 4. 拷贝构造函数拷贝构造函数概念拷贝构造函数特性特性1,2特性3特性4特性5 5. 运算符重载一般运算符重载赋值运算符…

大模型进展的主要观点综述

大模型模式的意义可以用两个词来概括&#xff1a;涌现和同质化。涌现意味着一个系统的行为是隐含诱导的&#xff0c;而不是明确构建的;它既是科学兴奋的源泉&#xff0c;也是对意外后果的一种焦虑。同质化表示在广泛的应用程序中构建机器学习系统的方法的整合;它为许多任务提供…

(14)学习笔记:动手深度学习(Pytorch神经网络基础)

文章目录 神经网络的层与块块的基本概念自定义块 问答 神经网络的层与块 块的基本概念 以多层感知机为例&#xff0c; 整个模型接受原始输入&#xff08;特征&#xff09;&#xff0c;生成输出&#xff08;预测&#xff09;&#xff0c; 并包含一些参数&#xff08;所有组成层…

FreeRTOS笔记【一】 任务的创建(动态方法和静态方法)

一、任务创建和删除API函数 函数描述xTaskCreate()使用动态的方法创建一个任务xTaskCreateStatic()使用静态的方法创建一个任务xTaskCreateRestricted()创建一个使用MPU进行限制的任务&#xff0c;相关内存使用动态内存分配vTaskDelete()删除一个任务 二、动态创建任务 2.1 …

国内外一级市场TOP10股权投资研究报告

前言 在金融领域&#xff0c;令人心跳加速的时刻往往来自于那些领先群雄的成就&#xff0c;无论是在科技创新、生产效率还是投资回报上。想象一下&#xff0c;如果财富的累积只是微不足道的&#xff0c;那又何异于日复一日的朝九晚五呢&#xff1f;随着时间的推移&#xff0c;…

【C++】详解IO流(输入输出流+文件流+字符串流)

文章目录 一、标准输入输出流1.1提取符>>&#xff08;赋值给&#xff09;与插入符<<&#xff08;输出到&#xff09;理解cin >> a理解ifstream&#xff08;读&#xff09; >> a例子 1.2get系列函数get与getline函数细小但又重要的区别 1.3获取状态信息…

数据包端到端的流程

流程 A给F发送一个数据包的流程&#xff1a; 首先 A&#xff08;192.168.0.1&#xff09;通过子网掩码&#xff08;255.255.255.0&#xff09;计算出自己与 F&#xff08;192.168.2.2&#xff09;并不在同一个子网内&#xff0c;于是决定发送给默认网关&#xff08;192.168.0.…

307. 区域和检索 - 数组可修改

给你一个数组 nums &#xff0c;请你完成两类查询。 其中一类查询要求 更新 数组 nums 下标对应的值 另一类查询要求返回数组 nums 中索引 left 和索引 right 之间&#xff08; 包含 &#xff09;的nums元素的 和 &#xff0c;其中 left < right 实现 NumArray 类&#xff…

数据分析与数据挖掘期末复习,附例题及答案

文章目录 一、概述1.数据挖掘能做什么&#xff1f;2.数据挖掘在哪些方面有应用&#xff1f;3.数据挖掘与数据分析的区别&#xff1f;4.数据挖掘的四大类模型5.什么是数据挖掘&#xff1f;6.数据挖掘的常用方法&#xff1f; 二、数据1.余弦相似度、欧几里得距离2.近似中位数 三、…

刚入职因为粗心大意,把事情办砸了,十分后悔

刚入职&#xff0c;就踩大坑&#xff0c;相信有很多朋友有我类似的经历。 5年前&#xff0c;我入职一家在线教育公司&#xff0c;新的公司福利非常好&#xff0c;各种零食随便吃&#xff0c;据说还能正点下班&#xff0c;一切都超出我的期望&#xff0c;“可算让我找着神仙公司…

数据结构与算法【02】—线性表

CSDN系列专栏&#xff1a;数据结构与算法专栏 针对以前写的数据结构与算法系列重写(针对文字描述、图片、错误修复)&#xff0c;改动会比较大&#xff0c;一直到更新完为止 前言 通过前面数据结构与算法基础知识我们知道了数据结构的一些概念和重要性&#xff0c;那么本章总结…

新技术前沿-2023-应用GPT提问模板写技术文章

参考一份万能的GPT提问模版&#xff01;直接套用&#xff01; 参考用GPT写技术文章是真爽&#xff01; 参考码住这篇 8200 字 ChatGPT 实战指南&#xff01;&#xff01; 1 GPT提问模板 想让GPT回答的内容符合我们所希望的&#xff0c;最最重要的一点就在于我们如何提问。提问…

NFS服务以及静态路由及临时IP配置

目录 一、NFC服务基础知识 1、NFS服务初相识 2、NFS服务工作原理 二、NFC服务基础操作 1、NFS服务端配置 2、NFS服务 - exports 相关参数 3、NFS服务 - 命令相关 三、RPC 远程调度 四、静态路由及临时IP配置 1、Linux 静态路由相关命令 2、Linux 临时IP地址添加与删除…

【漏洞复现】Nginx_0.7.65_空字节漏洞

感谢互联网提供分享知识与智慧&#xff0c;在法治的社会里&#xff0c;请遵守有关法律法规 文章目录 1.1、漏洞描述1.2、漏洞等级1.3、影响版本1.4、漏洞复现1、基础环境2、漏洞扫描3、漏洞验证 1.1、漏洞描述 1.2、漏洞等级 1.3、影响版本 0.7.65 1.4、漏洞复现 1、基础环…

Redis那些事儿(三)

文章目录 1. 前言2. 常用api介绍3. 需求假设&#xff08;获取离我最近的停车场&#xff09;4. 代码示例 1. 前言 接着上一篇Redis那些事儿&#xff08;二&#xff09; &#xff0c;这一篇主要介绍Redis基于Geo数据结构实现的地理服务&#xff0c;它提供了一种方便的方式来存储和…