设备驱动框架1——LED驱动框架的分析(核心层)

以下内容源于朱有鹏嵌入式课程的学习与整理,如有侵权请告知删除。

一、驱动框架的含义

1、理解层面1:驱动的分层设计

设备驱动程序,是由内核驱动部分的维护者,以及驱动开发工程师协作完成的。

内核驱动部分的维护者,往往为同类的设备(比如LED、LCD、蜂鸣器等等)设计了一个成熟的、标准的、典型的框架。他们将同类设备的驱动中通用的一些功能抽离出来,作为驱动框架中的核心层,然后设计好核心层与具体操作层的接口,让驱动开发者来实现具体操作层。

核心层与具体操作层的接口,其设计原则是标准化,尽量降低驱动开发者的编程难度。

设备驱动框架,简而言之就是驱动的分层设计。

2、理解层面2:系统资源的管控

内核维护者设计了一些系统资源管控体系,这些体系能够协调各个驱动的资源分配,保证内核的稳定。比如系统中所有的GPIO就属于系统资源,某个驱动模块如果需要使用某个GPIO,就需要调用特殊的接口进行申请,申请到之后再使用,使用完之后要释放。

这也是设备驱动框架的组成部分。

二、LED驱动框架概述

下面以内核中LED驱动框架为例,说明设备驱动框架的相关内容。 

1、相关文件

内核源码drivers/leds目录,是驱动框架规定的LED这种硬件的驱动应该待的地方。

2、LED驱动框架的核心层

drivers/leds目录中的led-class.c和led-core.c,其属于LED驱动框架的核心层。

它们由内核驱动部分维护者提供,描述的是所有厂家的不同LED硬件的相同部分的逻辑。

3、LED驱动框架的具体操作层

drivers/leds目录中的leds-xxxx.c文件,其属于LED驱动框架的具体操作层。

它们由不同厂商的驱动工程师编写。驱动工程师根据硬件的具体情况来对LED进行操作,使用LED驱动框架核心层提供的接口来与驱动框架进行交互,最终实现驱动的功能。

4、分析具体案例

分析驱动框架的关键,在于分析内核开发者提供了什么,驱动开发者需要完成什么。

以文件drivers/leds/leds-s3c24xx.c为例,这个文件是LED驱动框架的具体操作层。

drivers/leds目录中的led-class.c和led-core.c,这几个文件是LED驱动框架的核心层。

leds-s3c24xx.c文件调用drivers/leds/led-class.c文件的led_classdev_register()函数来完成LED驱动的注册。即驱动工程师通过调用驱动框架中提供的接口来实现自己的驱动。

注意:九鼎没有使用内核推荐的led驱动框架,它写的驱动是drivers/char/led/x210-led.c。

5、典型的驱动开发行业现状

内核驱动部分维护者,对驱动框架进行开发和维护、升级,对应着led-class.c和led-core.c。

SoC厂商(比如三星、华为等芯片厂商)的驱动工程师编写与测试设备驱动源码,提供参考版本,对应着leds-s3c24xx.c。

做产品的厂商(比如九鼎)的驱动工程师以SoC厂商提供的驱动源码leds-s3c24xx.c为基础,来做移植和调试。

三、LED驱动框架的初步分析

1、概述

以drivers/leds/leds-s3c24xx.c为例。

LED驱动框架的具体操作层指的是leds-s3c24xx.c文件。

LED驱动框架的核心层包括 led-class.c 文件、leds.h文件、led-core.c文件。

具体操作层leds-s3c24xx.c文件由于应用了驱动模型平台总线的概念,我们以后再分析。

这里先分析核心层的led-class.c文件

2、led-class.c文件的具体分析

led-class.c文件的内容如下:

//省略部分代码static int __init leds_init(void)
{   //创建了类名,叫/sys/class/leds           leds_class = class_create(THIS_MODULE, "leds");if (IS_ERR(leds_class))return PTR_ERR(leds_class);leds_class->suspend = led_suspend;// 关联 LED设备休眠函数leds_class->resume = led_resume;// 关联 LED设备唤醒函数// 创建设备属性文件 brightness、max_brightness、triggerleds_class->dev_attrs = led_class_attrs;return 0;
}static void __exit leds_exit(void)
{class_destroy(leds_class);//销毁了类名leds
}subsys_initcall(leds_init); //led-class.c是一个内核模块,体现为有安装与卸载函数
module_exit(leds_exit);     //所以对该文件的分析,应该从下往上阅读MODULE_AUTHOR("John Lenz, Richard Purdie");
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("LED Class Interface");

1、led-class.c将被编译成一个内核模块

由代码可以看出,led-class.c有安装与卸载函数,因此它是一个内核模块。LED驱动框架可以根据需要被安装与卸载。我们应该遵照分析模块的方法,从下往上分析。

2、类名“leds”的创建与销毁

led_init()在/sys/class目录下创建“leds”这个类名;led_exit()销毁“leds”这个类名。

3、subsys_initcall(leds_init)的分析

(1)subsys_initcall是一个宏,定义在linux/init.h中。

其功能是将其声明的函数放到一个特定的段:.initcall4.init。

#define subsys_initcall(fn)		       __define_initcall("4",fn,4)#define __define_initcall(level,fn,id) \static initcall_t __initcall_##fn##id __used \__attribute__((__section__(".initcall" level ".init"))) = fn

(2)分析module_init宏,可以看出它将函数放到了.initcall6.init段中。

#define module_init(x)	__initcall(x);
#define __initcall(fn) device_initcall(fn)
#define device_initcall(fn)		__define_initcall("6",fn,6)

也就是说,subsys_initcall和module_init的作用是一样的,只不过前者所声明的函数要比后者在内核启动时的执行顺序更早。

(3)内核实现先后顺序执行初始化操作的方法

将内核启动时要调用的所有函数归类,分类名就叫做“.initcalln.init”,n的值从1到8。内核开发者在编写内核代码时,将函数设置合适的级别,链接的时候,这些函数就会被放入特定的段中,内核启动时再按照(内核链接脚本中指定的)段顺序去依次执行各个段。

内核链接脚本(编译之后才有)在arch/arm/kernel/vmlinux.lds中。

4、led_class_attrs

(1)什么是attribute?

对应于将来/sys/class/leds/ 目录里的内容,这些内容一般是文件和文件夹。这些属性文件其实就是sysfs开放给应用层的一些操作接口,应用程序可以通过操作这些属性文件来操作硬件设备。(这些属性文件非常类似于/dev/目录下的设备文件,对/dev/目录下的设备文件的操作有关的API,对应着file_operations里面的函数。)

(2)attribute有什么用?

应用程序可以通过操作/sys/class/leds/目录下面的属性文件来操作硬件设备。它其实是另一条驱动实现路线(不再有cdev相关的函数操作),有区别于之前的file_operations那条线。

(3)class_create()的返回值类型是struct class类型。

该类型定义在/include/linux/device.h文件中:

struct class {const char		*name;      // 类的名称struct module		*owner; //类所属的模块,比如usb模块、led模块等struct class_attribute		*class_attrs;//类所添加的属性struct device_attribute		*dev_attrs;//类所包含的设备所添加的属性struct kobject	*dev_kobj;// 用于标识 类所包含的设备属于块设备还是字符设备// 用于在设备发出 uevent 消息时添加环境变量int (*dev_uevent)(struct device *dev, struct kobj_uevent_env *env);char *(*devnode)(struct device *dev, mode_t *mode);// 设备节点的相对路径名void (*class_release)(struct class *class);// 类被释放时调用的函数void (*dev_release)(struct device *dev);// 设备被释放时调用的函数//设备休眠时调用的函数int (*suspend)(struct device *dev, pm_message_t state);int (*resume)(struct device *dev);// 设备被唤醒时调用的函数const struct kobj_ns_type_operations *ns_type;const void *(*namespace)(struct device *dev); const struct dev_pm_ops *pm; // 用于电源管理的函数struct class_private *p;// 指向 class_private 结构的指针
};

其中,结构体struct device_attribute定义在/include/linux/device.h文件中:

/* interface for exporting device attributes */
struct device_attribute {struct attribute	attr;ssize_t (*show)(struct device *dev, struct device_attribute *attr,char *buf);ssize_t (*store)(struct device *dev, struct device_attribute *attr,const char *buf, size_t count);
};

因此led_class_attrs[ ]这个结构体数组的初始化如下:

//位于x210_kernel/drivers/leds/led-class.c文件中
static struct device_attribute led_class_attrs[] = {//文件             //对文件操作时实际对应的操作函数1  //操作函数2__ATTR(brightness, 0644, led_brightness_show, led_brightness_store),       __ATTR(max_brightness, 0444, led_max_brightness_show, NULL),
#ifdef CONFIG_LEDS_TRIGGERS__ATTR(trigger, 0644, led_trigger_show, led_trigger_store),
#endif__ATTR_NULL,
};//位于x210_kernel/include/linux/sysfs.h文件中
#define __ATTR(_name,_mode,_show,_store) { \.attr = {.name = __stringify(_name), .mode = _mode },	\.show	= _show,					\.store	= _store,					\
}//位于x210_kernel/drivers/leds/led-class.c文件中
static ssize_t led_brightness_show(struct device *dev, struct device_attribute *attr, char *buf)
{struct led_classdev *led_cdev = dev_get_drvdata(dev);/* no lock needed for this */led_update_brightness(led_cdev);return sprintf(buf, "%u\n", led_cdev->brightness);
}static ssize_t led_brightness_store(struct device *dev,struct device_attribute *attr, const char *buf, size_t size)
{struct led_classdev *led_cdev = dev_get_drvdata(dev);ssize_t ret = -EINVAL;char *after;unsigned long state = simple_strtoul(buf, &after, 10);size_t count = after - buf;if (isspace(*after))count++;if (count == size) {ret = count;if (state == LED_OFF)led_trigger_remove(led_cdev);led_set_brightness(led_cdev, state);}return ret;
}

5、设备注册函数:led_classdev_register()

(1)函数内容与作用

该函数的内容如下,它用于创建属于“leds”这个类的一个设备,其实就是去注册一个设备。

/*** led_classdev_register - register a new object of led_classdev class.* @parent: The device to register.* @led_cdev: the led_classdev structure for this device.*/
int led_classdev_register(struct device *parent, struct led_classdev *led_cdev)
{led_cdev->dev = device_create(leds_class, parent, 0, led_cdev,"%s", led_cdev->name);if (IS_ERR(led_cdev->dev))return PTR_ERR(led_cdev->dev);
#ifdef CONFIG_LEDS_TRIGGERSinit_rwsem(&led_cdev->trigger_lock);
#endif/* add to the list of leds */down_write(&leds_list_lock);list_add_tail(&led_cdev->node, &leds_list);up_write(&leds_list_lock);if (!led_cdev->max_brightness)led_cdev->max_brightness = LED_FULL;led_update_brightness(led_cdev);
#ifdef CONFIG_LEDS_TRIGGERSled_trigger_set_default(led_cdev);
#endifprintk(KERN_DEBUG "Registered led device: %s\n",led_cdev->name);return 0;
}

(2)struct led_classdev结构体

“leds”类别的设备,用结构体struct led_classdev表征,其定义在include/linux/leds.h中:

struct led_classdev {const char		*name;int			 brightness;int			 max_brightness;int			 flags;/* Lower 16 bits reflect status */
#define LED_SUSPENDED		(1 << 0)/* Upper 16 bits reflect control information */
#define LED_CORE_SUSPENDRESUME	(1 << 16)/* Set LED brightness level *//* Must not sleep, use a workqueue if needed */void		(*brightness_set)(struct led_classdev *led_cdev,enum led_brightness brightness);/* Get LED brightness level */enum led_brightness (*brightness_get)(struct led_classdev *led_cdev);/* Activate hardware accelerated blink, delays are in* miliseconds and if none is provided then a sensible default* should be chosen. The call can adjust the timings if it can't* match the values specified exactly. */int		(*blink_set)(struct led_classdev *led_cdev,unsigned long *delay_on,unsigned long *delay_off);struct device		*dev;  //注意这里的设备struct list_head	 node;			/* LED Device list */const char		*default_trigger;	/* Trigger to use */#ifdef CONFIG_LEDS_TRIGGERS/* Protects the trigger data below */struct rw_semaphore	 trigger_lock;struct led_trigger	*trigger;struct list_head	 trig_list;void			*trigger_data;
#endif
};

其中的struct device结构体定义在/include/linux/device.h文件中:

struct device {struct device		*parent; struct device_private	*p; struct kobject kobj;const char		*init_name; /* initial name of the device */struct device_type	*type;struct mutex		mutex;	/* mutex to synchronize calls to* its driver.*/struct bus_type	*bus;		/* type of bus device is on */struct device_driver *driver;	/* which driver has allocated thisdevice */void		*platform_data;	/* Platform specific data, devicecore doesn't touch it */struct dev_pm_info	power;#ifdef CONFIG_NUMAint		numa_node;	/* NUMA node this device is close to */
#endifu64		*dma_mask;	/* dma mask (if dma'able device) */u64		coherent_dma_mask;/* Like dma_mask, but foralloc_coherent mappings asnot all hardware supports64 bit addresses for consistentallocations such descriptors. */struct device_dma_parameters *dma_parms;struct list_head	dma_pools;	/* dma pools (if dma'ble) */struct dma_coherent_mem	*dma_mem; /* internal for coherent memoverride *//* arch specific additions */struct dev_archdata	archdata;
#ifdef CONFIG_OFstruct device_node	*of_node;
#endifdev_t			devt;	/* dev_t, creates the sysfs "dev" */spinlock_t		devres_lock;struct list_head	devres_head;struct klist_node	knode_class;struct class		*class;const struct attribute_group **groups;	/* optional groups */void	(*release)(struct device *dev);
};

(2)补充说明

该函数是LED驱动框架中,内核开发者提供给驱动开发者的一个注册设备的接口。

当使用LED驱动框架编写驱动时,这函数的作用类似于之前使用file_operations方式去注册字符设备驱动的register_chrdev函数。

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

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

相关文章

myeclipse连接mysql怎么调用_myeclipse连接mysql数据库详细步骤

第一步 打开Database windows-prefenrence-showview-DBbrowser ,此时会在工具底部有个DBbrowser &#xff0c;选中它&#xff0c;再它所控制的页面的任意位置 右击new---跳转到一个配置driver的页面 (选择连接方式)图一打开myeclipse然后点击window窗口 点击Open Perspective…

struts2中文件上传

注意点 private File image;//对应的就是表单中文件上传的那个输入域的名称&#xff0c;Struts2框架会封装成File类型的private String imageFileName;// 上传输入域FileName 文件名private String imageContentType;// 上传文件的MIME类型 单个文件 1 package cn.itcast.ac…

虚拟内存管理

MMU 现代操作系统普遍采用虚拟内存管理&#xff08;Virtual Memory Management&#xff09;机制&#xff0c;这需要处理器中的MMU&#xff08;Memory Management Unit&#xff0c;内存管理单元&#xff09;提供支持&#xff0c;本节简要介绍MMU的作用。 首先引入两个概念&…

设备驱动框架2——基于驱动框架写LED驱动(具体操作层)

以下内容源于朱有鹏嵌入式课程的学习与整理&#xff0c;如有侵权请告知删除。 前言 之前写到&#xff0c;九鼎没有使用内核推荐的LED驱动框架。因此&#xff0c;我们打算基于LED驱动框架来编写x210开发板的LED驱动。编写前先要去除九鼎移植的LED驱动&#xff0c;然后配置内核支…

mysql重新用户设置密码_mysql用户密码如何重新设置?

mysql用户密码重新设置停掉MySQL服务&#xff1a;sudo service mysql stop以上命令适用于Ubuntu和Debian。CentOS、Fedora和RHEL下使用mysqld替换mysql。以安全模式启动mysql&#xff1a;sudo mysqld_safe --skip-grant-tables --skip-networking &这样我们就可以直接用roo…

第三章 门电路

1 半导体二极管开关特性 1 二极管的特性可以近似的用3.2.1的PN结方程和图3.2.2伏安特性曲线描述 如下图 二极管近似伏安特性和对应的等效电路 1 a电路表示vcc和r都很小时候二极管正向导通压降和正向电阻都不能忽视 2 b电路表示二极管正向导通电压不可以忽视&#xff0c;但是二…

多个页面同时跳转到一个页面,然后返回到上级页面

第一步&#xff0c;在page_load事件中写入一下代码&#xff1a; string strUrl Request.UrlReferrer.AbsoluteUri;ViewState["Url"] strUrl;第二步&#xff0c;在返回事件中写入一下代码&#xff1a;string strUrl ViewState["Url"].ToString();Respons…

mysql查询数据库日期_mysql如何查询日期与时间

前言&#xff1a;在项目开发中&#xff0c;一些业务表字段经常使用日期和时间类型&#xff0c;而且后续还会牵涉到这类字段的查询。关于日期及时间的查询等各类需求也很多&#xff0c;本篇文章简单讲讲日期及时间字段的规范化查询方法。1.日期和时间类型概览MySQL支持的日期和时…

设备驱动框架3——使用gpiolib完成LED驱动

以下内容源于朱有鹏嵌入式课程的学习整理&#xff0c;如有侵权请告知删除。 一、前言 在实际情况中&#xff0c;很多硬件都要用到GPIO&#xff0c;因此GPIO会复用&#xff1b;如果同一个GPIO被2个驱动同时控制就会出现bug&#xff1b;因此内核提供了gpiolib来统一管理系统中所有…

from PyQt4 import QtGui,QtCore出错-解

from PyQt4 import QtGui,QtCore出错-解今天尝试着安装PyQt写界面&#xff0c;官网下载后发现import出错了&#xff0c;情况如下图&#xff1a;import PyQt4就可以&#xff0c;from PyQt4 import QtCore却不行提示DLL load faied找了下网上有些人说是某些dll文件丢失了&#xf…

多线程条件变量(pthread_cond_wait)用法

条件变量是利用线程间共享得全局变量进行同步的一种机制&#xff0c;主要包括两个动作&#xff1a;一个线程等待“条件变量的条件成立”而挂起&#xff1b;另一个线程使“条件成立”给出条件成立信号。为了防止竞争&#xff0c;条件变量得使用总是和一个互斥锁结合在一起。 1、…

mysql 文件系统规划_Mysql的文件系统规划以及日志配置

Mysql服务器文件系统规划:/dev/sda1 /boot/dev/sda2 //dev/sda3 /home/dev/sda4 /tmp/dev/sdb1 /data/dev/sdc1 /logsda:系统目录 sdb:数据目录 sdc:日志目录linux调度策略&#xff1a;--查看当前的调度策略:less /sys/block/sdb/queue/scheduler[noop] anticipatory deadli…

设备驱动框架4——将驱动集成到内核中

以下内容源于朱有鹏嵌入式课程的学习与整理&#xff0c;如有侵权请告知删除。 驱动集成到内核的概念 驱动开发的步骤一般是&#xff1a; &#xff08;1&#xff09;以模块的形式在内核外部编写与调试 &#xff08;2&#xff09;将调试好的驱动代码集成到kernel中 之前我们编写的…

VirtualBox 虚拟机的网络设置

http://cnzhx.net/blog/virtualbox-vm-network-setting/ 注意&#xff1a;修改第一个网卡虚拟机&#xff0c;此处网络设置添加的是第二个网卡虚拟机&#xff1f;&#xff1f;转载于:https://www.cnblogs.com/ztinass/p/3771225.html

[ios]UITableViewCell自适应高度 【转】

- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath { // 列寬 CGFloat contentWidth self.tableView.frame.size.width; // 用何種字體進行顯示 UIFont *font [UIFont systemFontOfSize:13]; // 該…

MYSQL存储磁盘创建vgcreate_【转】parted创建硬盘分区并创建LVM

目的&#xff1a;将两个三T的硬盘做成LVM(sdc,sdd)一、parted将硬盘进行分区&#xff1a;1)parted的命令方式Parted 命令分为两种模式&#xff1a;命令行模式和交互模式。1、命令行模式&#xff1a; parted [option] device [command] ,该模式可以直接在命令行下对磁盘进行分区…

Linux设备驱动模型1——设备驱动模型的简介与底层架构

以下内容源于朱有鹏嵌入式课程的学习与整理&#xff0c;如有侵权请告知删除。 一、linux设备驱动模型简介 1、设备驱动模型的含义 设备驱动模型主要体现为以下四个方面&#xff1a; &#xff08;1&#xff09;类class、总线bus、设备device、驱动driver 这四个概念分别对应着四…

例子简单说说C# ref和out

首写从这字段看 ref 就是引用的意思 out当然就是输出了public void getRefStr(ref string str) {str"hello 你好&#xff0c;你变成了Ref了" }public void getOutStr(out string outStr){outStr "hello 你好&#xff0c;你是out输出的值";} protected…

VARIANT变体类型数据

2019独角兽企业重金招聘Python工程师标准>>> 特殊 Variant 是一种特殊的数据类型&#xff0c;除了定长String数据及用户定义类型外&#xff0c;可以包含任何种类的数据。Variant 也可以包含Empty、Error、Nothing及Null等特殊值。可以用VarType函数或TypeName函数来…

mysql修改校对集_MySQL 图文详细教程之校对集问题

软件安装&#xff1a;装机软件必备包SQL是Structured Query Language(结构化查询语言)的缩写。SQL是专为数据库而建立的操作命令集&#xff0c;是一种功能齐全的数据库语言。在使用它时&#xff0c;只需要发出“做什么”的命令&#xff0c;“怎么做”是不用使用者考虑的。SQL功…