设备驱动框架之LED

文章目录

  • 前言
  • 一、什么是驱动框架
  • 二、使用步骤
    • 1.注册LED设备
    • 2.卸载LED设备
    • 3.内核中申请内存
    • 4.container_of
    • 5.platform_get_drvdata 和 platform_set_drvdata
    • 6.module_platform_driver
  • 三、驱动示例
  • 总结


前言

为了尽量降低驱动开发者难度以及接口标准化,就出现了设备驱动框架的概念;


一、什么是驱动框架

Linux 针对每个种类的驱动设计一套成熟的、标准的、典型的驱动实现, 然后把不同厂家的同
类硬件驱动中相同的部分抽出来自己实现好,再把不同部分留出接口给具体的驱动开发工程
师来实现,这就叫驱动框架。
说白了,设备驱动框架就是对原始的驱动开发接口进行了封装,不同种类的设备有不同
的封装和实现方法,驱动开发工程师根据这些封装好的接口来编写自己的驱动程序,这就是
设备驱动框架。
内核源码 drivers/leds 目录下有两个文件 ledclass.c 和 led-core.c,它们就是 LED 驱动框架的核心文件,由内核开发工作者进行维护和升级。
在这里插入图片描述

二、使用步骤

1.注册LED设备

LED 驱动框架提供了 led_classdev_register 宏用于注册 LED 设备,该宏定义在内核源码 drivers/leds/leds.h 头文件中

extern int of_led_classdev_register(struct device *parent,struct device_node *np,struct led_classdev *led_cdev);
#define led_classdev_register(parent, led_cdev) \
of_led_classdev_register(parent, NULL, led_cdev)parent: LED 设备的父设备, struct device 类型指针变量。
led_cdev: 需要注册的 LED 设备结构体,LED 驱动框架中使用struct led_classdev结构体来描述一个 LED设备。
返回值:成功返回 0,失败则返回一个负数struct led_classdev 结构体的内容,该结构体定义在 drivers/leds/leds.h 头文件中
struct led_classdev {const char *name; // 设备名字enum led_brightness brightness; // LED 默认亮度enum led_brightness max_brightness; // LED 的最大亮度。。。
}描述亮度的枚举类型:
enum led_brightness {LED_OFF = 0,LED_ON = 1,LED_HALF = 127,LED_FULL = 255,
}

2.卸载LED设备

void led_classdev_unregister(struct led_classdev *led_cdev)led_cdev:需要卸载的 led 设备结构体, struct led_classdev 结构体类型指针变量
返回值:无

3.内核中申请内存

1.kmalloc

void *kmalloc(size_t size, gfp_t flags);size:需要分配的内存大小。
flags:分配内存时所使用的标志位,这些 flag 定义在 include/linux/gfp.h 头文件中
主要:GFP_ATOMIC: 分配内存的过程是一个原子过程,分配内存的过程不会被(高优先级进程或中断)打断。GFP_KERNEL: 内核空间中正常的内存分配过程,也是用的最多的 flag。GFP_KERNEL_ACCOUNT: GFP_KERNEL_ACCOUNT 与 GFP_KERNEL 相同,只是分配是由 kmemcg 负责。GFP_DMA: 给 DMA 控制器分配内存,需要使用该标志( DMA 要求分配虚拟地址和物理地址连续)。
返回值:申请成功返回的就是内存的起始地址,申请失败返回 NULL

kmalloc()申请的内存位于物理内存映射区域,而且在物理上也是连续的,它们与真实的物理地址只有一个固定的偏移,因为存在较简单的转换关系,所以对申请的内存大小有限制,不能超过 128KB。 如何要释放内存可以使用 kfree 函数,函数原型如下:

void kfree(const void *addr);

2.kzalloc
kzalloc()函数与 kmalloc()非常相似,参数及返回值是一样的, kzalloc()函数除了申请内存
空间之外,还会对申请到的内存空间进行清零。同样 kzalloc()对应的内存释放函数也是 kfree()。

void *kzalloc(size_t size, gfp_t flags)

3.vmalloc

void *vmalloc(unsigned long size);
void vfree(const void *addr);

函数只有一个参数,就是需要申请的内存空间大小,返回值同样也是内存空间其实地址。 vmalloc()函数则会在虚拟内存空间给出一块连续的内存区域,但这片连续的虚拟内存在物理内存中并不一定连续。
由于 vmalloc()没有保证申请到的是连续的物理内存,因此对申请的内存大小没有限制,如果需要申请较大的内存空间就需要用此函数了。
vmalloc 函数和 vfree 函数可以睡眠,因此不能在中断上下文中使用。

4.总结
kmalloc()、 kzalloc()、 vmalloc()的共同特点是:
1.用于申请内核空间的内存;
2.内存以字节为单位进行分配;
3.所分配的内存空间在虚拟地址上连续;

区别:
1.kzalloc 是强制清零的 kmalloc 操作;
2.kmalloc 分配的内存大小有限制( 128KB),而 vmalloc 没有限制;
3.kmalloc 可以保证分配的内存物理地址是连续的,但是 vmalloc 不能保证;
4.kmalloc 分配内存的过程可以是原子过程(使用 GFP_ATOMIC),而 vmalloc 分配内存时则可能产生阻塞(休眠) ;
5.kmalloc 分配内存的开销小,因此 kmalloc 比 vmalloc 要快;

一般情况下,内存只有在被 DMA访问的时候才需要物理上连续,但为了性能上的考虑,内核中一般使用 kmalloc(),而只有在需要获得大块内存时才使用 vmalloc()。
例如,当模块被动态加载到内核当中时,就把模块装载到由 vmalloc()分配的内存上。

  1. devm_kmalloc 和 devm_kzalloc

一样是内核内存分配函数,但跟设备有关,当内核中设备被卸载时,内存会被自动释放

void *devm_kzalloc(struct device *dev, size_t size, gfp_t gfp)
void *devm_kmalloc(struct device *dev, size_t size, gfp_t gfp)当内存不再使用时, 也可以手动使用函数 devm_kfree 释放
void devm_kfree(struct device *dev, void *p)

凡是使用了 devm_xxx 开头的函数都是与设备挂钩的,也都是可以在设备被卸载的时候由系统自动释放。

4.container_of

宏定义在 linux 源码目录下的 include/linux/kernel.h 中。

 /*** container_of - cast a member of a structure out to the containing structure* @ptr:    the pointer to the member.* @type:   the type of the container struct this is embedded in.* @member: the name of the member within the struct.** WARNING: any const qualifier of @ptr is lost.*/#define container_of(ptr, type, member) ({              \void *__mptr = (void *)(ptr);                   \static_assert(__same_type(*(ptr), ((type *)0)->member) ||   \__same_type(*(ptr), void),            \"pointer type mismatch in container_of()");   \((type *)(__mptr - offsetof(type, member))); })

在linux内核用得非常普遍,用于从包含在某个结构体中的指针获得结构体本身的指针,通俗地讲就是通过结构体变量中某个成员的首地址进而获得整个结构体变量的首地址。
需要提供三个参数,第一个就是结构体变量中某个成员的首地址,第二个参数是结构体的类型,第三个参数是该成员的名字。

5.platform_get_drvdata 和 platform_set_drvdata

两个函数定义在内核源码 include/linux/platform_device.h 头文件中

static inline void *platform_get_drvdata(const struct platform_device *pdev)
{return dev_get_drvdata(&pdev->dev);
}static inline void platform_set_drvdata(struct platform_device *pdev,void *data)
{dev_set_drvdata(&pdev->dev, data);
}

用于保存data指针数据到pdev->dev.driver_data,通常用于保存相关数据,方便通过struct platform_device获取数据并使用。

6.module_platform_driver

宏定义在内核源码 include/linux/platform_device.h头文件中

#define module_platform_driver(__platform_driver) \module_driver(__platform_driver, platform_driver_register, \platform_driver_unregister)#define module_driver(__driver, __register, __unregister, ...) \
static int __init __driver##_init(void) \
{ \return __register(&(__driver) , ##__VA_ARGS__); \
} \
module_init(__driver##_init); \
static void __exit __driver##_exit(void) \
{ \__unregister(&(__driver) , ##__VA_ARGS__); \
} \
module_exit(__driver##_exit);

功能就是定义模块入口函数和出口函数,宏展开后跟以前platform注册和卸载是一样的。

驱动加载成功之后查看/sys/bus/platform/devices 目录下的文件看是否存在 led 设备(设备树节点名)。
查看/sys/bus/platform/drivers 目录下的文件是否存在 zynq-led 驱动(.driver中的name)。
可以通过去读写/sys目录下属性文件的方式操作设备,如/sys/bus/platform/devices/led/leds目录下brightness、 max_brightness、 trigger 等这些文件就是属性文件,是LED驱动框架核心层代码帮我们实现的。

三、驱动示例

设备树节点如下所示:

	led{compatible = "my_led";status = "okay";default-status = "on";led-gpio = <&gpio 38 GPIO_ACTIVE_HIGH>};

驱动代码:

#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/of_gpio.h>
#include <linux/platform_device.h>
#include <linux/leds.h>struct myled_data {struct led_classdev cdev;	//用于描述一个LED设备int gpio;
};
//通过 struct myled_data 结构体变量中 cdev 成员的首地址进而得到整个 struct myled_data 结构体变量的首地址
static inline struct myled_data *cdev_to_led_data(struct led_classdev *led_cdev)
{return container_of(led_cdev,struct myled_data,cdev);
}void myled_brightness_set(struct led_classdev *led_cdev,enum led_brightness value)
{struct myled_data *led_data = cdev_to_led_data(led_cdev);int level;if(value == LED_OFF)level = 0;elselevel = 1;gpio_set_value(led_data->gpio,level);
}int myled_brightness_set_blocking(struct led_classdev *led_cdev,enum led_brightness value)
{myled_brightness_set(led_cdev,value);return 0;
}int led_probe(struct platform_device *pdev)
{int ret = 0;struct myled_data *led_data;struct led_classdev *led_cdev;dev_info(&pdev->dev,"led probe begin init success !\n");led_data = devm_kzalloc(&pdev->dev,sizeof(struct myled_data),GFP_KERNEL);if(!led_data)return -ENOMEM;led_data->gpio = of_get_named_gpio(pdev->dev.of_node,"led-gpio",0);ret = devm_gpio_request(&pdev->dev,led_data->gpio,"LED GPIO");if(ret){dev_err(&pdev->dev,"devm_gpio_request failed !\n");return ret;}gpio_direction_output(led_data->gpio,0);platform_set_drvdata(pdev,led_data);led_cdev = &led_data->cdev;led_cdev->name = "myled";led_cdev->brightness = LED_OFF;	//	初始亮度led_cdev->max_brightness = LED_FULL;	//最大亮度led_cdev->brightness_set = myled_brightness_set;	//绑定设置函数,不可休眠led_cdev->brightness_set_blocking = myled_brightness_set_blocking;	//绑定设置函数,可休眠return led_classdev_register(&pdev->dev,led_cdev);
}int led_exit(struct platform_device *pdev)
{struct myled_data *led_data = platform_get_drvdata(pdev);led_classdev_unregister(&led_data->cdev);return 0;
}struct of_device_id led_of_match[] = {{.compatible = "my_led"},
};struct platform_driver myled_driver = 
{.driver = {.name = "zynq_led",.of_match_table = led_of_match,},.probe = led_probe,.remove = led_exit,
};module_platform_driver(myled_driver);MODULE_AUTHOR("LZW");
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("LED DRIVER BASE ON LED FRAMEWORK");

总结

以上就是今天要讲的内容,本文简单介绍了Linux内核中的LED驱动框架,还有一些常用的接口及使用方法。制作不易,多多包涵。

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

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

相关文章

基于weixin小程序智慧物业系统的设计

管理员账户功能包括&#xff1a;系统首页&#xff0c;个人中心&#xff0c;管理员管理&#xff0c;用户管理&#xff0c;员工管理&#xff0c;房屋管理&#xff0c;缴费管理&#xff0c;车位管理&#xff0c;报修管理 工作人员账号功能包括&#xff1a;系统首页&#xff0c;维修…

数据库原理之数据库基本概念

目录 前言 基本概念 数据库完整性 前言 今天我们来看看数据库的基本概念&#xff0c;帮助大家对数据库有一点点最基本的了解 基本概念 4个基本概念 数据data&#xff1a;描述事物的符号&#xff0c;数据库中存储的基本对象。 数据库Database&#xff1a;长期存储在计算机…

基于JSP技术的旅游网站系统

开头语&#xff1a;你好呀&#xff0c;我是计算机学长猫哥&#xff01;如果有相关需求&#xff0c;文末可以找到我的联系方式。 开发语言&#xff1a;Java 数据库&#xff1a;MySQL 技术&#xff1a;B/S架构&#xff0c;JSP技术 工具&#xff1a;Eclipse&#xff0c;Tomcat…

PostMan动态设置全局变量

1. 前言 在开发过程中调试接口&#xff0c;一般都会使用PostMan。 其中有几个变量可能是好几个接口共用的&#xff0c;就会出现频繁手动复制(ctrlc)、粘贴(ctrlv)的情况。 这个过程得非常留意&#xff0c;生怕复制错了&#xff0c;或删减了某些东西&#xff0c;导致接口报错。…

Open3D 显示带有强度的点云数据

目录 一、概述 1.1强度信息的意义 1.2应用场景 二、代码实现 三、实现效果 一、概述 在点云数据中&#xff0c;强度&#xff08;Intensity&#xff09;指的是激光雷达传感器在扫描环境时&#xff0c;每个点返回的反射强度值。这些强度值代表了激光脉冲返回的能量&#xff…

Houdini 通过wedge来做模拟参数对比 (PDG TOP)

我们的设定如下例子 这是个简单的布料悬挂的例子。上方两个角分别被固定住了&#xff0c;然后在distance约束下布料下垂。 我们现在的目的是想要对比不同的streach stiffness对模拟的影响。 第一步&#xff1a;找到stiffness参数&#xff0c;右键expression->edit expre…

基于Pytorch框架的深度学习ConvNext神经网络宠物猫识别分类系统源码

第一步&#xff1a;准备数据 12种宠物猫类数据&#xff1a;self.class_indict ["阿比西尼猫", "豹猫", "伯曼猫", "孟买猫", "英国短毛猫", "埃及猫", "缅因猫", "波斯猫", "布偶猫&q…

【uni-app学习手札】

uni-app&#xff08;vue3&#xff09;编写微信小程序 编写uni-app不必拘泥于HBuilder-X编辑器&#xff0c;可用vscode进行编写&#xff0c;在《微信开发者工具》中进行热加载预览&#xff0c; 主要记录使用uni-app过程中自我备忘一些api跟语法&#xff0c;方便以后编写查找使用…

论文翻译 | ITER-RETGEN:利用迭代检索生成协同增强检索增强的大型语言模型

论文地址&#xff1a;Enhancing Retrieval-Augmented Large Language Models with Iterative Retrieval-Generation Synergy 摘要 检索增强生成由于有望解决包括过时知识和幻觉在内的大型语言模型的局限性而引起广泛关注。然而&#xff0c;检索器很难捕捉相关性&#xff0c;尤…

开发板以电脑为跳板连接互联网

标题 开发板以电脑为跳板连接互联网网络共享方式桥接方式 开发板以电脑为跳板连接互联网 分享下用网线直连电脑的开发板如何以电脑为跳板连接互联网的两个方法。 网络共享方式桥接方式 补充下&#xff0c;我的电脑连接的是无线网络&#xff0c;开发板和电脑是用网线进行连接的…

ComfyUI如何使用Face Detailer和ComfyI2I插件进行修脸

一.插件ComfyI2I使用 1.ComfyUI中调用Mask Ops 2.创建蒙版插件BBOX Detector(combined) 3.创建UltralyticsDetectorProvider 里面包含多个模型其中bbox/face_yolov8m.pt是针对脸部修复 4.组合后测试脸部蒙版识别是否正常 5.测试正常后调出Inpaint Segments&#xff0c;放大…

作 业 二

cs与msf权限传递 1、进入cs界面,首先来到 Cobalt Strike 目录下&#xff0c;启动 Cobalt Strike 服务端 2、用客户端进 3、建立监听 4、生成脚本文件 5、开启服务&#xff0c;让win_2012 下载木马文件并运行 6、显示已经获取到了win的权限 转到Metasploit Framework 7、进去m…

公益培训|半导体与集成电路项目制培训项目

关于我们 硬蛋产业学院&#xff0c;基于硬蛋创新(http://00400.HK)在芯片产业的资源和技术优势&#xff0c;引进全球领先的芯片应用技术&#xff0c;为国内培养芯片应用技术人才&#xff0c;助力芯片应用产业发展。 硬蛋产业学院在国家各主管部门、广东省、深圳市及社会各界的大…

pycharm工具回退键调出

pycharm工具调出回退键。 View->Appearance->Toolbar,即可调出 调不出的可以使用快捷键&#xff1a;ctrlalt向左箭头 但是这个快捷键容易和电脑屏幕旋转冲突。可将电脑的快捷键关掉&#xff0c;即可。 ctrlalt向上箭头&#xff1a;将屏幕旋转到正常&#xff08;横向&am…

基于weixin小程序校园快递系统的设计

管理员账户功能包括&#xff1a;系统首页&#xff0c;个人中心&#xff0c;管理员管理&#xff0c;用户管理&#xff0c;订单管理&#xff0c;快递管理&#xff0c;快递记录管理&#xff0c;公告管理&#xff0c;基础数据管理 小程序功能包括&#xff1a;系统首页&#xff0c;…

YOLOv10改进 | 卷积模块 | 将Conv替换为轻量化的GSConv【轻量又涨点】

秋招面试专栏推荐 &#xff1a;深度学习算法工程师面试问题总结【百面算法工程师】——点击即可跳转 &#x1f4a1;&#x1f4a1;&#x1f4a1;本专栏所有程序均经过测试&#xff0c;可成功执行&#x1f4a1;&#x1f4a1;&#x1f4a1; 专栏目录&#xff1a;《YOLOv8改进有效…

H5实现第三方分享功能,(WhatsApp,Facebook,Messenger,Instagram,Telegram,Zalo,Twitter/X)

1. H5实现第三方分享功能 1. WhatsApp 分享 https://api.whatsapp.com/send/?phone&app_absent0&text${codeUrl}2. Facebook 分享 https://www.facebook.com/sharer/sharer.php?u${codeUrl}3. Messenger 分享 https://www.messenger.com/?${codeUrl}4. Instagra…

计算机网络:应用层 - 万维网 HTTP协议

计算机网络&#xff1a;应用层 - 万维网 & HTTP协议 万维网 WWW统一资源定位符 URL 超文本传输协议 HTTP非持续连接持续连接非流水线流水线 代理服务器HTTP报文 万维网 WWW 万维网是一个大规模的、联机式的信息储藏所。万维网用链接的方法能非常方便地从互联网上的一个站点…

eBPF 如何塑造 Linux 和平台工程的未来

当Docker 于 2013 年突然出现时&#xff0c;Linux 容器似乎一夜成名。但容器&#xff08;以及微服务和Kubernetes&#xff09;的演变实际上是基于 Linux 操作系统中的内核原语而进行的&#xff0c;历时数十年。 Docker 使用这些原语&#xff08;即 cgroups 和命名空间&#xf…

计算机Java项目|基于SpringBoot的基于保密信息学科平台系统

作者主页&#xff1a;编程指南针 作者简介&#xff1a;Java领域优质创作者、CSDN博客专家 、CSDN内容合伙人、掘金特邀作者、阿里云博客专家、51CTO特邀作者、多年架构师设计经验、腾讯课堂常驻讲师 主要内容&#xff1a;Java项目、Python项目、前端项目、人工智能与大数据、简…