lv15 驱动高级设备模型 1

news2024/10/6 22:28:15

之前的驱动操作称为硬编

一、起源

仅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_MODULE
    const 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 - 父对象,一般NULL
    dev_t devt - 设备号
    void *drvdata - 驱动私有数据,一般NULL
    const 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 open

	struct 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);

	//初始化置位1
	atomic_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) modules

modules_install:
	$(MAKE) -C $(KERNELDIR) M=$(PWD) modules INSTALL_MOD_PATH=$(ROOTFS) modules_install

clean:
	rm -rf  *.o  *.ko  .*.cmd  *.mod.*  modules.order  Module.symvers   .tmp_versions

else

CONFIG_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.o

endif

编译

测试,不需要我们自己mknod

 

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

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

相关文章

【51单片机】AT24C02(江科大、爱上半导体)

一、AT24C02 1.AT24C02介绍 AT24C02是一种可以实现掉电不丢失的存储器,可用于保存单片机运行时想要永久保存的数据信息 存储介质:E2PROM 通讯接口:12C总线 容量:256字节 2.引脚即应用电路 本开发板AT24C02原理图 12C地址全接地,即全为0 WE接地,没有写使能 SCL接P21 S…

无人机飞行原理,多旋翼无人机飞行原理详解

多旋翼无人机升空飞行的首要条件是动力&#xff0c;有了动力才能驱动旋粪旋转&#xff0c;才能产生克服重力所必需的升力。使旋翼产生升力&#xff0c;进而推动多旋翼无人机升空飞行的一套设备装置称为动力装置&#xff0c;包括多旋翼无人机的发动机以及保证发动机正常工作所必…

Could not determine ref type of version: remote: Invalid credentials

在按照别人代码的README配置环境时&#xff0c;需要clone很多库&#xff0c;但是有一个库一直下不下来。 解决办法&#xff0c;直接上github搜索该库&#xff0c;选好相应的版本tag&#xff0c; git clone -b <branch_name> <link>下载即可。

计算机网络——04接入网和物理媒体

接入网和物理媒体 接入网络和物理媒体 怎样将端系统和边缘路由器连接&#xff1f; 住宅接入网络单位接入网络&#xff08;学校、公司&#xff09;无线接入网络 住宅接入&#xff1a;modem 将上网数据调制加载到音频信号上&#xff0c;在电话线上传输&#xff0c;在局端将其…

InternLM大模型实战-5.LMDeploy大模型量化部署实战

文章目录 前言笔记正文大模型部署背景部署挑战部署方案 LMDeploy框架量化推理引擎Turbomind推理服务api server 前言 本文是对于InternLM全链路开源体系系列课程的学习笔记 【LMDeploy 大模型量化部署实践】 https://www.bilibili.com/video/BV1iW4y1A77P/?share_sourcecopy_…

《Linux 简易速速上手小册》第4章: 包管理与软件安装(2024 最新版)

文章目录 4.1 包管理基础4.1.1 重点基础知识4.1.2 重点案例&#xff1a;在 Ubuntu 上安装和管理软件4.1.3 拓展案例 1&#xff1a;添加软件仓库4.1.4 拓展案例 2&#xff1a;回滚软件到旧版本 4.2 使用 APT 与 YUM4.2.1 重点基础知识4.2.2 重点案例&#xff1a;在 Ubuntu 上配置…

Python||数据分析之pyecharts 绘图(词云、气泡)

1. echarts 和 Pyecharts 简介 1.1echarts 简介: • echarts 是一个使用 JavaScript 实现的开源可视化库,涵盖各行业图表,满足各种需求。 • echarts 遵循 Apache-2.0 开源协议,免费商用。 • ECharts 最初由百度团队开源,并于 2018 年初捐赠给 Apache 基金会,成为 AS…

第9讲用户信息修改实现

用户信息修改实现 后端修改用户昵称&#xff1a; /*** 更新用户昵称* param wxUserInfo* param token* return*/ RequestMapping("/updateNickName") public R updateNickName(RequestBody WxUserInfo wxUserInfo,RequestHeader String token){if(StringUtil.isNot…

HTTP协议-请求Request

前言&#xff1a; 序列&#xff1a;HTTP - 002 1.请求格式 1.1标椎格式 HTTP请求是字符串的格式传输&#xff0c;具体包含以下四部分&#xff1a; 首行&#xff1a;[方法][url][版本号]&#xff0c;分别使用空格分隔&#xff1b;请求头&#xff08;Header&#xff09;&#…

Verilog刷题笔记30

题目&#xff1a; You are provided with a BCD one-digit adder named bcd_fadd that adds two BCD digits and carry-in, and produces a sum and carry-out. 解题&#xff1a; module top_module( input [399:0] a, b,input cin,output cout,output [399:0] sum );reg [99…

【程序设计竞赛】竞赛中的细节优化

必须强调下&#xff0c;以下的任意一种优化&#xff0c;都应该是在本身采用的算法没有任何问题情况下的“锦上添花”&#xff0c;而不是“雪中送炭”。 如果下面的说法存在误导&#xff0c;请专业大佬评论指正 读写优化 C读写优化——解除流绑定 在ACM里&#xff0c;经常出现…

GO 的 Web 开发系列(五)—— 使用 Swagger 生成一份好看的接口文档

经过前面的文章&#xff0c;已经完成了 Web 系统基础功能的搭建&#xff0c;也实现了 API 接口、HTML 模板渲染等功能。接下来要做的就是使用 Swagger 工具&#xff0c;为这些 Api 接口生成一份好看的接口文档。 一、写注释 注释是 Swagger 的灵魂&#xff0c;Swagger 是通过…

Microsoft OneNote 图片文字提取

Microsoft OneNote 图片文字提取 1. 文件 -> 新建 -> 我的电脑 -> 名称 -> 位置 -> 创建笔记本2. 插入图片​​​3. 复制图片中的文本References 1. 文件 -> 新建 -> 我的电脑 -> 名称 -> 位置 -> 创建笔记本 ​ 2. 插入图片 ​​​3. 复制图片…

Material Design

1.使用颜色 2. 处理文本 3.处理形状 ​​​​​​

书生·浦语大模型第五课作业

基础作业&#xff1a; 使用 LMDeploy 以本地对话、网页Gradio、API服务中的一种方式部署 InternLM-Chat-7B 模型&#xff0c;生成 300 字的小故事&#xff08;需截图&#xff09; 这里 /share/conda_envs 目录下的环境是官方未大家准备好的基础环境&#xff0c;因为该目录是共…

161基于matlab的快速谱峭度方法

基于matlab的快速谱峭度方法&#xff0c;选择信号峭度最大的频段进行滤波&#xff0c;对滤波好信号进行包络谱分析。输出快速谱峭度及包络谱结果。程序已调通&#xff0c;可直接运行。 161 信号处理 快速谱峭度 包络谱分析 (xiaohongshu.com)

正则表达式与正则可视化工具:解密文本处理的利器

正则表达式与正则可视化工具&#xff1a;解密文本处理的利器 引言 在计算机科学和软件开发领域&#xff0c;正则表达式是一种强大而灵活的文本处理工具。然而&#xff0c;对于初学者来说&#xff0c;正则表达式的语法和规则可能会显得晦涩难懂。为了帮助初学者更好地理解和学…

Linux第48步_编译正点原子的出厂Linux内核源码

编译正点原子的出厂 Linux 内核源码&#xff0c;为后面移植linux做准备。研究对象如下&#xff1a; 1)、linux内核镜像文件“uImage” 路径为“arch/arm/boot”&#xff1b; 2)、设备树文件“stm32mp157d-atk.dtb” 路径为“arch/arm/boot/dts” 3)、默认配置文件“stm32m…

nba2k24 韩旭面补

nba2k23-24 韩旭面补 nba2k23-nba2k24通用 韩旭面补 下载地址&#xff1a; https://www.changyouzuhao.cn/9605.html