[Linux_IMX6ULL驱动开发]-驱动的分层及实现

目录

驱动分层的思路

驱动分层的实现

上层驱动的实现

次设备号的使用

上层驱动代码

底层驱动的实现

底层驱动c文件的实现

底层驱动头文件实现

应用层文件的实现


驱动分层的思路

在上一篇文章中,博主实现了通过寄存器控制引脚,以此来达到控制LED灯亮灭。但是其实,把对寄存器的映射等步骤在驱动文件中实现并不是很好的实现,因为不同的开发板,引脚、寄存器都是不同的,为了达到兼容的目的,我们最好实现如下分层,把驱动分为上层和下层,驱动的上层完成file_operation结构体注册、class类、devices设备的注册等。下层为具体的硬件实现,包括完成寄存器的映射、初始化以及硬件控制等。


驱动分层的实现

下图是实现驱动分层的具体实现思路

上层驱动的实现

在上层驱动中,我们主要完成的是,完成file_operation结构体的构造和注册、class类、device设备的创建(退出对应的函数需要完成上述三个的注销,devices一定要先于calss类销毁,否则会造成内核异常操控空指针,驱动会崩溃

上层驱动较为重要的步骤是:

        包含对应的定义的头文件

        获取对应的结构体,以此能够调用底层驱动实现的初始化、操控等函数

        在对应要填充到file_operation结构体的函数中,实现对应的初始化、操控等步骤

次设备号的使用

假如我们这里的需求是控制两盏LED灯,那么我们就需要用到次设备号来区分这两个LED灯,次设备号不同,代表我们需要的灯就不同,那么所需要初始化的引脚和操控的引脚也不同,在这里我们传入不同的次设备号,在驱动底层我们通过传入的次设备号来判断到底是进行哪个引脚的初始化。

上层驱动代码

注意,该代码中没有进行映射内存的释放,没有在file_operation结构体中定义释放函数,所以应用层并不能通过close来关闭此设备,无法释放映射的物理地址

#include <linux/module.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 "file_operation.h"/* 不同板子的操作函数结构体 */
struct led_operation* p_ledopr_func;/* 确定主设备号 */
static int major = 0;
/* 缓存数组 */
static char kernal_buf[1024];
/* 节点的定义 */
static struct class *led_class;/* 读多少的宏定义 */
#define data_num(a,b) ( (a) < (b) ? (a) : (b) )/* 定义函数入口地址 */
static int led_drv_open (struct inode *node, struct file *file)
{int minor = iminor(node);printk("%s %s %d \n",__FILE__,__FUNCTION__,__LINE__);/* 通过次设备号初始化灯 */p_ledopr_func->init(minor);return 0;
}static ssize_t led_drv_read (struct file *file, char __user *buf, size_t size, loff_t *offset)
{int return_size;printk("%s %s %d \n",__FILE__,__FUNCTION__,__LINE__);return_size = copy_to_user(buf, kernal_buf, data_num(1024,size));return return_size;}
static ssize_t led_drv_write (struct file *file, const char __user *buf, size_t size, loff_t *offset)
{int return_size;char status;/* 根据file结构体获得inode,然后获取次设备号 */struct inode* inode = file_inode(file);int minor = iminor(inode);printk("%s %s %d \n",__FILE__,__FUNCTION__,__LINE__);return_size = copy_from_user(&status, buf, 1);/* 根据次设备号和status控制LED */p_ledopr_func->ctl(minor,status);return 1;}
static int led_drv_rease (struct inode *node, struct file *file)
{printk("%s %s %d \n",__FILE__,__FUNCTION__,__LINE__);return 0;
}/* 定义文件结构体读,写,打开,卸载
*/
static struct file_operations led_driver = {.owner = THIS_MODULE,.open = led_drv_open,.read = led_drv_read,.write = led_drv_write,.release = led_drv_rease,
};/* 把结构体注册到内核为了能够把该结构体注册到内核需要init函数
*/
static int __init led_init(void)
{int err;int i;printk("%s %s %d \n",__FILE__,__FUNCTION__,__LINE__);/* 注册结构体到内核后,返回主设备号 */major = register_chrdev(0, "myled", &led_driver);//创建节点 /dev/ledled_class = class_create(THIS_MODULE, "myled_class");err = PTR_ERR(led_class);if (IS_ERR(led_class)){printk("%s %s %d \n",__FILE__,__FUNCTION__,__LINE__);/* 创建失败的话摧毁内核中的led结构体 */unregister_chrdev( major, "myled");return -1;}p_ledopr_func = get_board_led_operation();/* 创建了节点后,需要创建设备 *//* 因为LED不止一个,需要多个次设备号 */for( i = 0 ; i < p_ledopr_func->num ; i++ )device_create(led_class, NULL, MKDEV(major, i), NULL, "myled%d" , i);return 0;
}/* 有注册函数就有卸载函数 */
static void __exit led_exit(void)
{int i;printk("%s %s %d \n",__FILE__,__FUNCTION__,__LINE__);/* 把device卸载 */for( i = 0 ; i < p_ledopr_func->num ; i++ )device_destroy(led_class, MKDEV(major, i));/* 把class卸载 */class_destroy(led_class);/* 把file_operation从内核中卸载 */unregister_chrdev( major, "myled");}/* 需要用某些宏表示上述两个函数分别是入口函数和出口函数 */
module_init(led_init);
module_exit(led_exit);/* 遵循GPL协议 */
MODULE_LICENSE("GPL");

底层驱动的实现

底层驱动的实现主要就是通过上层驱动传递下来的次设备号来判断,到底是初始化哪些寄存器,应该映射哪些硬件地址进行操作。同时还需把操控硬件的结构体返回给上层驱动。

底层驱动c文件的实现

#include <linux/module.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 <asm/io.h>
#include "file_operation.h"/* 一个灯为 GPIO5组的03     *//* 使能时钟 */
static volatile unsigned int* CCM_CCGR1 = NULL;
/* 引脚复用 */
static volatile unsigned int* IOMUXC_SNVS_SW_MUX_CTL_PAD_SNVS_TAMPER3 = NULL;
/* 设置为输出模式 */
static volatile unsigned int* GPIO5_GDIR = NULL;
/* 设置输出高电平/低电平 */
static volatile unsigned int* GPIO5_DR = NULL;static int board_demo_led_init (int which) /* 初始化LED, which-哪个LED */	   
{/* 在初始化函数中,实现初始化、复用引脚设置为GPIO模式、设置为输出模式 *//* 通过次设备号判断操控哪个LED */if( 0 == which ){/* 只映射一次 */if( NULL == CCM_CCGR1 ){CCM_CCGR1 = ioremap(0x20C406C , 4 );IOMUXC_SNVS_SW_MUX_CTL_PAD_SNVS_TAMPER3 = ioremap(0x2290014 , 4 );GPIO5_GDIR = ioremap(0x020AC004, 4);GPIO5_DR = ioremap(0x020AC000, 4);	}*CCM_CCGR1 |= (3 << 30);/* 因为引脚复用为GPIO为0101,包含0,所以先清零,防止原本的位包含1,导致|1后,还会是1 */*IOMUXC_SNVS_SW_MUX_CTL_PAD_SNVS_TAMPER3 &= ~(0xf);*IOMUXC_SNVS_SW_MUX_CTL_PAD_SNVS_TAMPER3 |= 5;*GPIO5_GDIR |= (1 << 3);}/* 初始化另一盏灯 */else {}	printk("%s %s line %d, led %d\n", __FILE__, __FUNCTION__, __LINE__, which);return 0;
}static int board_demo_led_ctl (int which, char status) /* 控制LED, which-哪个LED, status:1-亮,0-灭 */
{printk("%s %s line %d, led %d, %s\n", __FILE__, __FUNCTION__, __LINE__, which, status ? "on" : "off");if( 0 == which ){/* 低电平点灯 */if( 1 == status )*GPIO5_DR &= ~(1 << 3);else *GPIO5_DR |= (1 << 3);}/* 打开另一盏灯 */else{}return 0;
}static struct led_operation board_demo_led_opr = {.num = 1,.init = board_demo_led_init,.ctl  = board_demo_led_ctl,
};struct led_operation *get_board_led_operation(void)
{return &board_demo_led_opr;
}

底层驱动头文件实现

#ifndef	__FILE_OPERATION_H
#define __FILE_OPERATION_Hstruct led_operation{int num;int (*init)(int which);/* 初始化哪个LED */int(*ctl)(int which , char status);/* 控制哪个LED,以及控制的状态 */};struct led_operation* get_board_led_operation(void);#endif

应用层文件的实现

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h> int main(int argc, char** argv)
{char status = 0;if( argc != 3 ){printf("./ledtest /dev/myled on \n");printf("./ledtest /dev/myled off \n");return -1;}int fd;//openfd = open(argv[1] , O_RDWR);if( fd < 0 ){printf("open %s file \n",argv[1]);return -1;}//writeif( 0 == strcmp(argv[2] , "on") ){status = 1;write(fd , &status , 1);}else{status = 0;write(fd , &status , 1);}return 1;}

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

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

相关文章

代码随想录-KMP算法

LeetCode28: . - 力扣&#xff08;LeetCode&#xff09; public class KMP {public static void main(String[] args) {KMP kmpnew KMP();kmp.strStr("aabaabaaf","aabaaf");}public int strStr(String haystack, String needle) {//获取next数组int[] nex…

ENVI实战—一文搞定NDVI计算和MNDWI计算

实验1&#xff1a;使用波段计算器计算波段值 目的&#xff1a;熟练掌握ENVI中波段计算器的使用方法&#xff0c;学会波段之间的运算。 过程&#xff1a; ①数据导入&#xff1a;打开ENVI5.6&#xff0c;在“文件”选项卡中选择“打开”&#xff0c;打开此前裁剪好的Sentinel…

外包干了3天,技术退步明显.......

先说一下自己的情况&#xff0c;大专生&#xff0c;19年通过校招进入杭州某软件公司&#xff0c;干了接近4年的功能测试&#xff0c;今年年初&#xff0c;感觉自己不能够在这样下去了&#xff0c;长时间呆在一个舒适的环境会让一个人堕落! 而我已经在一个企业干了四年的功能测…

✔ ★Java项目——设计一个消息队列(二)

Java项目——设计一个消息队列 四. 项⽬创建五. 创建核⼼类创建 Exchange&#xff08;名字、类型、持久化&#xff09;创建 MSGQueue&#xff08;名字、持久化、独占标识&#xff09;创建 Binding&#xff08;交换机名字、队列名字、bindingKey用于与routingKey匹配&#xff09…

【经典算法】LeetCode25:K 个一组翻转链表(Java/C/Python3,Hard)

#算法 目录 题目描述思路及实现方式一&#xff1a;递归思路代码实现Java 版本C 语言版本Python3 版本 复杂度分析 方式二&#xff1a;迭代和原地反转思路代码实现Java 版本C 语言版本Python3 版本 复杂度分析 总结相似题目 标签&#xff1a;链表、递归 题目描述 给你链表的头…

代码随想录阅读笔记-二叉树【总结】

二叉树的理论基础 代码随想录 (programmercarl.com)&#xff1a;二叉树的种类、存储方式、遍历方式、定义方式 二叉树的遍历方式 深度优先遍历 代码随想录阅读笔记-二叉树【递归遍历】-CSDN博客&#xff1a;递归三部曲初次亮相代码随想录阅读笔记-二叉树【迭代遍历】-CSDN博…

2024年3月文章一览

2024年3月编程人总共更新了12篇文章&#xff1a; 1.2024年2月文章一览 2.Programming Abstractions in C阅读笔记&#xff1a;p308-p311 3.Programming Abstractions in C阅读笔记&#xff1a;p312-p326 4.Programming Abstractions in C阅读笔记&#xff1a;p327-p330 5.…

基于SSM的电影网站(有报告)。Javaee项目。ssm项目。

演示视频&#xff1a; 基于SSM的电影网站&#xff08;有报告&#xff09;。Javaee项目。ssm项目。 项目介绍&#xff1a; 采用M&#xff08;model&#xff09;V&#xff08;view&#xff09;C&#xff08;controller&#xff09;三层体系结构&#xff0c;通过Spring SpringMv…

Ollama教程——兼容OpenAI API:高效利用兼容OpenAI的API进行AI项目开发

相关文章: Ollama教程——入门&#xff1a;开启本地大型语言模型开发之旅 Ollama教程——模型&#xff1a;如何将模型高效导入到ollama框架 Ollama教程——兼容OpenAI API&#xff1a;高效利用兼容OpenAI的API进行AI项目开发 Ollama教程——兼容OpenAI API&#xff1a;高效利用…

L2-2 巴音布鲁克永远的土(二分+并查集)

思路&#xff1a;我们可以二分答案&#xff0c;然后判断当前答案合不合理。 对于判断答案合理&#xff0c;可以用并查集&#xff0c;看mid能否把所有检查点连进一个集合中&#xff0c;枚举每个结点&#xff0c;如何当前结点周围的四个方向可以连的话&#xff0c;就加进同一个集…

【电子通识】热风枪的结构与使用方法

热风枪的结构 热风枪是专门用来拆焊、焊接贴片元器件和贴片集成电路的焊接工具&#xff0c;它主要由主机和热风焊枪两大部分构成。 热风枪主要有电源开关、风速设置、温度设置、热风连接等部件组成。根据不同品牌和价位的热风枪&#xff0c;有一些功能齐全的也集成了烙铁功能。…

波奇学Linux:

面向数据报&#xff1a;udp没有发送缓冲区&#xff0c;发送几次数据报&#xff0c;读取几次数据报&#xff0c;write和read一一对应 tcp通信时只管识别数据&#xff0c;在应用层才对字节进行拼接分析&#xff0c;得到完整请求 简单来说&#xff1a;udp之间传递的是报文&#x…

使用LNMP部署动态网站环境

目录 实验环境 一、配置LNMP架构环境 二、验证部署的LNMP 动态网站环境是否可用 三、配置过程中遇到的问题及解决思路 实验环境 centos7 192.168.81.131/24 一、配置LNMP架构环境 概念及配置手册参考第20章 使用LNMP架构部署动态网站环境。 | 《Linux就该这么学》 安装g…

三行命令解决Ubuntu Linux联网问题

本博客中Ubuntu版本为23.10.1最新版本&#xff0c;后续发现了很多问题我无法解决&#xff0c;已经下载了另外一个版本22.04&#xff0c;此版本自带网络 一开始我找到官方文档描述可以通过命令行连接到 WiFi 网络&#xff1a;https://cn.linux-console.net/?p10334#google_vig…

漫画|数据工程师面试常见问题之数据倾斜

话说&#xff0c;闹钟一响&#xff0c;现实照进梦想&#xff0c;又是李大虎面试找工作的一天。 李大虎心里一直有个想法&#xff0c;如果一天睡20个小时&#xff0c;然后这20个小时全做美梦&#xff0c;醒来的4个小时用来吃喝拉撒&#xff0c;这样岂不就和那些富二代一样了&am…

【core analyzer】core analyzer的介绍和安装详情

目录 &#x1f31e;1. core和core analyzer的基本概念 &#x1f33c;1.1 coredump文件 &#x1f33c;1.2 core analyzer &#x1f31e;2. core analyzer的安装详细过程 &#x1f33c;2.1 方式一 简单但不推荐 &#x1f33c;2.2 方式二 推荐 &#x1f33b;2.2.1 安装遇到…

【vue】v-if 条件渲染

v-if 不适用于频繁切换显示模式的场景 修改web.user&#xff0c;可看到条件渲染的效果 <!DOCTYPE html> <html lang"en"><head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initi…

【MATLAB源码-第5期】基于matlab的BPSK的理论误码率和实际误码率对比仿真。

1、算法描述 BPSK (Binary Phase Shift Keying)-------二进制相移键控。是把模拟信号转换成数据值的转换方式之一&#xff0c;利用偏离相位的复数波浪组合来表现信息键控移相方式。BPSK使用了基准的正弦波和相位反转的波浪&#xff0c;使一方为0&#xff0c;另一方为1&#xf…

Vue3大事件项目1 登录注册验证

创建项目 引入 element-ui 组件库 登录&#xff1a;注册样式准备之后&#xff0c;配置校验规则&#xff08;4个条件&#xff1a;一数据、二规则&#xff09; 1. 校验相关 (1) 给当前表单绑上整个的数据对象&#xff1a;el-form > :model"ruleForm" 绑…

Centos7搭建 Skywalking 单机版

介绍 Skywalking是应用性能监控平台&#xff0c;可用于分布式系统&#xff0c;支持微服务、云原生、Docker、Kubernetes 等多种架构场景。 整体架构如图 Agent &#xff1a;在应用中&#xff0c;收集 Trace、Log、Metrics 等监控数据&#xff0c;使用 RPC、RESTful API、Kafk…