I.MX6ULL_Linux_驱动篇(44)linux MISC驱动

MISC 驱动也叫做杂项驱动,也就是当我们板子上的某些外设无法进行分类的时候就可以使用 MISC 驱动。 MISC 驱动其实就是最简单的字符设备驱动,通常嵌套在 platform 总线驱动中,实现复杂的驱动,本章我们就来学习一下 MISC 驱动的编写。

MISC 设备驱动简介

所有的 MISC 设备驱动的主设备号都为 10,不同的设备使用不同的从设备号。随着 Linux字符设备驱动的不断增加,设备号变得越来越紧张,尤其是主设备号, MISC 设备驱动就用于解
决此问题。 MISC 设备会自动创建 cdev,不需要像我们以前那样手动创建,因此采用 MISC 设备驱动可以简化字符设备驱动的编写。我们需要向 Linux 注册一个 miscdevice 设备, miscdevice
是一个结构体,定义在文件 include/linux/miscdevice.h 中,内容如下:

57 struct miscdevice {
58     int minor; /* 子设备号 */
59     const char *name; /* 设备名字 */
60     const struct file_operations *fops; /* 设备操作集 */
61     struct list_head list;
62     struct device *parent;
63     struct device *this_device;
64     const struct attribute_group **groups;
65     const char *nodename;
66     umode_t mode;
67 };

定义一个 MISC 设备(miscdevice 类型)以后我们需要设置 minor、 name 和 fops 这三个成员变量。 minor 表示子设备号, MISC 设备的主设备号为 10,这个是固定的,需要用户指定子设备
号, Linux 系统已经预定义了一些 MISC 设备的子设备号,这些预定义的子设备号定义在include/linux/miscdevice.h 文件中,如下所示:

13 #define PSMOUSE_MINOR 1
14 #define MS_BUSMOUSE_MINOR 2 /* unused */
15 #define ATIXL_BUSMOUSE_MINOR 3 /* unused */
16 /*#define AMIGAMOUSE_MINOR 4 FIXME OBSOLETE */
17 #define ATARIMOUSE_MINOR 5 /* unused */
18 #define SUN_MOUSE_MINOR 6 /* unused */
......
52 #define MISC_DYNAMIC_MINOR 255

我们在使用的时候可以从这些预定义的子设备号中挑选一个,当然也可以自己定义,只要这个子设备号没有被其他设备使用接口。name 就是此 MISC 设备名字,当此设备注册成功以后就会在/dev 目录下生成一个名为 name的设备文件。 fops 就是字符设备的操作集合, MISC 设备驱动最终是需要使用用户提供的 fops操作集合。当设置好 miscdevice 以后就需要使用 misc_register 函数向系统中注册一个 MISC 设备,此函数原型如下:

int misc_register(struct miscdevice * misc)

函数参数和返回值含义如下:
misc:要注册的 MISC 设备。
返回值: 负数,失败; 0,成功。

以前我们需要自己调用一堆的函数去创建设备,比如在以前的字符设备驱动中我们会使用
如下几个函数完成设备创建过程:

1 alloc_chrdev_region(); /* 申请设备号 */
2 cdev_init(); /* 初始化 cdev */
3 cdev_add(); /* 添加 cdev */
4 class_create(); /* 创建类 */
5 device_create(); /* 创建设备 */

现在我们可以直接使用 misc_register 一个函数来完成示上述代码中的这些步骤。当我们卸载设备驱动模块的时候需要调用 misc_deregister 函数来注销掉 MISC 设备,函数原型如下:

int misc_deregister(struct miscdevice *misc)

函数参数和返回值含义如下:
misc:要注销的 MISC 设备。
返回值: 负数,失败; 0,成功。
以前注销设备驱动的时候,我们需要调用一堆的函数去删除此前创建的 cdev、设备等等内容,如下所示:

1 cdev_del(); /* 删除 cdev */
2 unregister_chrdev_region(); /* 注销设备号 */
3 device_destroy(); /* 删除设备 */
4 class_destroy(); /* 删除类 */

现在我们只需要一个 misc_deregister 函数即可完成上述代码中的这些工作。关于MISC 设备驱动就讲解到这里,接下来我们就使用 platform 加 MISC 驱动框架来编写 beep 蜂鸣器驱动。

实验

驱动代码

#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/delay.h>
#include <linux/ide.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/errno.h>
#include <linux/gpio.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/of_gpio.h>
#include <linux/platform_device.h>
#include <linux/miscdevice.h>
#include <asm/mach/map.h>
#include <asm/uaccess.h>
#include <asm/io.h>#define MISCBEEP_NAME		"miscbeep"	/* 名字 	*/
#define MISCBEEP_MINOR		144			/* 子设备号 */
#define BEEPOFF 			0			/* 关蜂鸣器 */
#define BEEPON 				1			/* 开蜂鸣器 *//* miscbeep设备结构体 */
struct miscbeep_dev{dev_t devid;			/* 设备号 	 */struct cdev cdev;		/* cdev 	*/struct class *class;	/* 类 		*/struct device *device;	/* 设备 	 */struct device_node	*nd; /* 设备节点 */int beep_gpio;			/* beep所使用的GPIO编号		*/
};struct miscbeep_dev miscbeep;		/* beep设备 *//** @description		: 打开设备* @param - inode 	: 传递给驱动的inode* @param - filp 	: 设备文件,file结构体有个叫做private_data的成员变量* 					  一般在open的时候将private_data指向设备结构体。* @return 			: 0 成功;其他 失败*/
static int miscbeep_open(struct inode *inode, struct file *filp)
{filp->private_data = &miscbeep; /* 设置私有数据 */return 0;
}/** @description		: 向设备写数据 * @param - filp 	: 设备文件,表示打开的文件描述符* @param - buf 	: 要写给设备写入的数据* @param - cnt 	: 要写入的数据长度* @param - offt 	: 相对于文件首地址的偏移* @return 			: 写入的字节数,如果为负值,表示写入失败*/
static ssize_t miscbeep_write(struct file *filp, const char __user *buf, size_t cnt, loff_t *offt)
{int retvalue;unsigned char databuf[1];unsigned char beepstat;struct miscbeep_dev *dev = filp->private_data;retvalue = copy_from_user(databuf, buf, cnt);if(retvalue < 0) {printk("kernel write failed!\r\n");return -EFAULT;}beepstat = databuf[0];		/* 获取状态值 */if(beepstat == BEEPON) {	gpio_set_value(dev->beep_gpio, 0);	/* 打开蜂鸣器 */} else if(beepstat == BEEPOFF) {gpio_set_value(dev->beep_gpio, 1);	/* 关闭蜂鸣器 */}return 0;
}/* 设备操作函数 */
static struct file_operations miscbeep_fops = {.owner = THIS_MODULE,.open = miscbeep_open,.write = miscbeep_write,
};/* MISC设备结构体 */
static struct miscdevice beep_miscdev = {.minor = MISCBEEP_MINOR,.name = MISCBEEP_NAME,.fops = &miscbeep_fops,
};/** @description     : flatform驱动的probe函数,当驱动与*                    设备匹配以后此函数就会执行* @param - dev     : platform设备* @return          : 0,成功;其他负值,失败*/
static int miscbeep_probe(struct platform_device *dev)
{int ret = 0;printk("beep driver and device was matched!\r\n");/* 设置BEEP所使用的GPIO *//* 1、获取设备节点:beep */miscbeep.nd = of_find_node_by_path("/beep");if(miscbeep.nd == NULL) {printk("beep node not find!\r\n");return -EINVAL;} /* 2、 获取设备树中的gpio属性,得到BEEP所使用的BEEP编号 */miscbeep.beep_gpio = of_get_named_gpio(miscbeep.nd, "beep-gpio", 0);if(miscbeep.beep_gpio < 0) {printk("can't get beep-gpio");return -EINVAL;}/* 3、设置GPIO5_IO01为输出,并且输出高电平,默认关闭BEEP */ret = gpio_direction_output(miscbeep.beep_gpio, 1);if(ret < 0) {printk("can't set gpio!\r\n");}/* 一般情况下会注册对应的字符设备,但是这里我们使用MISC设备* 所以我们不需要自己注册字符设备驱动,只需要注册misc设备驱动即可*/ret = misc_register(&beep_miscdev);if(ret < 0){printk("misc device register failed!\r\n");return -EFAULT;}return 0;
}/** @description     : platform驱动的remove函数,移除platform驱动的时候此函数会执行* @param - dev     : platform设备* @return          : 0,成功;其他负值,失败*/
static int miscbeep_remove(struct platform_device *dev)
{/* 注销设备的时候关闭LED灯 */gpio_set_value(miscbeep.beep_gpio, 1);/* 注销misc设备 */misc_deregister(&beep_miscdev);return 0;
}/* 匹配列表 */static const struct of_device_id beep_of_match[] = {{ .compatible = "atkalpha-beep" },{ /* Sentinel */ }};/* platform驱动结构体 */
static struct platform_driver beep_driver = {.driver     = {.name   = "imx6ul-beep",         /* 驱动名字,用于和设备匹配 */.of_match_table = beep_of_match, /* 设备树匹配表          */},.probe      = miscbeep_probe,.remove     = miscbeep_remove,
};/** @description	: 驱动出口函数* @param 		: 无* @return 		: 无*/
static int __init miscbeep_init(void)
{return platform_driver_register(&beep_driver);
}/** @description	: 驱动出口函数* @param 		: 无* @return 		: 无*/
static void __exit miscbeep_exit(void)
{platform_driver_unregister(&beep_driver);
}module_init(miscbeep_init);
module_exit(miscbeep_exit);
MODULE_LICENSE("GPL");

应用代码

#include "stdio.h"
#include "unistd.h"
#include "sys/types.h"
#include "sys/stat.h"
#include "fcntl.h"
#include "stdlib.h"
#include "string.h"#define BEEPOFF	0
#define BEEPON 	1/** @description		: main主程序* @param - argc 	: argv数组元素个数* @param - argv 	: 具体参数* @return 			: 0 成功;其他 失败*/
int main(int argc, char *argv[])
{int fd, retvalue;char *filename;unsigned char databuf[1];if(argc != 3){printf("Error Usage!\r\n");return -1;}filename = argv[1];fd = open(filename, O_RDWR);	/* 打开beep驱动 */if(fd < 0){printf("file %s open failed!\r\n", argv[1]);return -1;}databuf[0] = atoi(argv[2]);	/* 要执行的操作:打开或关闭 */retvalue = write(fd, databuf, sizeof(databuf));if(retvalue < 0){printf("BEEP Control Failed!\r\n");close(fd);return -1;}retvalue = close(fd); /* 关闭文件 */if(retvalue < 0){printf("file %s close failed!\r\n", argv[1]);return -1;}return 0;
}

测试

将上一小节编译出来 miscbeep.ko 和 miscbeepApp 这两个文件拷贝到rootfs/lib/modules/4.1.15 目录中,重启开发板,进入到目录 lib/modules/4.1.15 中,输入如下命令加载 miscbeep.ko 这个驱动模块。

depmod //第一次加载驱动的时候需要运行此命令
modprobe miscbeep.ko //加载设备模块

当驱动模块加载成功以后我们可以在/sys/class/misc 这个目录下看到一个名为“miscbeep”的子目录,如图所示:

 所有的 misc 设备都属于同一个类, /sys/class/misc 目录下就是 misc 这个类的所有设备,每个设备对应一个子目录。驱动与设备匹配成功以后就会生成/dev/miscbeep 这个设备驱动文件,输入如下命令查看这个文件的主次设备号:

ls /dev/miscbeep -l

从图中可以看出, /dev/miscbeep 这个设备的主设备号为 10,次设备号为 144,和我们驱动程序里面设置的一致。输入如下命令打开 BEEP:

./miscbeepApp /dev/miscbeep 1 //打开 BEEP

在输入如下命令关闭 beep:

./miscbeepApp /dev/miscbeep 0 //关闭 BEEP

观察一下 BEEP 能否打开和关闭,如果可以的话就说明驱动工作正常,如果要卸载驱动的话输入如下命令即可:

rmmod miscbeep.ko

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

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

相关文章

Homer:一个简单的静态主页

什么是 Homer ? Homer 是一个完全静态的 html/js 仪表板&#xff0c;基于一个简单的 yaml 配置文件。它旨在由 HTTP 服务器提供服务&#xff0c;如果您直接通过 file:// 协议打开 index.html&#xff0c;它将无法工作。 安装 在群晖上以 Docker 方式安装。 在注册表中搜索 h…

蓝桥杯上岸每日N题 第八期 (全球变暖)!!!

蓝桥杯上岸每日N题第八期(全球变暖)&#xff01;&#xff01;&#xff01; 同步收录 &#x1f447; 蓝桥杯上岸必背&#xff01;&#xff01;&#xff01;(第五期BFS) 大家好 我是寸铁&#x1f4aa; 冲刺蓝桥杯省一模板大全来啦 &#x1f525; 蓝桥杯4月8号就要开始了 &am…

原型链污染攻击

原型链污染攻击 prototype 和 _proto_是什么 JavaScript中的类的简历 在JavaScript中&#xff0c;我们如果要定义一个类&#xff0c;需要以定义“构造函数”的方式来定义&#xff1a; function Foo() {this.bar 1 }new Foo() 解析&#xff1a; Foo函数的内容&#xff0c;就…

2023年华数杯数学建模A题思路代码分析 - 隔热材料的结构优化控制研究

# 1 赛题 A 题 隔热材料的结构优化控制研究 新型隔热材料 A 具有优良的隔热特性&#xff0c;在航天、军工、石化、建筑、交通等 高科技领域中有着广泛的应用。 目前&#xff0c;由单根隔热材料 A 纤维编织成的织物&#xff0c;其热导率可以直接测出&#xff1b;但是 单根隔热…

山西电力市场日前价格预测【2023-08-06】

日前价格预测 预测明日&#xff08;2023-08-06&#xff09;山西电力市场全天平均日前电价为411.77元/MWh。其中&#xff0c;最高日前电价为457.52元/MWh&#xff0c;预计出现在19: 30。最低日前电价为370.37元/MWh&#xff0c;预计出现在13: 15。 价差方向预测 1&#xff1a; 实…

机器学习---概述(一)

文章目录 1.人工智能、机器学习、深度学习2.机器学习的工作流程2.1 获取数据集2.2 数据基本处理2.3 特征工程2.3.1 特征提取2.3.2 特征预处理2.3.3 特征降维 2.4 机器学习2.5 模型评估 3.机器学习的算法分类3.1 监督学习3.1.1 回归问题3.1.2 分类问题 3.2 无监督学习3.3 半监督…

前端(十一)——Vue vs. React:两大前端框架的深度对比与分析

&#x1f60a;博主&#xff1a;小猫娃来啦 &#x1f60a;文章核心&#xff1a;Vue vs. React&#xff1a;两大前端框架的深度对比与分析 文章目录 前言概述原理与设计思想算法生态系统与社区支持API与语法性能与优化开发体验与工程化对比总结结语 前言 在当今快速发展的前端领…

软件设计原则

文章目录 一、软件设计原则1. 开闭原则2. 里氏代换原则3. 依赖倒转原则4. 接口隔离原则5. 迪米特法则6. 合成复用原则 一、软件设计原则 在软件开发中&#xff0c;为了提高软件系统的可维护性和可复用性&#xff0c;增加软件的可扩展性和灵活性&#xff0c;程序员要尽量根据软件…

django使用mysql数据库

Django开 发操作数据库比使用pymysql操作更简单&#xff0c;内部提供了ORM框架。 下面是pymysql 和orm操作数据库的示意图&#xff0c;pymysql就是mysql的驱动&#xff0c;代码直接操作pymysql ,需要自己写增删改查的语句 django 就是也可以使用pymysql、mysqlclient作为驱动&a…

迁移学习:使用Restnet预训练模型构建高效的水果识别模型

目录 引言 1 迁移学习 1.1 什么是迁移学习 1.2 迁移学习能解决什么问题 1.3 迁移学习面临的三个问题 1.3.1 何时迁移 1.3.2 何处迁移 1.3.3 如何迁移 1.4 迁移学习的分类 1.4.1 按照学习方式的划分 1.4.2 按照使用方法的划分 2 Restnet网络 2.1 Restnet介绍 2.2 Re…

element-ui树形表格,左边勾选,右边显示选中的数据-功能(如动图)

功能如图 功能需求 表格树形表格勾选数据&#xff0c;右边显示对应勾选的数据内容&#xff0c;选中客户&#xff0c;自动勾选所有的店铺(子级)&#xff0c;选中其中一个店铺&#xff0c;自动勾选上客户(父级)&#xff0c;同时会存在只有客户&#xff08;下面没有子级的情况&am…

Apache Flink概述

Flink 是构建在数据流之上的一款有状态的流计算框架&#xff0c;通常被人们称为第三代大数据分析方案 第一代大数据处理方案&#xff1a;基于Hadoop的MapReduce 静态批处理 | Storm 实时流计算 &#xff0c;两套独立的计算引擎&#xff0c;难度大&#xff08;2014年9月&#x…

Java版工程行业管理系统源码-专业的工程管理软件-em提供一站式服务 em

​ Java版工程项目管理系统 Spring CloudSpring BootMybatisVueElementUI前后端分离 功能清单如下&#xff1a; 首页 工作台&#xff1a;待办工作、消息通知、预警信息&#xff0c;点击可进入相应的列表 项目进度图表&#xff1a;选择&#xff08;总体或单个&#xff09;项目…

STM32存储左右互搏 I2C总线读写EEPROM ZD24C1MA

STM32存储左右互搏 I2C总线读写EEPROM ZD24C1MA 在较低容量存储领域&#xff0c;EEPROM是常用的存储介质&#xff0c;不同容量的EEPROM的地址对应位数不同&#xff0c;在发送字节的格式上有所区别。EEPROM是非快速访问存储&#xff0c;因为EEPROM按页进行组织&#xff0c;在连…

caj文件怎么转换成pdf?了解一下这种方法

caj文件怎么转换成pdf&#xff1f;如果你曾经遇到过需要将CAJ文件转换成PDF格式的情况&#xff0c;那么你一定知道这是一件麻烦的事情。幸运的是&#xff0c;现在有许多软件和工具可以帮助你完成这项任务。下面就给大家介绍一款使用工具。 【迅捷PDF转换器】是一款功能强大的工…

启动RocketMQ报错

说明&#xff1a;启动RocketMQ消费者时&#xff0c;报以下错误&#xff1a;java.lang.IllegalStateException&#xff1a;Failed to start RocketMQ push consumer. 解决&#xff1a;看下所有的监听器类&#xff0c;检查是不是有相同的消费者组名&#xff0c;注释掉其中一个即可…

BI技巧丨利用OFFSET计算同环比

微软最近更新了很多开窗函数&#xff0c;其内部参数对比以往的DAX函数来说&#xff0c;多了很多&#xff0c;这就导致学习的时间成本直线上升。 而且对于新增函数的应用场景&#xff0c;很多小伙伴也是一知半解的&#xff0c;本期我们就来聊一聊关于最近新增的开窗函数——OFF…

Docker网络模型使用详解(2)Docker网络模式

安装Docker时会自动创建3个网络&#xff0c;可以使用docker network ls命令列出这些网络。 [rootlocalhost ~]# docker network ls NETWORK ID NAME DRIVER SCOPE ebcfad6f4255 bridge bridge local b881c67f8813 compose_lnmp_lnmp…

介绍Sping Boot的5个扩展点

1、初始化器ApplicationContextInitializer 我们在启动Spring Boot项目的时候&#xff0c;是执行这样一个方法来启动的 我们一层一层往下点&#xff0c;最终发现执行的是这个方法 所以我们在启动项目的时候也可以这样启动 new SpringApplication(SpringbootExtensionPointAp…

无脑入门pytorch系列(二)—— torch.mean

本系列教程适用于没有任何pytorch的同学&#xff08;简单的python语法还是要的&#xff09;&#xff0c;从代码的表层出发挖掘代码的深层含义&#xff0c;理解具体的意思和内涵。pytorch的很多函数看着非常简单&#xff0c;但是其中包含了很多内容&#xff0c;不了解其中的意思…