设备驱动框架之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.mzph.cn/bicheng/35898.shtml

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

相关文章

面试-Java线程池

1.利用Excutors创建不同的线程池满足不同场景的需求 分析&#xff1a; 如果并发的请求的数量非常多&#xff0c;但每个线程执行的时间非常短&#xff0c;这样就会频繁的创建和销毁线程。如此一来&#xff0c;会大大降低系统的效率。 可能出现&#xff0c;服务器在为每个线程创建…

Docker创建容器时提示 [Warning] IPv4 forwarding is disabled. Networking will not work.

解决办法 1. 在docker的宿主机中更改以下文件&#xff1a; [rootlocalhost ~]# vi /usr/lib/sysctl.d/00-system.conf2. 添加如下代码&#xff1a; net.ipv4.ip_forward13. 重启network服务&#xff1a; systemctl restart network

Lua 垃圾回收

Lua 垃圾回收 Lua 是一种轻量级的编程语言&#xff0c;广泛用于游戏开发、脚本编写和其他应用程序。Lua 的设计哲学是简单和高效&#xff0c;这同样体现在它的内存管理机制中。在 Lua 中&#xff0c;垃圾回收&#xff08;Garbage Collection&#xff0c;简称 GC&#xff09;是…

利用powershell开展网络钓鱼

要确保人们打开我们的恶意文件并执行它们&#xff0c;我们只需让微软努力工作多年来赢得人们的信任&#xff0c;然后将一些危险的宏插入到幻灯片中。 本博文将介绍如何通过屏幕顶部的一个友好的警告提示&#xff0c;在用户启用宏后立即运行您的宏。 首先&#xff0c;我们需要打…

【面向对象】复习(四)

const构不构成重载 C 加const能不能构成重载的几种情况_多个const修饰可以重载吗-CSDN博客

如何在Java中实现并发编程,包括锁机制、线程池等。

在Java中实现并发编程有很多种方式&#xff0c;包括使用锁机制、线程池等。以下是一些基本的步骤和代码示例&#xff1a; 1. **锁机制**&#xff1a;Java提供了多种锁机制&#xff0c;包括ReentrantLock&#xff0c;synchronized关键字等。ReentrantLock是一个可重入的锁&…

[分布式网络通讯框架]----MprpcApplication实现

在之前我们提到了很多回MprpcApplication类&#xff0c;这是一个mprpc框架的初始化类。 在此之前&#xff0c;先来看看MprpcConfig 类 MprpcConfig 类 重要成员变量 std::unordered_map<std::string,std::string> m_configMap;存放配置信息键值对。 本项目配置信息 …

Spring Boot中的国际化(i18n)实现技巧

Spring Boot中的国际化&#xff08;i18n&#xff09;实现技巧 大家好&#xff0c;我是免费搭建查券返利机器人省钱赚佣金就用微赚淘客系统3.0的小编&#xff0c;也是冬天不穿秋裤&#xff0c;天冷也要风度的程序猿&#xff01;在开发多语言支持的应用程序时&#xff0c;国际化…

python环境变量

目录 python环境变量 python-opencv cuda cudnn pytorch pycharm 激活ok了 pyqt5 labelimg notepad gpu-z python 3.6或3.7 标注,文件路径不能有 python环境变量 import os import syscurrent_dir = os.path.dirname(os.path.abspath(__file__))paths = [os.path.abspath(…

浏览器扩展V3开发系列之 chrome.commands 快捷键的用法和案例

【作者主页】&#xff1a;小鱼神1024 【擅长领域】&#xff1a;JS逆向、小程序逆向、AST还原、验证码突防、Python开发、浏览器插件开发、React前端开发、NestJS后端开发等等 chrome.commands API 允许开发者使用快捷键来执行特定的命令。 在使用 chrome.commands API 之前必须…

Vue 学习之 axios

目录 执行安装命令&#xff1a;npm install axios 使用的时候导入 axios以data&#xff0c;params&#xff0c;headers传参方式的区别 axios封装 是一个基于 promise 的 网络请求库&#xff0c;作用于浏览器和 node.js 中。使用Axios可以在前端项目中发送各种方式的HTTP请求…

如何查看websocket连接信息

Chrome 浏览器中查看 webSocket 连接信息_谷歌浏览器看不到 websocket-CSDN博客 Getting Started — Flask-SocketIO documentation 运作原理 | Socket.IO

VS Code准备JAVA环境

背景 由于IntelliJ IDEA是需要激活码的,简单的java demo测试,除了可以直接使用命令行java和javac进行执行, 推荐使用VS code, 其功能比较强大,扩展插件也比丰富,对阅读和书写代码都是比较友好的. JDK环境准备 Linux JDK可以使用Open JDK 11. sudo apt-get install openjdk-1…

Git+Maven+Jenkins的增量发布

项目结构大致如下&#xff1a; platform-server ├─doc ├─platform-base ├─platform-bom-dependencies ├─platform-commons │ ├─platform-commons-core │ ├─platform-commons-dubbo │ ├─platform-commons-geo │ ├─platform-commons-lang │ ├─platf…

UE引擎实现ShadowMap、体积光(C++)

前言 整体上参考了YivanLee大佬的这两篇文&#xff1a; 虚幻4渲染编程&#xff08;灯光篇&#xff09;【第一卷&#xff1a;各种ShadowMap】 虚幻4渲染编程&#xff08;灯光篇&#xff09;【第二卷&#xff1a;体积光】 正文 1、ShadowMap &#xff08;1&#xff09;创建工…

【浦语开源】深入探索:大模型全链路开源组件 InternLM Lagent,打造灵笔Demo实战指南

一、准备工作&#xff1a; 1、环境配置&#xff1a; pip、conda换源&#xff1a; pip临时换源&#xff1a; pip install -i https://mirrors.cernet.edu.cn/pypi/web/simple some-package# 这里的“https://mirrors.cernet.edu.cn/pypi/web/simple”是所换的源&#xff0c;…

C++设计模式---模版方法模式

1、介绍 概念&#xff1a; 模板方法模式&#xff08;Template Method Pattern&#xff09;【行为型模式】&#xff0c;定义一个操作中算法的骨架&#xff0c;而将一些步骤延迟到子类中&#xff0c;模板方法使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤。 …

AI绘画Stable Diffusion人物背景替换实操教程,让创意无限延伸

大家好&#xff0c;我是画画的小强 Stable Diffusion以其强大的能力可以实现人物背景的更换。本文将带你深入了解如何利用Stable Diffusion中的Inpaint Anything插件快速且精准地实现人物背景的替换&#xff0c;从而让你的图片焕发新生。 前期准备 本文会使用到Inpaint Anyt…

密码学及其应用——专用名词(法语版)

1. 密码学概念 1. cryptographie - 密码学 2. cryptosystme - 密码系统 3. cryptographie symtrique - 对称密码学 4. cryptographie asymtrique - 非对称密码学 5. cryptographie quantique - 量子密码学 6. stganographie - 隐写术 2. 加密与解密 1. cryptage - 加密 2. dc…

如何优化Spring Boot应用的启动时间

如何优化Spring Boot应用的启动时间 大家好&#xff0c;我是免费搭建查券返利机器人省钱赚佣金就用微赚淘客系统3.0的小编&#xff0c;也是冬天不穿秋裤&#xff0c;天冷也要风度的程序猿&#xff01;今天我们将讨论如何优化Spring Boot应用的启动时间&#xff0c;提升应用的性…