驱动基础开发

news2025/1/11 23:56:43

1、字符设备传统开发模板

字符设备驱动框架,首先我们需要去用module_init这个宏去修饰整个驱动的入口函数,用module_exit去修饰整个驱动的出口函数,然后还需要用MODULE_LICENSE用于声明模块的许可证类型。
在入口函数里面我们需要注册字符设备,使用register_chrdev()注册字符设备,使用class_create来注册区分一个类,在用device_create来为这个类创造一个设备节点,供我们在linux根目录下的dev目录下给应用层程序访问。在register_chrdev注册时最重要的是要提供字符设备的结构体file_operations,这个结构体启动了内核和应用层交互数据的功能。需要实现file_operations结构体的open,write,read,release函数,对应于应用层的open,write,read,close

#include "asm-generic/errno-base.h"
#include "asm-generic/gpio.h"
#include "asm/uaccess.h"
#include <linux/module.h>
#include <linux/poll.h>
#include <linux/fs.h>
#include <linux/errno.h>
#include <linux/miscdevice.h>
#include <linux/kernel.h>
#include <linux/major.h>
#include <linux/mutex.h>
#include <linux/proc_fs.h>
#include <linux/seq_file.h>
#include <linux/stat.h>
#include <linux/init.h>
#include <linux/device.h>
#include <linux/tty.h>
#include <linux/kmod.h>
#include <linux/gfp.h>
#include <linux/gpio/consumer.h>
#include <linux/platform_device.h>
#include <linux/of_gpio.h>
#include <linux/of_irq.h>
#include <linux/interrupt.h>
#include <linux/irq.h>
#include <linux/slab.h>
#include <linux/fcntl.h>
#include <linux/timer.h>


struct gpios
{
	int gpio;
	int irq;
    char *name;
};

static gpois gpios_s[2]={
	{115,0,"sr51"},
};


static int major=0; 
static struct class *cls=NULL;




static ssize_t sr51_read(struct file *file, char __user *buf, size_t size, loff_t * loff)
{
	char ker_buf[2]={0};
	int err=0;
	err=copy_from_user(ker_buf, buf, 1);
	if(err)
	{
		 return -EINVAL;

	}
	ker_buf[1]=gpio_get_value(gpios_s[0].gpio);
	
	copy_to_user(buf, ker_buf,2)

	return 2;
}

static int sr51_release(struct inode *node, struct file *file)
{

	printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
	printk("fd close\n");

	return 0;

}

static int sr51_open(struct inode *node, struct file *file)
{

	printk("%s %s line %d\n",__FILE__,__FUNCTION__,__LINE__);
	return 0;
}

ssize_t sr51_write(struct file *file, const char __user *buf , size_t size , loff_t * loff)
{

	printk("%s %s line %d\n",__FILE__,__FUNCTION__,__LINE__);

	return 0;
}


static struct file_operations sr51_driver={
	.owner=THIS_MODULE,
	.open=sr51_open,
	.write=sr51_write,
	.read=sr51_read,
	.release=sr51_release,
};





static  int __init sr51_driver_init(void)
{
		int err;
		err=gpio_request(gpios_s[0].gpio, gpios[0].name);
		if (err < 0) {
			printk("can not request gpio %s %d\n", gpios_s[0].gpio, gpios[0].name);
			return -ENODEV;
		}
		
		gpio_direction_output(gpios[0].gpio, 1);

		major=register_chrdev(0, "sr51",&sr51_driver);
		cls=class_create(THIS_MODULE,"sr51_class");
		if(IS_ERR(cls))
		{
			printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
			unregister_chrdev(major,"sr51");
			return PTR_ERR(cls);
		}
		device_create(&cls, NULL, MKDEV(major, 0), NULL, "sr51");
		

		return 0;
}

static  void __exit sr51_driver_exit(void)
{
	device_destroy(cls, MKDEV(major, 0));
	unregister_chrdev(major,"sr51");
	class_destroy(cls);
	gpio_free(gpios_s[0].gpio);
}



module_init(sr51_driver_init);
module_exit(sr51_driver_exit);
MODULE_LICENSE("GPL");

2、不使用register_chrdev的另外一类驱动的注册方法

register_chrdev其实是cdev封装好的一个函数,他其实也会调用cdev_init(),cdev_add(),
初始化设备号的另一种方法
1、int alloc_chrdev_region(dev_t *dev, unsigned baseminor, unsigned count, const char *name);

1、dev_t *dev
这个参数是一个指向 dev_t 类型的指针。dev_t 是一个在 <sys/types.h> 中定义的数据类型,用于表示设备号。在调用 alloc_chrdev_region() 函数时,你需要传递一个 dev_t 类型的变量的地址给这个函数。如果函数成功地为你的字符设备驱动程序分配了一个设备号,那么它会将这个设备号存储在 dev 指针所指向的变量中。
2、unsigned baseminor次设备号
3、unsigned count几个次设备号
4、const char *name设备名称
2、void cdev_init(struct cdev *cdev, const struct file_operations *fops);
cdev设备号
fops字符设备结构体
3、int cdev_add(struct cdev *p, dev_t num, unsigned int count);
struct cdev *p:指向已经初始化的cdev结构体的指针。
dev_t num:分配给这个字符设备的设备号。这通常是通过alloc_chrdev_region()函数获得的。
unsigned int count:通常设置为1,除非你的字符设备支持多个次设备号。

3、当下用的最多的驱动开发,设备树加platform总线模型

设备树的作用:用于描述硬件的具体的功能。像gpio控制器,ii2c控制器,io多路复用控制器,spi控制器,中断控制器gic都需要使用设备树去描述这个硬件。
platform总线模型:他是一个虚拟的总线模型,内核里面使用bus_type这个结构体来描述这个虚拟总线,这个虚拟中心又分为左边一部分和右边一部分,左边一部分使用platform_device来描述,这个结构体它可以由程序员编写,也可以直接使用设备树自动生成(目前使用的是这个),而右边是使用的是platform_driver这个是由当下的驱动程序员来编写的,虚拟总线的功能就是让这两个设备相匹配,如果匹配到了,那么驱动就会被加载进内核当中。
3.1他们是怎么匹配到,然后加载进入内核的详细过程

在这里插入图片描述
1、设备树通常在系统引导阶段由引导加载器(如U-Boot)从预定义的配置文件加载,并传递给内核,它包含了硬件的层次结构和属性(如内存地址、中断号和GPIO配置等)。
2、设备树中的节点会被映射到内核中的platform_device结构体实例。每个设备节点通常包含compatible属性,此属性用于标识设备类型和需要加载的驱动。(第一次会比对)
3、设备通过platform_device_register函数在内核中注册。这个过程会为设备创建一个platform_device实例,并将其添加到platform_bus_type所代表的总线上。
设备注册后,内核会使用platform_bus_type中的.match函数来查找和该设备兼容的驱动程序。
4、驱动程序使用platform_driver_register函数注册自己,并提供一个platform_driver结构体,其中包含驱动程序的名称、匹配表、以及回调函数,如.probe和.remove。
.match回调函数确定一个驱动程序是否适用于某个特定的设备,这通常是基于设备的compatible属性进行匹配的。
5、当设备和驱动成功匹配时,驱动的.probe函数被调用,这是驱动初始化设备的地方。例如,驱动可能会配置设备使用的GPIO引脚,设置初始状态,或注册更高层的服务(如输入设备或网络接口)。
4、设备树的使用
设备树的常见属性:
compatible:用于在驱动匹配时使用的匹配字符串。
#address-cells:常见于父节点中用于规定子节点使用多少字长来描述硬件的起始地址。
#size-cells:常见于父节点中用于规定子节点使用多少字长来描述硬件的地址长度。

/ {
#address-cells = <1>;
#size-cells = <1>;
memory {
reg = <0x80000000 0x20000000>;
};
};

status:表示这个设备的状态
在这里插入图片描述
reg 节点:的本意是 register,用来表示寄存器地址。
但是在设备树里,它可以用来描述一段空间。反正对于 ARM 系统,寄存器和
内存是统一编址的,即访问寄存器时用某块地址,访问内存时用某块地址,在访
问方法上没有区别
chosen 节点:可以通过设备树文件给内核传入一些参数。

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

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

相关文章

mitmdump 实时抓包处理

mitmdump 是 mitmproxy 的命令行接口&#xff0c;可以对接 Python 脚本处理请求和响应&#xff0c;这是比 Fiddler , Charles 等工具更加方便的地方&#xff0c;有了它&#xff0c;我们不用再手动抓取和分析HTTP 请求和响应&#xff0c;只要写好请求和响应的处理逻辑就好了。 …

jenkins jdk8下载

jdk8 对应的 jenkins版本是2.346.1 http://updates.jenkins-ci.org/download/war/2.346.1/jenkins.warjenkins和jdk安装教程(安装支持jdk8的最新版本) https://blog.csdn.net/u013078871/article/details/127200623刚买的腾讯云安装jenkins步骤&#xff0c;服务器安装jenkins步…

基于华为atlas的皮带跑偏、空载、堆煤、启停探索

生乎吾前&#xff0c;其闻道也固先乎吾&#xff0c;吾从而师之&#xff1b;生乎吾后&#xff0c;其闻道也亦先乎吾&#xff0c;吾从而师之。吾师道也&#xff0c;夫庸知其年之先后生于吾乎&#xff1f;是故无贵无贱&#xff0c;无长无少&#xff0c;道之所存&#xff0c;师之所…

超详细!!!electron-vite-vue开发桌面应用之配置路由router(五)

云风网 云风笔记 云风知识库 一、安装依赖 npm install vue-router二、配置项目文件路径 三、配置路由router 在src下新建一个router目录&#xff0c;然后在里面添加一个index.ts文件&#xff0c;在里面配置路由 import { createRouter, createWebHashHistory } from vue-…

针对thinkphp站点的漏洞挖掘和经验分享

0x1 前言 浅谈 目前在学习和研究thinkphp相关漏洞的打法&#xff0c;然后最近对于thinkphp资产的收集方面有了一个简单的认识&#xff0c;然后写一篇新手看的thinkphp相关的漏洞收集和挖掘的文章来分享下。然后后面是给师傅们分享下后台文件上传&#xff0c;然后直接打一个ge…

RCE-eval长度限制绕过技巧

目录 限制16字符 题目源码 方法一&#xff1a;$_GET[1] 方法二&#xff1a;file_put_contents 方法三&#xff1a;usort(…$_GET); 限制7字符 题目源码 限制16字符 题目源码 <?php $param $_REQUEST[param]; If ( strlen($param) < 17 && stripos($param…

微服务系列:Spring Cloud 之 Feign、Ribbon、Hystrix 三者超时时间配置

Feign 自身有超时时间配置 Feign 默认集成的 Ribbon 中也有超时时间配置 假如我们又使用了 Hystrix 来实现熔断降级&#xff0c;Hystrix 自身也有一个超时时间配置 注: spring-cloud-starter-openfeign 低一点的版本中默认集成的有 Hystrix&#xff0c;高版本中又移除了。 …

Gin框架接入Prometheus,grafana辅助pprof检测内存泄露

prometheus与grafana的安装 grom接入Prometheus,grafana-CSDN博客 Prometheus 动态加载 我们想给Prometheus新增监听任务新增ginapp项目只需要在原来的配置文件下面新增ginapp相关metric 在docker compose文件下面新增 执行 docker-compose up -d curl -X POST http://lo…

C++ 设计模式——模板方法模式

模板方法模式 模板方法模式逐步重构并引入模板方法模式初始实现提取共性并引入模板方法模式实现具体类 完整代码示例模板方法模式的 UML 图UML 图详细介绍 模板方法模式适用于以下场景 模板方法模式 模板方法模式是一种行为设计模式&#xff0c;它定义了一个算法的骨架&#x…

C++11代码实战经典—MySQL数据库连接池

课程总目录 文章目录 一、项目介绍1.1 关键技术点1.2 项目背景1.3 连接池功能点介绍1.4 MySQL Server参数介绍1.5 项目功能点设计和技术细节 二、MySQL数据库编程三、项目代码逐步实现3.1 连接池单例模式实现3.2 实现加载配置项3.3 连接池的构造函数3.4 实现生产者3.5 实现消费…

其他浏览器正常,火狐浏览器ui-grid换行问题

ui-grid火狐浏览器兼容性问题 ui-grid表格插件问题描述解决方案 ui-grid表格插件 火狐浏览器 UI-grid 兼容性问题 其他如Edge、谷歌、360浏览器正常情况下 火狐浏览器 问题描述 如上图一和图二显示&#xff0c;UI-gird在火狐换行了&#xff1a;从图片来看&#xff1b;后面…

【车载开发系列】ASPICE标准实践---使用Drome系统保证一致性

【车载开发系列】ASPICE标准实践—使用Drome系统保证一致性 【车载开发系列】ASPICE标准实践---使用Drome系统保证一致性 【车载开发系列】ASPICE标准实践---使用Drome系统保证一致性一、一致性的目的二、ASPICE标准三、ASPICE标准实施难点四、保证一致性的实践1. 参与评审2. 可…

ES6-ES13学习笔记

目录 初识ES6 变量声明 解构赋值 对象解构 ​编辑 数组解构 ​编辑模版字符串 字符串扩展 includes() repeat() startsWith() endsWith() 数值扩展 二进制和八进制表示法 &#xff08;Number.&#xff09;isFinite()与isNaN() Number.isInteger() Math.trunc …

vue前端可以完整的显示编辑子级部门,用户管理可以为用户分配角色和部门?

用户和角色是一对多的关系用户和部门是多对多得关系<template><div class="s"><!-- 操作按钮 --><div class="shang"><el-input v-model="searchText" placeholder="请输入搜索关键词" style="width:…

上海凯泉泵业入职测评北森题库题型分析、备考题库、高分攻略

上海凯泉泵业&#xff08;集团&#xff09;有限公司是一家大型综合性泵业公司&#xff0c;专注于设计、生产、销售泵、给水设备及其控制设备。作为中国泵行业的领军企业&#xff0c;凯泉集团拥有7家企业和5个工业园区&#xff0c;总资产达到25亿元&#xff0c;生产性建筑面积35…

Python 在PDF中添加条形码、二维码

在PDF中添加条码是一个常见需求&#xff0c;特别是在需要自动化处理、跟踪或检索PDF文件时。作为一种机器可读的标识符&#xff0c;PDF中的条码可以包含各种类型的信息&#xff0c;如文档的唯一标识、版本号、日期等。以下是一篇关于如何使用Python在PDF中添加条形码或二维码的…

Linux 【进程替换】详细讲解

替换原理 进程是由PCB和内核数据结构以及进程的代码和数据形成 用 fork 创建子进程后执行的是和父进程相同的程序 ( 但有可能执行不同的代码分支 ), 子进程往往要调用一种 exec 函数来进行进程替换 ,对子进程进行替换由于原先子进程与父进程使用的是同一物理内存空间&#xff0…

前端 JavaScript 的 _ 语法是个什么鬼?

前言 我们有时候会看这样的前端代码&#xff1a; const doubled _.map(numbers, function(num) { return num * 2; });刚接触前端的童鞋可能会有点惊奇&#xff0c;不知道这个 _ 是什么语法&#xff0c;为什么这么神通广大&#xff1f; 其实 _ 是 Lodash 或 Underscore.js …

Django Project | 云笔记练习项目

文章目录 功能整体架构流程搭建平台环境子功能先创建用户表 并同步到数据库1.用户注册密码存储 -- 哈希算法唯一索引引发的重复问题 try登陆状态保持 -- 详细看用户登录状态 2. 用户登录会话状态时间 cookie用户登录状态校验 3. 网站首页4.退出登录5.笔记模块 列表页添加笔记 …

AFSim 仿真系统----脚本

概述 脚本为用户提供了一种在模拟中基于发生的事件执行复杂指令集的方式。该语言类似于 C# 和 Java&#xff0c;对于具备基本编程技能的人来说应该会很熟悉。它采用块结构&#xff0c;包含熟悉的声明、赋值和控制流语句&#xff0c;允许用户检查和操作模拟环境。 脚本本质上是由…