嵌入式驱动学习第一周——linux设备管理模型

前言

   现在来聊点原理性的东西——linux设备管理模型

   嵌入式驱动学习专栏将详细记录博主学习驱动的详细过程,未来预计四个月将高强度更新本专栏,喜欢的可以关注本博主并订阅本专栏,一起讨论一起学习。现在关注就是老粉啦!

行文目录

  • 前言
  • 1. LDM数据结构
    • 1.1 总线、设备、驱动关系
      • 1.1.1 类
      • 1.1.2 设备
      • 1.1.3 驱动
  • 2. kobject sysfs
    • 2.1 kobjects
    • 2.2 ktype
    • 2.3 kset
  • 参考资料

1. LDM数据结构

   linux 设备模型LDM的上层依赖于总线、设备驱动程序、设备和类。

1.1 总线、设备、驱动关系

   在LINUX驱动的世界里,所有的设备和驱动都是挂在总线上的,也就是总线来管理设备和驱动的,总线知道挂在它上边的所有驱动和设备的情况,由总线完成驱动和设备的匹配和探测。

   总线上挂着驱动和设备,一个驱动可以管理多个设备,一个设备保存一个对应驱动的信息,一般在初始化的时候,总线先初始化,然后设备先注册,最后驱动去找设备,完成他们之间的衔接。

   系统已经给我们准备好了我们所学要的总线。对于我们来说,就是去学好怎么在系统中添加设备以及相关的驱动就行了。

1.1.1 类

   相关结构体:struct classstruct class_device

   类发明来就是来管理设备的,是对设备的高级抽象,本质也是一个结构体,但是按照类的思想来组织成员的。运用class,可以让用户空间的程序根据自己要处理的事情来调用设备,而不是根据设备被接入到系统的方式或设备的工作原来调用。

   一个struct class结构体类型变量对应一个类,内核提供了class_create() 函数,可以用它来创建一个类,这个类存放于 sysfs 下面, 一旦创建了类,再调用 device_create() 函数在 /dev 目录下创建相应的设备节点。

1.1.2 设备

   驱动中常写的struct device是硬件设备在内核驱动框架中的抽象
   使用device_register函数向内核驱动框架注册一个设备,也可使用device_create来创建,device_create函数是对device_register的封装。
   通常device不会单独使用,而是被包含在一个具体的设备结构体中

   使用案例如下所示:

struct gpioled_dev {dev_t devid;struct cdev cdev;struct class *class;			// 定义一个classstruct device *device;			// 定义一个deviceint major;int minor;struct device_node *nd;int led_gpio;
};static int __init led_init(void)
{......newchrled.class = class_create(THIS_MODULE, NEWCHRLED_NAME);		// 先创建classif (IS_ERR(newchrled.class)) {return PTR_ERR(newchrled.class);}newchrled.device = device_create(newchrled.class, NULL, newchrled.devid, NULL, NEWCHRLED_NAME);		// 再用class创建deviceif (IS_ERR(newchrled.device)) {return PTR_ERR(newchrled.device);}......
}static void __exit led_exit(void)
{......device_destroy(newchrled.class, newchrled.devid);		// 先释放deviceclass_destroy(newchrled.class);							// 再释放class
}

1.1.3 驱动

   struct device_driver是驱动程序在内核驱动框架中的抽象
   关键元素1:name,驱动程序的名字,很重要,经常被用来作为驱动和设备的匹配依据
   关键元素2:probe,驱动程序的探测函数,用来检测一个设备是否可以被该驱动所管理

   使用方式:

/** @description: 驱动程序的探测函数,检测设备是否被该驱动所管理,当驱动与设备匹配后此函数会执行* @param-dev  : platform设备* @return     : 0,成功;其他负值,失败*/
static int led_probe(struct platform_device *dev)
{int i = 0;int ressize[5];u32 val = 0;struct resource *ledsource[5];printk("led driver and device has matched!\r\n");for (i = 0; i < 5; i++) {ledsource[i] = platform_get_resource(dev, IORESOURCE_MEM, i);if (!ledsource[i]) {dev_err(&dev->dev, "No MEM resource for always on\n");return -ENXIO;}ressize[i] = resource_size(ledsource[i]);}IMX6U_CCM_CCGR1  = ioremap(ledsource[0]->start, ressize[0]);SW_MUX_GPIO1_IO3 = ioremap(ledsource[1]->start, ressize[1]);SW_PAD_GPIO1_IO3 = ioremap(ledsource[2]->start, ressize[2]);GPIO1_DR         = ioremap(ledsource[3]->start, ressize[3]);GPIO1_GDIR       = ioremap(ledsource[4]->start, ressize[4]);val = readl(IMX6U_CCM_CCGR1);val &= ~(3 << 26);val |= (3 << 26);writel(val, IMX6U_CCM_CCGR1);writel(5, SW_MUX_GPIO1_IO3);writel(0x10b0, SW_PAD_GPIO1_IO3);val = readl(GPIO1_GDIR);val &= ~(1 << 3);val |= (1 << 3);writel(val, GPIO1_GDIR);val = readl(GPIO1_DR);val |= (1 << 3);writel(val, GPIO1_DR);if (leddev.major) {leddev.devid = MKDEV(leddev.major, 0);register_chrdev_region(leddev.devid, LEDDEV_CNT, LEDDEV_NAME);} else {alloc_chrdev_region(&leddev.devid, 0, LEDDEV_CNT, LEDDEV_NAME);leddev.major = MAJOR(leddev.devid);leddev.minor = MINOR(leddev.devid);}leddev.cdev.owner = THIS_MODULE;cdev_init(&leddev.cdev, &led_fops);cdev_add(&leddev.cdev, leddev.devid, LEDDEV_CNT);leddev.class = class_create(THIS_MODULE, LEDDEV_NAME);if (IS_ERR(leddev.class)){return PTR_ERR(leddev.class);}leddev.device = device_create(leddev.class, NULL, leddev.devid, NULL, LEDDEV_NAME);if (IS_ERR(leddev.device)) {return PTR_ERR(leddev.device);}return 0;
}/** @description: remove函数,移除platform驱动的时候此函数会执行* @param-dev  : platfrom 设备* @return     : 0,成功;其他负值,失败*/
static int led_remove(struct platform_device *dev)
{iounmap(IMX6U_CCM_CCGR1);iounmap(SW_MUX_GPIO1_IO3);iounmap(SW_PAD_GPIO1_IO3);iounmap(GPIO1_DR);iounmap(GPIO1_GDIR);cdev_del(&leddev.cdev);unregister_chrdev_region(leddev.devid, LEDDEV_CNT);device_destroy(leddev.class, leddev.devid);class_destroy(leddev.class);return 0;
}static struct platform_driver led_driver = {.driver = {.name  = "imx6ul-led2",},.probe  = led_probe,.remove = led_remove
};

2. kobject sysfs

2.1 kobjects

   设备模型的核心部分是kobjects,类似于java中object对象类,提供了诸如计数、名称、父指针等字段,可以创建对象的层次结构。其定义如下:

struct kobject {char			* k_name;				// 指向kobject名称,如果名称长度小于KOBJ_NAME_LEN,则存入name数组中,如果超过,则动态分配一个缓冲区存放,KOBJ_NAME_LEN是20个字节char			name[KOBJ_NAME_LEN];struct kref		kref;					// 实现kobject的引用计数struct list_head	entry;struct kobject		* parent;			// 父对象,在内核中构造一个对象层次结构,并可以将多个对象间的关系表现出来struct kset		* kset;struct kobj_type	* ktype;struct dentry		* dentry;			// 指向dentry结构体,在sysfs中该结构体就表示这个kobject
};

   kobject通常是嵌入到其他结构体中的,其单独意义其实并不大。当kobject被嵌入到其他结构体中时,该结构体便拥有了kobject提供的标准功能。

2.2 ktype

   ktype是为了描述一族kobject所具有的普遍特性。因此,不在需要每个kobject都分别定义自己的特性,而是将这些特性在ktype结构体中一次定义,然后所有“同类”的kobject都能共享一样的特性。

struct kobj_type {void (*release)(struct kobject *);struct sysfs_ops	* sysfs_ops;struct attribute	** default_attrs;
};

   release指针指向在kobject引用计数减为零时要被调用的析构函数。该函数负责释放所有的kobject使用的内存和其他相关清理工作。

   sysfs_ops变量指向sysfs_ops结构体。该结构体表述了sysfs文件读写是的特性。

   default_attrs指向一个attribute结构体数组。这些结构体定义了该kobject相关的默认属性。属性描述了给定对象的特征,如果该kobject被导出到sysfs中,那么这些属性都将相应地作为文件而导出。数组中的最后一项必须为NULL。

2.3 kset

   kset是kobject对象的集合体。把它看成是一个容器,可将所有相关的kobject对象,比如“全部的块设备”置于一个位置。kset把kobject集中到一个集合中。

struct kset {struct subsystem	* subsys;struct kobj_type	* ktype;struct list_head	list;struct kobject		kobj;struct kset_hotplug_ops	* hotplug_ops;
};

   其中ktype指向kset集合中kobject对象的类型。
   list连接该集合中所有的kobject对象。
   kobj指向的kobject对象代表了该集合的基类。
   hotplug_ops指向一个用于处理集合中kobject对象的热插拔操作的结构体。
   subsys指针指向该结构体相关的struct subsystem结构体。

参考资料

[1] linux设备模型

[2] Linux设备模型

[3] Linux设备驱动模型

[4] device_create()、device_register()、deivce_add()区别

[5] Linux内核设计与实现—kobject sysfs

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

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

相关文章

编写dockerfile挂载卷

编写dockerfile文件 [rootwq docker-test-volume]# vim dockerfile1 [rootwq docker-test-volume]# cat dockerfile1 FROM centosVOLUME ["volume01","volume02"]CMD echo "------end------" CMD /bin/bash [rootwq docker-test-volume]#使用do…

leetcode:51.N皇后(复习)

题目理解&#xff1a;&#xff08;回溯算法&#xff09; 树形结构——层数代表行数&#xff0c;递归的深度就是总行数。 代码实现&#xff1a;

解释Android中的Activity生命周期,以及在哪个生命周期方法中可以进行布局的初始化?

在Android中&#xff0c;Activity的生命周期是指Activity从创建到销毁的整个过程&#xff0c;这个过程中会经历一系列的回调方法。了解Activity的生命周期对于管理资源、处理用户交互和确保应用的稳定性非常重要。下面是Activity生命周期的主要阶段及其对应的回调方法&#xff…

手写 Attention 迷你LLaMa2——LLM实战

https://github.com/Yuezhengrong/Implement-Attention-TinyLLaMa-from-scratch 1. Attention 1.1 Attention 灵魂10问 你怎么理解Attention&#xff1f; Scaled Dot-Product Attention中的Scaled&#xff1a; 1 d k \frac{1}{\sqrt{d_k}} dk​ ​1​ 的目的是调节内积&…

分布式ID选型对比(4)

百度UID generator 一, 创建表: worker_node(在项目启动时初始化生成workId) CREATE TABLE worker_node (ID bigint NOT NULL AUTO_INCREMENT COMMENT auto increment id,HOST_NAME varchar(64) NOT NULL COMMENT host name,PORT varchar(64) NOT NULL COMMENT port,TYPE int…

金属3D打印新材料崭露头角,性能卓越引领行业新潮流

在3D打印模具制造领域&#xff0c;材料的选择对最终产品的性能有着至关重要的影响。随着技术的不断进步&#xff0c;金属3D打印材料正迅速发展&#xff0c;展现出强大的竞争力和创新潜力。其中&#xff0c;3D打印企业毅速推出的多款不锈钢粉末材料&#xff0c;如EM191、EM191S、…

字符串函数 strncpy() 详解

什么是 strncpy() 函数&#xff1f; strncpy() 函数是 C 语言中的一个标准库函数&#xff0c;它的作用是从一个字符串中按照指定的长度复制字符到另一个字符串中。它的原型如下&#xff1a; char *strncpy(char *dest, const char *src, size_t n);其中&#xff0c;dest 是目…

VMware Workstation Pro 17 虚拟机软件安装教程

VMware软件介绍 VMware Workstation是一款功能强大的桌面虚拟计算机软件&#xff0c;提供用户可在宿主机操作系统上同时运行不同的操作系统(虚拟化技术)&#xff0c;所运行的操作系统可方便的进行复制和移动&#xff0c;突破传统架构的限制。本文将以VMware Workstation Pro 1…

使用lnmp环境部署laravel框架需要注意的点

1&#xff0c;上传项目文件后&#xff0c;需要chmod -R 777 storage授予文件权限&#xff0c;不然会报错file_put_contents(/): failed to open stream: Permission denied。 如果后面还是报错没有权限的话&#xff0c;就执行ps -ef |grep php查询php运行用户。然后执行chown …

2024真正有效的苹果mac电脑清理工具CleanMyMac X

一、前言 对于Mac用户来说&#xff0c;电脑卡顿、运行缓慢无疑是一件令人头疼的事情。而市面上的清理软件又五花八门&#xff0c;效果参差不齐&#xff0c;如何才能找到一款真正有效的清理工具呢&#xff1f;今天&#xff0c;我们为大家推荐一款实力派电脑清理软件——CleanMy…

【双指针】删除有序数组中重复元素,双指针原地修改数组

删除有序数组中重复元素 链接 . - 力扣&#xff08;LeetCode&#xff09;. - 备战技术面试&#xff1f;力扣提供海量技术面试资源&#xff0c;帮助你高效提升编程技能,轻松拿下世界 IT 名企 Dream Offer。https://leetcode.cn/problems/remove-duplicates-from-sorted-array/…

宏定义中#与##的注意事项

1. #是字符串化操作符。它的作用是将宏参数转换成字符串 2. ##是标记粘贴操作符。它的作用是将两个标记连接起来形成一个新的标记 #define TEST1(a) #a #define TEST2(a) b##a/***********************************************************/ 举例&#xff1a;TEST1(hello) 会…

python virtualenv创建虚拟环境

文章目录 安装virtualenv在当前目录创建虚拟环境命令进入虚拟环境激活虚拟环境查看python版本退出虚拟环境 安装virtualenv pip install virtualenv在当前目录创建虚拟环境命令 python -m virtualenv venvPS E:\coding\bertopic> python -m virtualenv venv created virtu…

Linux:Nginx服务重写功能

目录 一、重写功能 1.重写功能作用 2.rewrite指令 if指令 return指令 set指令 break指令 3.rewrite标志 redirect标志 permanent标志 break标志 last标志 rewrite标志实验 一、重写功能 1.重写功能作用 重写功能(rewrite)用于实现URL的重写&#xff0c;URL的重写是非常有用…

LC打怪录 283. moving zero

题目链接 力扣 class Solution:def moveZeroes(self, nums: List[int]) -> None:l0for r in range(len(nums)):if nums[r]:nums[l], nums[r] nums[r], nums[l]l 1return nums 方法一&#xff1a; 思路 双指针 1. def moveZeroes(self, nums: List[int]) -> None: …

Stable Diffusion 3正式发布,旨在巩固其在AI图像领域相对于Sora和Gemini的领先地位

每周跟踪AI热点新闻动向和震撼发展 想要探索生成式人工智能的前沿进展吗&#xff1f;订阅我们的简报&#xff0c;深入解析最新的技术突破、实际应用案例和未来的趋势。与全球数同行一同&#xff0c;从行业内部的深度分析和实用指南中受益。不要错过这个机会&#xff0c;成为AI领…

vue3 + vite 项目可以使用纯Js开发吗?

答案&#xff1a;可以 创建项目&#xff1a; 按照链接参考或者按官方&#xff1a; webstorm 创建vue3 vite 项目-CSDN博客 项目目录 tsconfig.json 配置允许js allowJs指定是否编译js文件&#xff0c;在任意文件当中,如果我们模块使用js写的&#xff0c;那么我们需要 将all…

地理空间分析15——Python在应急响应与地理空间分析中的创新应用

目录 写在开头1.Python及其在地理空间分析中的应用地理空间数据处理库地理空间数据可视化工具 2.应急响应中的Python应用实例2.1 灾害风险评估2.2 实时数据分析与响应2.3资源优化分配 3 地震应急响应案例&#xff1a;利用Python进行数据分析3.1 背景介绍3.2 数据收集与处理3.3 …

链式插补 (MICE):弥合不完整数据分析的差距

导 读 数据缺失可能会扭曲结果&#xff0c;降低统计功效&#xff0c;并且在某些情况下&#xff0c;导致估计有偏差&#xff0c;从而破坏从数据中得出的结论的可靠性。 处理缺失数据的传统方法&#xff08;例如剔除或均值插补&#xff09;通常会引入自己的偏差或无法充分利用数…

机器学习:模型选择和模型优化

进行数据处理之后&#xff0c;我们得到了x_train和y_train&#xff0c;我们就可以用来进行回归或分类模型训练啦~ 一、模型选择 我们这里可能使用的是回归模型&#xff08;Regression&#xff09;&#xff0c;值得注意的是&#xff0c;回归和分类不分家。分类是预测离散值&…