驱动之设备模型

news2024/11/24 17:18:07

1. 起源与新方案

1.1 起源

仅devfs,导致开发不方便以及一些功能难以支持

  1. 热插拔
  2. 不支持一些针对所有设备的同意操作(如电源管理)
  3. 不能自动mknod
  4. 用户查看不了设备信息
  5. 设备信息硬编码,导致驱动代码通用性差,即没有分离设备和驱动

1.2 新方案

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

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

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这里是系统中电源选项,这个目录下有几个属性文件可以用于控制整个机器的电源状态,如可以向其中写入控制命令让机器关机、重启等。

1.2.2 uevent

在这里插入图片描述

2.代码中自动mknod

struct class *class_create(struct module *owner, const char *name);
/*
 * 功能:在/sys/class生成一个目录,目录名由name指定
 * 参数:
	struct module *owner - THIS_MODULE
	const char *name - 目录名
 * 返回值  成功:class指针   失败:NULL
*/
/*
辅助接口:可以定义一个struct class 的指针变量cls来接受返回值,然后通过IS_ERR(cls)判断是否失败;
IS_ERR(cls);成功----------------->0
IS_ERR(cls);失败----------------->非0
PTR_ERR(cls);来获得失败的返回错误码;
*/
void class_destroy(struct class *cls)
/*
* 功能:删除class_create生成目录
* 参数:
 	struct class *cls - class指针
* 返回值
*/
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 - 父对象,一般NULL
 	dev_t devt - 设备号
 	void *drvdata - 驱动私有数据,一般NULL
 	const char *fmt - 字符串的格式
 	 ... - 不定参数
 * 返回值
 	成功:device指针
 	失败:NULL
 */
void device_destroy(struct class *class, dev_t devt)
/*
 * 功能:删除device_create生成目录
 * 参数:
 	struct class *class - class指针
 	dev_t devt - 设备号
 * 返回值
*/

3.代码实践

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/wait.h>
#include <linux/device.h>
#include <linux/sched.h>
#include <asm/uaccess.h>
#include <asm/atomic.h>

int major = 11;
int minor = 0;
int mychar_num = 1;

struct mysecond_timer_dev
{
	struct cdev mydev;
	int second;
	struct timer_list timer;
	atomic_t openflag;
	struct class *pcls;
	struct device *pdev;
};

struct mysecond_timer_dev gmydev;

void time_func(unsigned long arg);
int mysecond_timer_open(struct inode *pnode,struct file *pfile);
int mysecond_timer_close(struct inode *pnode,struct file *pfile);

ssize_t mysecond_timer_read(struct file *pfile,char __user *puser,size_t size,loff_t *pos);

struct file_operations myops = {
	.owner = THIS_MODULE,
	.open = mysecond_timer_open,
	.release = mysecond_timer_close,
	.read = mysecond_timer_read,
};

int __init mysecond_timer_init(void)
{
	int ret = 0;
	dev_t devno = MKDEV(major,minor);
	
	ret = register_chrdev_region(devno,mychar_num,"mychar_timer");
	if(ret){
		ret = alloc_chrdev_region(&devno,minor,mychar_num,"mychar_timer");
		if(ret){
			printk("get devno failed\n");
			return -1;
		}
		major = MAJOR(devno);
	}

	cdev_init(&gmydev.mydev,&myops);

	gmydev.mydev.owner = THIS_MODULE;
	cdev_add(&gmydev.mydev,devno,mychar_num);

	init_timer(&gmydev.timer);

	atomic_set(&gmydev.openflag,1);

	gmydev.pcls = class_create(THIS_MODULE,"mysecond");

	if(IS_ERR(gmydev.pcls)){
		printk("class_create failed\n");
		cdev_del(&gmydev.mydev);
		unregister_chrdev_region(devno,mychar_num);
		return -1;
	}

	gmydev.pdev = device_create(gmydev.pcls,NULL,devno,NULL,"mysec");
	if(NULL == gmydev.pdev){
		printk("device_create failed\n");
		class_destroy(gmydev.pcls);
		cdev_del(&gmydev.mydev);
		unregister_chrdev_region(devno,mychar_num);
		return -1;
	}
	return 0;
}

void __exit mysecond_timer_exit(void)
{
	dev_t devno = MKDEV(major,minor);
	class_destroy(gmydev.pcls);
	device_destroy(gmydev.pcls,devno);
	cdev_del(&gmydev.mydev);
	unregister_chrdev_region(devno,mychar_num);
}

void time_func(unsigned long arg)
{
	struct mysecond_timer_dev *pmydev = (struct mysecond_timer_dev *)arg;
	pmydev->second++;
	mod_timer(&pmydev->timer,jiffies + HZ * 1);
}

int mysecond_timer_open(struct inode *pnode,struct file *pfile)
{
	struct mysecond_timer_dev *pmydev = NULL;
	pfile->private_data = (void *)container_of(pnode->i_cdev,struct mysecond_timer_dev,mydev);
	pmydev = (struct mysecond_timer_dev *)pfile->private_data;
	if(atomic_dec_and_test(&pmydev->openflag)){
		pmydev->timer.expires = jiffies + HZ * 1;
		pmydev->timer.function = time_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_timer_close(struct inode *pnode,struct file *pfile)
{
	struct mysecond_timer_dev *pmydev = (struct mysecond_timer_dev *)pfile->private_data;
	del_timer(&pmydev->timer);
	atomic_set(&pmydev->openflag,1);
	return 0;
}

ssize_t mysecond_timer_read(struct file *pfile,char __user *puser,size_t size,loff_t *pos)
{
	struct mysecond_timer_dev *pmydev = (struct mysecond_timer_dev *)pfile->private_data;
	int ret = 0;
	if(size < sizeof(int)){
		printk("the expert 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 size;
}

MODULE_LICENSE("GPL");
module_init(mysecond_timer_init);
module_exit(mysecond_timer_exit);

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

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

相关文章

终章:学习路线

说明 该文章来源于徒弟lu2ker转载至此处&#xff0c;更多文章可参考&#xff1a;https://github.com/lu2ker/ 文章目录说明一些废话成果路线第一阶段要点第二阶段要点第三阶段要点第四阶段要点最后一些废话 截至这篇文章前已经有150star了&#xff0c;虽然比不上大佬们K级的量…

【链表】leetcode203.移除链表元素(C/C++/Java/Js)

leetcode203.移除链表元素1 题目2 思路 (两种方式&#xff09;2.1 在原来链表上进行删除2.2 设置一个虚拟头结点删除3 代码3.1 C &#xff08;两种方式&#xff09;3.2 C版本&#xff08;两种方式&#xff09;3.3 Java版本&#xff08;两种方式&#xff09;3.4 JavaScript版本4…

Vue的组件、组件的创建、data、methods

一、组件 组件是vue的重要的特征之一&#xff0c;可以扩展html的功能&#xff0c;也可以封装代码实现重复使用。 二、组件的创建 1. 非脚手架方式下创建 ​ 第一步&#xff1a;使用Vue.extend创建组件 ​ 第二步&#xff1a;使用Vue.component注册组件 ​ …

OpenGov(三):新波卡治理机制有哪些可期待?

OpenGov维持波卡开创的信念投票&#xff0c;与以前相同的方式进行&#xff0c;使用WebAssembly和几个链上投票机制。也就是说&#xff0c;OpenGov通过降低障碍&#xff0c;来更好地管理网络的日常决策&#xff0c;将流程推向去中心化。真正的重点是使提案的范围与通过治理流程的…

企业数字化转型到底是什么?

企业的数字化转型单单是从基础设施上变更&#xff0c;更要从企业数据从文化上入手&#xff0c;培养企业的数据文化&#xff0c;以数据驱动来促进业务发展。大家都把数据基础设施讲的很详细了&#xff0c;那么我就从企业的数据化转型当中的数据文化是什么&#xff1f;以下来为大…

Python类型注解(十)

python学习之旅(十) &#x1f44d;查看更多可以关注查看首页或点击下方专栏目录 一.为什么需要类型注解 在代码中提供数据类型的注解&#xff08;显式的说明&#xff09;&#xff0c;使用时能获得相关提示 帮助第三方IDE工具&#xff08;如PyCharm&#xff09;对代码进行类型推…

想从事网络信息安全的工作,该如何自学?

前言 【一一帮助网络安全入门和提升学习点这里一一】 由于我之前写了不少网络安全技术相关的文章&#xff0c;不少读者朋友知道我是从事网络安全相关的工作&#xff0c;于是经常有人私信问我&#xff1a; 我刚入门网络安全&#xff0c;该怎么学&#xff1f;要学哪些东西&#…

第56篇-利用jsRpc获取某博的登录参数

声明:该专栏涉及的所有案例均为学习使用,严禁用于商业用途和非法用途,否则由此产生的一切后果均与作者无关!如有侵权,请私信联系本人删帖! 文章目录 一、前言二、JsRpc的基本使用1.准备工作2.简单使用三、使用jsRpc获取微博登录参数1.网站分析2.构建rpc一、前言 以前使用…

记录 一次 小米路由器4C 刷openwrt 过程

前言 起因是4C的性能不太行&#xff0c;用久了网络也不稳定&#xff0c;且100M带宽跑不满&#xff0c;然后就换了路由器&#xff0c;闲置的这个准备哪来跑个Linux挂个bot来着&#xff0c;结果可好&#xff0c;刷完发现内存小的可怜呀&#xff0c;架构也不是主流的&#xff08;…

低代码助力工业软件发展,提升智能制造“软”实力

在《“十四五”智能制造发展规划》中&#xff0c;将工业软件作为加强自主供给的一个重点任务进行单独部署&#xff0c;强调了工业软件的工业属性&#xff0c;明确了工业软件对于智能制造的核心支撑作用&#xff0c;凸显了我国补足工业软件短板、以工业软件助推智能制造发展的决…

跨境资讯 | 亚马逊三站点将更新供应链标准,1月19日生效

让我们一起来看看今日都有哪些新鲜事吧&#xff01;01 亚马逊将更新供应链标准 亚马逊美国站、欧洲站和日本站发布公告称2023年1月19日将更新供应链标准&#xff0c;在亚马逊销售的产品必须符合这些新标准。作为定期审查的一部分&#xff0c;这些标准每三年更新一次。亚马逊表…

虹科案例 | 解决ASRS系统的痛点问题居然这么简单?(下)

ASRS中的定位器 在考虑传感技术时&#xff0c;重要的是每种技术都能够以最高程度的重复性和精确度保持绝对分量&#xff0c;并非所有的方法都是一样托盘梭子和立式起重机在任何时候都要求绝对位置。 托盘梭子&#xff1a; 过道位置 行位置 垂直起重机&#xff1a; 高度 线性…

Java中解决lambda表达式内部访问在其外部定义的变量-使用mapToInt

场景 Java8新特性-Stream对集合进行操作的常用API&#xff1a; Java8新特性-Stream对集合进行操作的常用API_霸道流氓气质的博客-CSDN博客_streamapi对集合修改 上面介绍Stream的相关使用示例。 如果遇到在lambda表达式内部访问在其外部定义的变量&#xff0c;比如一个求和…

MATLAB-surf/ezsurf函数绘制三维图形

&#xff08;1&#xff09;surf 函数的用法和 mesh函数类似&#xff0c;MATLAB中 surf函数专门用于绘制三维着色曲面图和 surfc是通过矩形区域来观测数学函数的函数。surf和 surfc能够产生由X、Y、Z指定的有色参数化曲面&#xff0c;即三维有色图。具体调用方法如下。1、surf(Z…

JDBC数据库连接

下载jdbc jar包&#xff0c;中央仓库下载https://mvnrepository.com/artifact/mysql/mysql-connector-java/8.0.24项目导入右键jar包&#xff0c;然后add as library数据库操作3.1连接数据库package com.heima.jdbc;import java.sql.Connection; import java.sql.DriverManager…

Appium自动化测试环境搭建

Appium自动化环境搭建 首先整体了解一下Appium自动化环境搭建都需要哪些软件或者组件 Python、Appium、AndroidSDK&#xff08;主要是adb.exe、aapt.exe&#xff09;、Node Js、java 一、安装并配置java环境 1、这个可以搜到的教程实在是太多了&#xff0c;建议直接官方下载…

汽车数据分析,2022年汽车产量总体高于2021年,年产量增长了6%左右

哈喽&#xff0c;大家好&#xff0c;春节将近&#xff0c;想必大家也开始抢票准备回家过年了&#xff0c;有车的伙伴也可能打算自驾归家了。大家辛苦工作了一年&#xff0c;手里积攒了一些积蓄&#xff0c;有些伙伴可能想赶在春节购车购房&#xff0c;这里小编为大家准备了一些…

java面试之设计篇

一、基础 1.六大设计原则 单一职责原则 一个类只专注于做一件事&#xff1b;高内聚&#xff0c;低耦合&#xff1b; 开闭原则 对拓展开放&#xff0c;对修改关闭&#xff08;尽可能不动原有代码进行拓展&#xff09;&#xff1b; 高内聚&#xff0c;低耦合&#xff1b; 为达到…

电力系统|基于分布式高斯-牛顿方法结合置信传播 (BP) 的概率推理方法的非线性状态估计 (SE) 模型(Matlab代码实现)

&#x1f4a5;&#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️❤️&#x1f4a5;&#x1f4a5;&#x1f4a5; &#x1f4dd;目前更新&#xff1a;&#x1f31f;&#x1f31f;&#x1f31f;电力系统相关知识&#xff0c;期刊论文&…

新考纲下的PMP考试有多难?

一&#xff0c;2022新考纲PMP考试有多难&#xff1f; PMP考试被认为是最难的项目管理考试之一&#xff0c;主要是因为其庞大的教学大纲、所需的投入的精力、所问问题的类型和长度、考试时间和答案选择。它测试候选人对项目管理技能和耐心水平的深入了解。这并不意味着不可能通…