【Linux】【驱动】自动创建设备节点

news/2024/10/23 9:27:03/

【Linux】【驱动】自动创建设备节点

  • 驱动代码
  • 操作指令
    • linux端
    • 从机端

这里展示了如何自动的方式去创建一个字符类的节点
下面就是需要调用到的程序

函数

 void cdev_init(struct cdev *, const struct file_operations *);

第一个参数 要初始化的 cdev
第二个参数 文件操作集 cdev->ops = fops; //实际就是把文件操作集写给 ops
功能 cdev_init()函数用于初始化 cdev 的成员,并建立 cdev 和 file_operations 之间的连接。

函数

 int cdev_add(struct cdev *, dev_t, unsigned);

第一个参数 cdev 的结构体指针
第二个参数 设备号
第三个参数 次设备号的数量
功能 cdev_alloc()函数用于动态申请一个 cdev 内存

void cdev_del(struct cdev *);

cdev 的结构体指针

生成设备节点
字符设备注册完以后不会自动生成设备节点。我们需要使用 mknod 命令创建一个设备节点
格式:mknod 名称 类型 主设备号 次设备号

mknod /dev/test c 247 0

驱动代码

代码实现的流程

从 hello_init 函数开始看,

  1. 注册设备号,
  2. 初始化 cdev
  3. 向系统注册设备
  4. 创建 class 类
  5. 在 class 类下创建设备

从hello_exit 来看

  1. 注销设备号
  2. 删除设备
  3. 注销设备
  4. 删除类
#include <linux/init.h>
#include <linux/module.h>     //最基本的文件,支持动态添加和卸载模块。
#include <linux/fs.h>        //包含了文件操作相关 struct 的定义,例如大名鼎鼎的 struct file_operations
#include <linux/kdev_t.h>
#include <linux/cdev.h> //对字符设备结构 cdev 以及一系列的操作函数的定义。//包含了 cdev 结构及相关函数的定义。
#include <linux/device.h> //包含了 device、class 等结构的定义#define DEVICE_NUMBER 1 		//定义次设备号的个数
#define DEVICE_SNAME "schrdev"  //定义静态注册设备的名称
#define DEVICE_ANAME "achrdev"  //定义动态注册设备的名称
#define DEVICE_MINOR_NUMBER 0 	//定义次设备号的起始地址#define DEVICE_CLASS_NAME "chrdev_class" //宏定义类名
#define DEVICE_NODE_NAME "chrdev_test" //宏定义设备节点的名字static int major_num, minor_num; //定义主设备号和次设备号struct class *class; //定义类
struct device *device; /* 设备 */
struct cdev cdev;//定义一个 cdev 结构体module_param(major_num,int,S_IRUSR); //驱动模块传入普通参数 major_num
module_param(minor_num ,int,S_IRUSR);//驱动模块传入普通参数 minor_numdev_t dev_num;/**
* @description: 打开设备
* @param {structinode} *inode:传递给驱动的 inode
* @param {structfile} *file:设备文件,file 结构体有个叫做 private_data 的成员变量,
* 一般在 open 的时候将 private_data 指向设备结构体。
* @return: 0 成功;其他 失败
*/
int chrdev_open(struct inode *inode, struct file *file)
{printk("chrdev_open\n");return 0;
}// 设备操作函数结构体
struct file_operations chrdev_ops = {.owner = THIS_MODULE,.open = chrdev_open};/**
* @description: 驱动入口函数
* @param {*}无
* @return {*} 0 成功;其他 失败
*/
static int hello_init(void)
{int ret;//函数返回值if(major_num){/*静态注册设备号*/printk("major_num = %d\n",major_num);//打印传入进来的主设备号printk("minor_num = %d\n",minor_num);//打印传入进来的次设备号dev_num = MKDEV(major_num,minor_num);//MKDEV 将主设备号和次设备号合并为一个设备号ret = register_chrdev_region(dev_num, DEVICE_NUMBER,DEVICE_SNAME);//注册设备号if(ret<0){printk("register_chrdev_region error\n");}//静态注册设备号成功,则打印。printk("register_chrdev_region ok\n");}else{/*动态注册设备号*/ret = alloc_chrdev_region(&dev_num,DEVICE_MINOR_NUMBER,1, DEVICE_ANAME);if(ret<0){printk("alloc_chrdev_region error\n");}//动态注册设备号成功,则打印printk("alloc_chrdev_region ok\n");major_num =MAJOR(dev_num); //将主设备号取出来minor_num = MINOR(dev_num);//将次设备号取出来printk("major_num = %d\n",major_num);//打印传入进来的主设备号printk("minor_num = %d\n",minor_num);//打印传入进来的次设备号}// 初始化 cdevcdev.owner = THIS_MODULE;//cdev_init 函数初始化 cdev 结构体成员变量cdev_init(&cdev, &chrdev_ops);//完成字符设备注册到内核cdev_add(&cdev, dev_num, DEVICE_NUMBER);//创建类class = class_create(THIS_MODULE, DEVICE_CLASS_NAME);// 在 class 类下创建设备device = device_create(class, NULL, dev_num, NULL, DEVICE_NODE_NAME);return 0;
}//drivers for exit 
static void hello_exit(void)
{//注销设备号unregister_chrdev_region(MKDEV(major_num, minor_num), DEVICE_NUMBER);//删除设备cdev_del(&cdev);//注销设备device_destroy(class, dev_num);//删除类class_destroy(class);printk("gooodbye! \n");}module_init(hello_init);
module_exit(hello_exit);MODULE_LICENSE("GPL");
MODULE_AUTHOR("Chris");

下面就是app的代码

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>int main(int argc,char *argv[])
{int fd;char buf[64] = {0};fd = open("/dev/chrdev_test",O_RDWR); //打开设备节点if(fd < 0){perror("open error \n");return fd;}//read(fd,buf,sizeof(buf)); //从文件中读取数据放入缓冲区中close(fd);return 0;
}

操作指令

linux端

arm-buildroot-linux-gnueabihf-gcc -o app app.c
cp app /home/book/nfs_rootfs/

从机端

驱动卸载掉,再加载新编译好的的驱动

rmmod chrdev
insmod chrdev.ko

我们输入以下命令查看/sys/class 下面是否生成类,

ls /sys/class/chrdev_class/

查看下是否生成了设备节点

ls /sys/class/

来验证生成的设备节点是否可以使用

 ./app

在这里插入图片描述


http://www.ppmy.cn/news/1069086.html

相关文章

专业制造一体化ERP系统,专注于制造工厂生产管理信息化,可定制-亿发

制造业是国民经济的支柱产业&#xff0c;对于经济发展和竞争力至关重要。在数字化和智能化趋势的推动下&#xff0c;制造业正处于升级的关键时期。而ERP系统&#xff0c;即企业资源计划系统&#xff0c;能够将企业的各个业务环节整合起来&#xff0c;实现资源的有效管理和信息的…

AI大模型的使用-让AI帮你写单元测试

1.体验多步提示语 我们本节就让AI帮我们写一个单元测试&#xff0c;全程用AI给我们答案&#xff0c;首先单元测试前需要有代码&#xff0c;那么我们让AI给我们生成一个代码&#xff0c;要求如下&#xff1a; 用Python写一个函数&#xff0c;进行时间格式化输出&#xff0c;比…

判断聚类 n_clusters

目录 基本原理 代码实现&#xff1a; 肘部法则&#xff08;Elbow Method&#xff09;&#xff1a; 轮廓系数&#xff08;Silhouette Coefficient&#xff09; Gap Statistic&#xff08;间隙统计量&#xff09;&#xff1a; Calinski-Harabasz Index&#xff08;Calinski-…

Java进阶篇--泛型

前言 Java 泛型&#xff08;generics&#xff09;是 JDK 5 中引入的一个新特性, 泛型提供了编译时类型安全检测机制&#xff0c;该机制允许程序员在编译时检测到非法的类型。它允许在定义类、接口和方法时使用类型参数。这种技术使得在编译期间可以使用任何类型&#xff0c;而…

StarRocks入门到熟悉

1、部署 1.1、注意事项 需要根据业务需求设计严谨的集群架构&#xff0c;一般来说&#xff0c;需要注意以下几项&#xff1a; 1.1.1、FE数量及高可用 FE的Follower要求为奇数个&#xff0c;且并不建议部署太多&#xff0c;通常我们推荐部署1个或3个Follower。在三个Followe…

Flutter可执行屏幕动画的AnimateView

1.让动画使用起来就像使用widget。 2.可自定义动画。 3.内置平移动画。 演示&#xff1a; 代码: import dart:math; import package:flutter/cupertino.dart;class AnimateView extends StatefulWidget {///子Widgetfinal Widget child;///动画自定义final IAnimate? anim…

复数的四则运算(java版)

复数的四则运算&#xff08;java版&#xff09; 目录 复数的四则运算&#xff08;java版&#xff09;介绍复数的四则运算实现思路代码1、封装复数类2、测试复数类3、代码测试结果 介绍 复数&#xff0c;为实数的延伸&#xff0c;它使任一多项式方程都有根。复数当中有个“虚数单…

设计模式—职责链模式(Chain of Responsibility)

目录 思维导图 什么是职责链模式&#xff1f; 有什么优点呢&#xff1f; 有什么缺点呢&#xff1f; 什么场景使用呢&#xff1f; 代码展示 ①、职责链模式 ②、加薪代码重构 思维导图 什么是职责链模式&#xff1f; 使多个对象都有机会处理请求&#xff0c;从而避免请…