lv15 驱动高级设备模型 1

news/2024/3/4 8:54:55

之前的驱动操作称为硬编

一、起源

仅devfs(dev目录),导致开发不方便以及一些功能难以支持:

  1. 热插拔(如何插入一个设备然后找到设备的驱动应用到程序中)

  2. 不支持一些针对所有设备的统一操作(如电源管理)

  3. 不能自动mknod

  4. 用户查看不了设备信息(必须cat /proc/devices 查看组设备号)

  5. 设备信息硬编码,导致驱动代码通用性差,即没有分离设备和驱动

二、新方案

总设计原则:

uevent机制:sysfs + uevent + udevd(上层app)

2.1 sysfs: 一种用内存模拟的文件系统,系统启动时mount到/sys目录

sysfs 是 Linux 内核提供的一种虚拟文件系统,它将内核中设备、驱动程序和总线等信息以文件和目录的形式呈现给用户空间。当有新的设备插入或移除时,相关的信息会被添加、修改或删除,并通过 uevent 发送到用户空间。

sysfs用途:(类似于windows的设备管理器)

  1. 建立系统中总线、驱动、设备三者之间的桥梁

  2. 向用户空间展示内核中各种设备的拓扑图

  3. 提供给用户空间对设备获取信息和操作的接口,部分取代ioctl功能

sysfs在内核中的组成要素在用户空间/sys下的显示
内核对象(kobject,可以包含多个子目录)目录
对象属性(attribute)文件
对象关系(relationship)链接(Symbolic Link)

四个基本结构

类型所包含的内容内核数据结构对应/sys项
设备(Devices)设备是此模型中最基本的类型,以设备本身的连接按层次组织struct device/sys/devices/?/?/.../
驱动(Drivers)在一个系统中安装多个相同设备,只需要一份驱动程序的支持struct device_driver/sys/bus/pci/drivers/?/
总线(Bus)整个总线级别对此总线上连接的所有设备进行管理struct bus_type/sys/bus/?/
类别(Classes)这是按照功能进行分类组织的设备层次树;如 USB 接口和 PS/2 接口的鼠标都是输入设备,都会出现在/sys/class/input/下struct class/sys/class/?/

 

 

目录组织结构:

/sys下的子目录所包含的内容
/sys/devices这是内核对系统中所有设备的分层次表达模型,也是/sys文件系统管理设备的最重要的目录结构;
/sys/dev这个目录下维护一个按字符设备和块设备的主次号码(major:minor)链接到真实的设备(/sys/devices下)的符号链接文件;
/sys/bus这是内核设备按总线类型分层放置的目录结构, devices 中的所有设备都是连接于某种总线之下,在这里的每一种具体总线之下可以找到每一个具体设备的符号链接,它也是构成 Linux 统一设备模型的一部分;
/sys/class这是按照设备功能分类的设备模型,如系统所有输入设备都会出现在/sys/class/input 之下,而不论它们是以何种总线连接到系统。它也是构成 Linux 统一设备模型的一部分;
/sys/kernel这里是内核所有可调整参数的位置,目前只有 uevent_helper, kexec_loaded, mm, 和新式的slab 分配器等几项较新的设计在使用它,其它内核可调整参数仍然位于sysctl(/proc/sys/kernel) 接口中;
/sys/module这里有系统中所有模块的信息,不论这些模块是以内联(inlined)方式编译到内核映像文件(vmlinuz)中还是编译为外部模块(ko文件),都可能会出现在/sys/module 中
/sys/power这里是系统中电源选项,这个目录下有几个属性文件可以用于控制整个机器的电源状态,如可以向其中写入控制命令让机器关机、重启等。

2.2 uevent

uevent 是一种内核事件通知机制,它负责在发生设备插拔事件时生成事件消息,并将消息发送给用户空间。这些消息可以包含有关设备的信息,例如设备的类型、标识符和状态等。

2.3 udevd

udevd 是一个在用户空间运行的守护进程,它监听来自内核的 uevent 消息,并根据这些消息来管理设备节点的创建和销毁。当 udevd 接收到 uevent 消息时,它会执行预定义的规则,以便为新插入的设备创建相应的设备节点,并在设备移除时进行清理工作。

三、代码中自动mknod

目的:自动在dev目录下创建设备,省去手工mknod

带有面向思想的内容去理解struct class

struct class *class_create(struct module *owner, const char *name);
/** 功能:在/sys/class生成一个目录,目录名由name指定* 参数:struct module *owner - THIS_MODULEconst char *name - 目录名* 返回值  成功:class指针   失败:NULL
*/
/*
辅助接口:可以定义一个struct class 的指针变量cls来接受返回值,然后通过IS_ERR(cls)判断是否失败;
IS_ERR(cls);成功----------------->0
IS_ERR(cls);失败----------------->非0
PTR_ERR(cls);来获得失败的返回错误码;
*/

 销毁struct classs

void class_destroy(struct class *cls)
/*
* 功能:删除class_create生成目录
* 参数:struct class *cls - class指针
* 返回值
*/

创建节点 (重点关注*fmt和...最后两个参数,用法类似printk,可以方便创建设备)

struct device *device_create(struct class *class, struct device *parent,dev_t devt, void *drvdata, const char *fmt, ...)
/** 功能:在/sys/class目录下class_create生成目录再生成一个子目录与该设备相对应,发uevent让应用程序udevd创建设备文件* 参数:struct class *class - class指针struct device *parent - 父对象,一般NULLdev_t devt - 设备号void *drvdata - 驱动私有数据,一般NULLconst char *fmt - 字符串的格式... - 不定参数* 返回值成功:device指针失败:NULL*/

 删除devicecreate生成的目录

void device_destroy(struct class *class, dev_t devt)
/** 功能:删除device_create生成目录* 参数:struct class *class - class指针dev_t devt - 设备号* 返回值
*/

3.1 利用second.c的代码修改

second.c

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/wait.h>
#include <linux/sched.h>
#include <linux/poll.h>
#include <linux/device.h>
#include <asm/uaccess.h>
#include <asm/atomic.h>int major = 11;
int minor = 0;
int mysecond_num  = 1;struct mysecond_dev
{struct cdev mydev;atomic_t openflag;//1 can open, 0 can not openstruct timer_list timer;int second;struct class *cls;       //<-----------------struct device *dvs;       //<-----------------
};struct mysecond_dev gmydev;void timer_func(unsigned long arg)
{struct mysecond_dev * pmydev = (struct mysecond_dev *)arg;pmydev->second++;mod_timer(&pmydev->timer, jiffies + 1 * HZ);}int mysecond_open(struct inode *pnode,struct file *pfile)
{struct mysecond_dev *pmydev = NULL;pfile->private_data =(void *) (container_of(pnode->i_cdev,struct mysecond_dev,mydev));pmydev = (struct mysecond_dev *)pfile->private_data;//运算后结果为0则返回真,否则返回假表示设备已经被打开if(atomic_dec_and_test(&pmydev->openflag)){pmydev->timer.expires = jiffies + 1 * HZ;pmydev->timer.function = timer_func;pmydev->timer.data = (unsigned long)pmydev;add_timer(&pmydev->timer);return 0;}else{atomic_inc(&pmydev->openflag);printk("The device is opened already\n");return -1;}
}int mysecond_close(struct inode *pnode,struct file *pfile)
{struct mysecond_dev *pmydev = (struct mysecond_dev *)pfile->private_data;del_timer(&pmydev->timer);atomic_set(&pmydev->openflag,1);return 0;
}int mysecond_read(struct file *pfile,char __user *puser, size_t size, loff_t *p_pos)
{struct mysecond_dev *pmydev = (struct mysecond_dev *)pfile->private_data;int ret = 0;if(size < sizeof(int)){printk("the expect read size is invalid\n");return -1;}if(size >= sizeof(int)){size= sizeof(int);}ret = copy_to_user(puser, &pmydev->second, size);if(ret){printk("copy to user failed\n");return -1;}return 0;
}struct file_operations myops = {.owner = THIS_MODULE,.open = mysecond_open,.release = mysecond_close,.read = mysecond_read,
};int __init mysecond_init(void)
{int ret = 0;dev_t devno = MKDEV(major,minor);/*申请设备号*/ret = register_chrdev_region(devno,mysecond_num,"mysecond");if(ret){ret = alloc_chrdev_region(&devno,minor,mysecond_num,"mysecond");if(ret){printk("get devno failed\n");return -1;}major = MAJOR(devno);//容易遗漏,注意}/*给struct cdev对象指定操作函数集*/	cdev_init(&gmydev.mydev,&myops);/*将struct cdev对象添加到内核对应的数据结构里*/gmydev.mydev.owner = THIS_MODULE;cdev_add(&gmydev.mydev,devno,mysecond_num);//初始化置位1atomic_set(&gmydev.openflag,1);//初始化定时器init_timer(&gmydev.timer);gmydev.cls = class_create(THIS_MODULE, "mysecond");    //<-----------------if(IS_ERR(gmydev.cls)){printk("class_create failed\n");cdev_del(&gmydev.mydev);unregister_chrdev_region(devno,mysecond_num);return -1;}gmydev.dvs = device_create(gmydev.cls, NULL, devno, NULL,"mysec");   //<-----------------if(gmydev.dvs == NULL){printk("device_create failed\n");class_destroy(gmydev.cls);cdev_del(&gmydev.mydev);unregister_chrdev_region(devno,mysecond_num);return -1;}return 0;
}void __exit mysecond_exit(void)
{dev_t devno = MKDEV(major,minor);device_destroy(gmydev.cls, devno);    //<-----------------class_destroy(gmydev.cls);            //<-----------------cdev_del(&gmydev.mydev);unregister_chrdev_region(devno,mysecond_num);
}MODULE_LICENSE("GPL");module_init(mysecond_init);
module_exit(mysecond_exit);

修改Makefile

ifeq ($(KERNELRELEASE),)ifeq ($(ARCH),arm)
KERNELDIR ?= /home/linux/Linux_4412/kernel/linux-3.14
ROOTFS ?= /opt/4412/rootfs
else
KERNELDIR ?= /lib/modules/$(shell uname -r)/build
endif
PWD := $(shell pwd)modules:$(MAKE) -C $(KERNELDIR) M=$(PWD) modulesmodules_install:$(MAKE) -C $(KERNELDIR) M=$(PWD) modules INSTALL_MOD_PATH=$(ROOTFS) modules_installclean:rm -rf  *.o  *.ko  .*.cmd  *.mod.*  modules.order  Module.symvers   .tmp_versionselseCONFIG_MODULE_SIG=n
obj-m += mychar.o
obj-m += mychar_poll.o
obj-m += openonce_atomic.o
obj-m += openonce_spinlock.o
obj-m += mychar_sema.o
obj-m += mychar_mutex.o
obj-m += second.o
#obj-m += leddrv.o
#obj-m += leddrv_dt.o
#obj-m += fs4412_key2.o
#obj-m += fs4412_key2_tasklet.o
#obj-m += fs4412_key2_workqueue.oendif

编译

测试,不需要我们自己mknod

 


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

相关文章

网络安全漏洞管理十大度量指标

当前&#xff0c;网络安全漏洞所带来的风险及产生的后果&#xff0c;影响到网络空间乃至现实世界的方方面面&#xff0c;通信、金融、能源、电力、铁路、医院、水务、航空、制造业等行业各类勒索、数据泄露、供应链、钓鱼等网络安全攻击事件层出不穷。因此&#xff0c;加强对漏…

OpenCV-36 多边形逼近与凸包

目录 一、多边形的逼近 二、凸包 一、多边形的逼近 findContours后的轮廓信息countours可能过于复杂不平滑&#xff0c;可以用approxPolyDP函数对该多边形曲线做适当近似&#xff0c;这就是轮廓的多边形逼近。 apporxPolyDP就是以多边形去逼近轮廓&#xff0c;采用的是Doug…

MySQL ——group by子句使用with rollup

group by 子句使用with rollup关键字之后&#xff0c;具有分组加和的功能。即&#xff1a;在所有的分组记录之后&#xff0c;自动新增一条记录&#xff0c;从全局计算所有记录的数据。 0 问题描述 求出每年的学生平均成绩&#xff0c;及历史至今的平均成绩&#xff0c;结果保留…

matlab发送串口数据,并进行串口数据头的添加,我们来看下pwm解析后并通过串口输出的效果

uintt16位的话会在上面前面加上00&#xff0c;16位的话一定是两个字节&#xff0c;一共16位的数据 如果是unint8的话就不会&#xff0c; 注意这里给的是13&#xff0c;但是现实的00 0D&#xff0c;这是大小端的问题&#xff0c;在matlanb里设置&#xff0c;我们就默认用这个模式…

MATLAB知识点:unifrnd函数(★★★☆☆)生成任意区间内均匀分布的随机数

​讲解视频&#xff1a;可以在bilibili搜索《MATLAB教程新手入门篇——数学建模清风主讲》。​ MATLAB教程新手入门篇&#xff08;数学建模清风主讲&#xff0c;适合零基础同学观看&#xff09;_哔哩哔哩_bilibili 节选自第3章&#xff1a;课后习题讲解中拓展的函数 在讲解第…

米贸搜|Facebook在购物季使用的Meta广告投放流程

一、账户简化 当广告系列开始投放后&#xff0c;每个广告组都会经历一个初始的“机器学习阶段”。简化账户架构可以帮助AI系统更快获得广告主所需的成效。例如&#xff1a; 每周转化次数超过50次的广告组&#xff0c;其单次购物费用要低28%&#xff1b;成功结束机器学习阶段的…

Vue3中Setup概述和使用(三)

一、引入Setup 1、Person.Vue 与Vue3编写简单的App组件(二) 中的区别是&#xff1a;取消data、methods等方法,而是将数据和方法定义全部放进setup中。 <template><div class"person"><h1>姓名:{{name}}</h1><h1>年龄:{{age}}</h…

Arduino中关于error: reference to ‘map‘ is ambiguous

此错误信息指示在您的 Arduino 代码中&#xff0c;map 函数的引用不明确。这是因为 map 同时存在于 Arduino API 和 C std 命名空间&#xff08;通过包含的 <map> 头文件&#xff09;中&#xff0c;导致编译器无法确定您打算使用哪个版本。 为了解决这个问题&#xff0c…

C# CAD2016获取数据操作BlockTableRecord、Polyline、DBObject

一、数据操作说明 //DBObject 基础类 DBObject dbObj (DBObject)tr.GetObject(outerId, OpenMode.ForRead); //Polyline 线段类 Polyline outerPolyline (Polyline)tr.GetObject(outerId, OpenMode.ForRead); //BlockTableRecord 块表类 BlockTableRecord modelSpace (Bloc…

学习Android的第四天

目录 Android FrameLayout ( 帧布局 ) FrameLayout size 大小 FrameLayout 属性 Android GridLayout ( 网格布局 ) GridLayout 属性 计算器布局 Android AbsoluteLayout 绝对布局 AbsoluteLayout 四大控制属性 Android FrameLayout ( 帧布局 ) FrameLayout 是 Android…

2024年-视觉AI检测的面试题目总结

Hello,各位&#xff0c;面试大宝典又来拉; 持续更新&#xff1b;一定要理清自己简历上的项目&#xff0c;因为简历是你给面试官的入口&#xff0c;会根据你的简历问问题&#xff1b;目前leetcode水平169题&#xff1b;持续更新&#xff1b; c八股文和python的八股文最好持续看&…

C++初阶:容器(Containers)list常用接口详解

介绍完了vector类的相关内容后&#xff0c;接下来进入新的篇章&#xff0c;容器list介绍&#xff1a; 文章目录 1.list的初步介绍2.list的定义&#xff08;constructor&#xff09;3.list迭代器&#xff08; iterator &#xff09;4.string的三种遍历4.1迭代器4.2范围for循环 5…

AutoSAR(基础入门篇)8.3-IO相关驱动(二)

目录 一、ICU驱动 1、边缘计数(Edge Count) 2、边缘检测(Signal Edge Detection) 3、信号测量(Signal Measurement)

「优选算法刷题」:搜索插入位置

一、题目 给定一个排序数组和一个目标值&#xff0c;在数组中找到目标值&#xff0c;并返回其索引。如果目标值不存在于数组中&#xff0c;返回它将会被按顺序插入的位置。 请必须使用时间复杂度为 O(log n) 的算法。 示例 1: 输入: nums [1,3,5,6], target 5 输出: 2示例…

【第三十五节】idea项目的创建以及setting和Project Structure的设置

项目创建 Project Structure的设置 点击file ~ Project Structure 进入 进入view/Appearance 选中Toolbar 就会出现状态栏

龙芯开启ssh服务——使用Putty连接

本文采用龙芯3A6000处理器&#xff0c;Loongnix操作系统。 为了能使用其他电脑远程操控龙芯电脑&#xff0c;需要打开loongnix的ssh服务&#xff0c;并在其他电脑里使用putty连接loongnix。 1 修改ssh配置文件 命令行输入&#xff1a; sudo vim /etc/ssh/sshd_config按下i插…

vue day06

1、路由模块封装 2、声明式导航 实现导航高亮效果 直接通过这两个类名对相应标签设置样式 点击a链接进入my页面时&#xff0c;a链接 我的音乐高亮&#xff0c;同时my下的a、b页面中的 我的音乐也有router-link-active类&#xff0c;但没有精确匹配的类&#xff08;只有my页…

【Linux进阶之路】网络——“?“(上)

文章目录 一、历史发展1. 独立形态2. 互联形态3. 局域网 二、网络协议1.OSI七层协议2.TCP/IP四&#xff08;五&#xff09;层模型 三、网络通信1.封装与解包2.数据的传输1.局域网2.广域网 总结尾序 本篇文章的目的是带大家初步认识网络&#xff0c;为后面的网络编程打下基础&am…

Oracle恢复数据库某张表某一时刻的数据

测试时误删数据&#xff0c;或是需要还原数据反复测试sql&#xff0c;可以用Oracle的闪回功能使数据库恢复到某一时刻。 恢复数据到某一时刻 -- 假设表名为 HOLIDAY-- 开启行移动 ALTER TABLE HOLIDAY ENABLE ROW MOVEMENT; -- 恢复数据到某一时刻 FLASHBACK TABLE HOLIDAY TO…

【MATLAB源码-第137期】基于matlab的NOMA系统和OFDMA系统对比仿真。

操作环境&#xff1a; MATLAB 2022a 1、算法描述 NOMA&#xff08;非正交多址&#xff09;和OFDMA&#xff08;正交频分多址&#xff09;是两种流行的无线通信技术&#xff0c;广泛应用于现代移动通信系统中&#xff0c;如4G、5G和未来的6G网络。它们的设计目标是提高频谱效…
最新文章