Linux学习笔记(九)MISC设备驱动

前言

        misc 的意思是混合、杂项的,因此 MISC 驱动也叫做杂项驱动。也就是当我们板子上的某些外设无法进行分类的时候就可以使用 MISC 驱动。 MISC 驱动其实就是最简单的字符设备驱动,通常嵌套在 platform 总线驱动中,实现复杂的驱动,接下来就来讲下一下MISC的使用,其实总结就是一句话,用MISC 设备驱动来简化字符设备驱动的编写,也就是替代我们之前注册字符设备的那一堆操作


一.MISC设备驱动简介

        所有的 MISC 设备驱动的主设备号都为 10,不同的设备使用不同的从设备号。随着 Linux字符设备驱动的不断增加,设备号变得越来越紧张,尤其是主设备号, MISC 设备驱动就用于解决此问题。

         MISC 设备会自动创建 cdev,不需要像我们以前那样手动创建,因此采用 MISC 设备驱动可以简化字符设备驱动的编写

1.miscdevice 设备结构体:

同样的,我们要使用MISC,就需要向 Linux 注册一个 miscdevice 设备, miscdevice是一个结构体,定义在文件 include/linux/miscdevice.h 中,内容如下:

struct miscdevice  {int minor;const char *name;const struct file_operations *fops;struct list_head list;struct device *parent;struct device *this_device;const struct attribute_group **groups;const char *nodename;umode_t mode;
};

    这个结构体需要我们填入的参数有:minor、 name 和 fops 这三个成员变量。

<1>minor:

        minor 表示子设备号, MISC 设备的主设备号为 10,这个是固定的,需要用户指定子设备号, Linux 系统已经预定义了一些 MISC 设备的子设备号,这些预定义的子设备号定义在include/linux/miscdevice.h 文件中,如下所示:

#define PSMOUSE_MINOR       1
#define MS_BUSMOUSE_MINOR   2   /* unused */
#define ATIXL_BUSMOUSE_MINOR    3   /* unused */
/*#define AMIGAMOUSE_MINOR  4   FIXME OBSOLETE */
#define ATARIMOUSE_MINOR    5   /* unused */
#define SUN_MOUSE_MINOR     6   /* unused */
#define APOLLO_MOUSE_MINOR  7   /* unused */
#define PC110PAD_MINOR      9   /* unused */
......
#define VHOST_VSOCK_MINOR   241
#define RFKILL_MINOR        242
#define MISC_DYNAMIC_MINOR  255

我们在使用的时候可以从这些预定义的子设备号中挑选一个,当然也可以自己定义,只要这个子设备号没有被其他设备使用接口。

<2>name :

        name 就是此 MISC 设备名字,当此设备注册成功以后就会在/dev 目录下生成一个名为 name的设备文件。

<3>fops :

fops 是字符设备的操作函数集合, MISC 设备驱动最终是需要使用用户提供的 fops操作集合。也就是之前我们自己手动注册字符设备的操作函数:

/* 设备操作函数结构体 */
static const struct file_operations gpioled_fops = {.owner		= THIS_MODULE,.open		= gpioled_open,.read		= gpioled_read,.write      = gpioled_write,.release    = gpioled_release
};

2.miscdevice 注册函数:

函数原型如下:

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

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

 /* 传统的创建设备过程 */
alloc_chrdev_region();    /* 申请设备号 */
cdev_init();              /* 初始化 cdev */
cdev_add();               /* 添加 cdev */
class_create();           /* 创建类 */
device_create();          /* 创建设备 */

 现在我们可以直接使用 misc_register 一个函数来完成上面的传统的创建设备过程中的这些步骤。如下所示:

   ret = misc_register(&led_miscdev);if(ret < 0){printk("misc device register failed!\r\n");return -EFAULT;}

3.misc_deregister卸载函数:

当我们卸载设备驱动模块的时候需要调用 misc_deregister 函数来注销掉 MISC 设备,函数原型如下:

int misc_deregister(struct miscdevice *misc)函数参数和返回值含义如下:
misc:要注销的 MISC 设备。
返回值: 负数,失败; 0,成功。

以前注销设备驱动的时候,我们需要调用一堆的函数去删除此前创建的 cdev、设备等等内容,如下所示:

/* 传统的删除设备的过程 */
cdev_del();                 /* 删除 cdev */
unregister_chrdev_region(); /* 注销设备号 */
device_destroy();           /* 删除设备 */
class_destroy();            /* 删除类 */

 现在我们只需要一个 misc_deregister 函数即可完成传统的删除设备的过程中的这些工作。关于MISC 设备驱动就讲解到这里,接下来我们就使用 platform 加 MISC 驱动框架来编写 led驱动。如下所示:

misc_deregister(&led_miscdev);

二、设备树的修改:

在设备树下的根节点添加以下结点:

	gpioled{#address-cells = <1>;#size-cells    = <1>;compatible = "led-gpio";pinctrl-names = "default";pinctrl-0 = <&pinctrl_led>;gpios = <&gpio1 3 GPIO_ACTIVE_LOW>;state = "okay";};

在iomux节点下添加;

		pinctrl_led:ledgrp{fsl,pin=</* 配置 GPIO1_IO03 的 IO 属性 *bit 16:0 HYS 关闭 *bit [15:14]: 00 默认下拉 *bit [13]: 0 kepper 功能 *bit [12]: 1 pull/keeper 使能 *bit [11]: 0 关闭开路输出 *bit [7:6]: 10 速度 100Mhz *bit [5:3]: 110 R0/6 驱动能力 *bit [0]: 0 低转换率 */MX6UL_PAD_GPIO1_IO03__GPIO1_IO03 0x10B0>;

三、驱动代码的编写

1.引入框架

        框架就使用我们之前的platfrom-led的框架不过在peobe函数里面,我们要把关于那些字符设备注册初始化的部分删除了:

static int led_probe(struct platform_device *dev)
{myled_init(&gpioled);return 0;
}

 剩下gpio初始化部分就可以了。

2.创建miscdevice结构体

#define DEVICE_NAME         "miscled"
#define DEVICE_MINOR		145			/* 子设备号 *//* MISC设备结构体 */
static struct miscdevice led_miscdev = {.minor = DEVICE_MINOR,.name = DEVICE_NAME,.fops = &gpioled_fops,
};

3.使用.miscdevice 注册函数

/*当谁列表的设备和驱动匹配上后执行的peobe函数*/
static int led_probe(struct platform_device *dev)
{int ret = 0;myled_init(&gpioled);ret = misc_register(&led_miscdev);if(ret < 0){printk("misc device register failed!\r\n");return -EFAULT;}return 0;
}

4.misc_deregister卸载函数

static int led_remove(struct platform_device *dev)
{gpio_set_value(gpioled.led_gpio,1);gpio_free(gpioled.led_gpio);misc_deregister(&led_miscdev);printk("gpioled exit!\r\n");return 0;
}

完整代码:

/**************头文件区域*********************************************************/
#include <linux/ide.h>
#include <linux/fs.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/cdev.h>
#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/errno.h>#include <linux/of.h> 
#include <linux/of_address.h> 
#include <linux/of_gpio.h> 
#include <linux/irq.h>
#include <linux/poll.h>
#include <linux/platform_device.h>
#include <linux/fcntl.h>
#include <linux/miscdevice.h>#include <asm/mach/map.h>
#include <asm/uaccess.h>
#include <linux/io.h>
/**********************************************************************************//************************函数定义-begin***********************************************/
static int gpioled_release(struct inode *inode, struct file *file);
static ssize_t gpioled_read(struct file *file, char __user *buf, size_t size, loff_t *ptr);
static ssize_t gpioled_write(struct file *file, const char __user *buf, size_t size, loff_t *ptr);
static int gpioled_open(struct inode *inode , struct file *file);
static int led_probe(struct platform_device *dev);
static int led_remove(struct platform_device *dev);
/************************函数定义-end********************************************//************************宏定义-begin***********************************************/
#define DEVICE_NAME         "miscled"
#define DEVICE_MINOR		145			/* 子设备号 */
#define DEVICE_CNT          1
#define BEEP_ON              1
#define BEEP_OFF             0
/************************宏定义-end********************************************//************************结构体定义-begin***********************************************/
/* dtsled设备信息结构体 */
struct dts_dev
{dev_t devid;			/* 设备号 	 */struct cdev cdev;		/* cdev 	*/struct class *class;	/* 类 		*/struct device *device;	/* 设备 	 */struct device_node *nd; /* 设备节点 */int led_gpio; /* led 所使用的 GPIO 编号 */
};
struct dts_dev gpioled; /* led设备 *//* 设备操作函数结构体 */
static const struct file_operations gpioled_fops = {.owner		= THIS_MODULE,.open		= gpioled_open,.read		= gpioled_read,.write      = gpioled_write,.release    = gpioled_release
};/* MISC设备结构体 */
static struct miscdevice led_miscdev = {.minor = DEVICE_MINOR,.name = DEVICE_NAME,.fops = &gpioled_fops,
};/* 匹配列表 */
static const struct of_device_id led_of_match[] = {{ .compatible = "led-gpio" },{ /* Sentinel */ }
};static struct platform_driver led_driver = {.driver = {.name = "imx6ul-led",.of_match_table = led_of_match,},.probe  =  led_probe,.remove =  led_remove,
};
/************************结构体定义-end***********************************************//************************file_operations操作函数-begin***********************************************/
static int gpioled_release(struct inode *inode, struct file *file)
{printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);return 0;
}
static ssize_t gpioled_read(struct file *file, char __user *buf, size_t size, loff_t *ptr)
{printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);return 0;
}
static ssize_t gpioled_write(struct file *file, const char __user *buf, size_t size, loff_t *ptr)
{int ret;unsigned char databuf[1];unsigned char ledstate;struct dts_dev *dev = file->private_data;ret = __copy_from_user(databuf,buf,size);if(ret < 0){printk("kernel write failed!\r\n");return -EFAULT;}ledstate = databuf[0];if(ledstate == BEEP_OFF){   gpio_set_value(dev->led_gpio,1);}else if(ledstate == BEEP_ON){gpio_set_value(dev->led_gpio,0);}return 0;
}
static int gpioled_open(struct inode *inode , struct file *file)
{printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);file->private_data = &gpioled; /* 设置私有数据 */ return 0;
}
/************************file_operations操作函数-end***********************************************//*****************led初始化函数************************/
static int myled_init(struct dts_dev *dev)
{int ret = 0;/* 1、设置 led 所使用的 GPIO */ dev->nd = of_find_node_by_path("/gpioled");if(dev->nd == NULL){printk("gpioled node cant not found!\r\n");return -EINVAL;}else{printk("gpioled node hase been found!\r\n");}/* 2、 获取设备树中的 gpio 属性,得到 led 所使用的 led 编号 */ dev->led_gpio =  of_get_named_gpio(dev->nd,"gpios",0);if(dev->led_gpio < 0){printk("can't get led-gpio\r\n");return -EINVAL;}printk("led-gpio num = %d\r\n", dev->led_gpio); /* 3、设置 GPIO1_IO03 为输出,并且输出高电平,默认关闭 led 灯 */ ret = gpio_request(dev->led_gpio,"led");if(ret < 0){printk("led-gpio request fail\r\n"); return -EINVAL;}ret = gpio_direction_output(dev->led_gpio,1);if(ret < 0) {printk("can't set gpio!\r\n");}return ret;
}/************************platfrom操作函数-begin***********************************************/
/*当谁列表的设备和驱动匹配上后执行的peobe函数*/
static int led_probe(struct platform_device *dev)
{int ret = 0;myled_init(&gpioled);ret = misc_register(&led_miscdev);if(ret < 0){printk("misc device register failed!\r\n");return -EFAULT;}return 0;
}
static int led_remove(struct platform_device *dev)
{gpio_set_value(gpioled.led_gpio,1);gpio_free(gpioled.led_gpio);misc_deregister(&led_miscdev);printk("gpioled exit!\r\n");return 0;
}
/************************platfrom操作函数-endn***********************************************/static int __init gpioled_init(void)
{return platform_driver_register(&led_driver);
}static void __exit gpioled_exit(void)
{platform_driver_unregister(&led_driver);
}module_init(gpioled_init);
module_exit(gpioled_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("oudafa");

四、编写测试 APP

这里的测试APP和之前得没什么区别,不用改:

#include "stdio.h"
#include "unistd.h"
#include "sys/types.h"
#include "sys/stat.h"
#include "fcntl.h"
#include "stdlib.h"
#include "string.h"/** @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];/* 打开led驱动 */fd = open(filename, O_RDWR);if(fd < 0){printf("file %s open failed!\r\n", argv[1]);return -1;}databuf[0] = atoi(argv[2]);	/* 要执行的操作:打开或关闭 *//* 向/dev/led文件写入数据 */retvalue = write(fd, databuf, sizeof(databuf));if(retvalue < 0){printf("LED 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;
}

五、运行测试

1.编写makefile

并且使用make命令得到.ko文件和APP文件:

KERN_DIR = /home/odf/linux-imx/linux-imxall:clearmake -C $(KERN_DIR) M=`pwd` modules $(CROSS_COMPILE)gcc -o miscledApp miscledApp.c clean:clearmake -C $(KERN_DIR) M=`pwd` modules cleanrm -rf modules.orderrm -f miscledAppobj-m += miscled.o 

2.加载模块

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

insmod miscled.ko 

所有的 misc 设备都属于同一个类, /sys/class/misc 目录下就是 misc 这个类的所有设备,每个设备对应一个子目录。

        驱动与设备匹配成功以后就会生成/dev/miscled这个设备驱动文件,输入如下命令查看这个文件的主次设备号:

ls -l /sys/class/misc 

结果如下所示: 

从上面可以看出, /dev/misc_beep 这个设备的主设备号为 10,次设备号为 144,和我们驱动程序里面设置的一致。

3.测试代码

输入如下命令打开 led:

./misc_beep_app /dev/miscled  1

输入如下命令关闭 led:

./misc_beep_app /dev/miscled  0

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

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

相关文章

Mysql 索引概念回顾

一、什么是索引 在关系数据库中&#xff0c;索引是一种单独的、物理的对数据库表中一列或多列的值进行排序的一种存储结构&#xff0c;它是某个表中一列或若干列值的集合和相应的指向表中物理标识这些值的数据页的逻辑指针清单。索引的作用相当于图书的目录&#xff0c;可以根据…

《算法竞赛进阶指南》------图论篇

文章目录 0x01 Telephone Lines POJ - 36620x02 P1073 [NOIP2009 提高组] 最优贸易0x03 道路和航线 BZOJ22000x04 Sorting It All Out POJ - 1094 topo0x05 Sightseeing trip POJ - 1734 最小环问题0x06 Cow Relays POJ - 3613 S到E经过k条边的最短路0x07 走廊泼水节 &#xff…

为什么Java程序员需要掌握多线程?揭秘并发编程的奥秘

为什么Java程序员需要掌握多线程&#xff1f;揭秘并发编程的奥秘 个人简介前言多线程对于Java的意义&#x1f4cc;1.提高程序性能&#xff1a;&#x1f4cc;2 提高用户体验&#xff1a;&#x1f4cc;3支持并发处理&#xff1a;&#x1f4cc;4 资源共享和同步&#xff1a;&#…

C# 语法笔记

1.ref、out&#xff1a;参数传递的两种方式 ref&#xff1a;引用传递 using System; namespace CalculatorApplication {class NumberManipulator{public void swap(ref int x, ref int y){int temp;temp x; /* 保存 x 的值 */x y; /* 把 y 赋值给 x */y temp; /* 把 t…

Python中的range()函数详解:掌握迭代的利器

更多资料获取 &#x1f4da; 个人网站&#xff1a;ipengtao.com Python中的range()函数是一个强大的工具&#xff0c;用于生成一系列的数字&#xff0c;常用于循环操作。虽然看似简单&#xff0c;但其灵活性和功能却不容小觑。在本文中&#xff0c;将深入研究range()函数&…

docker容器_自定义上传jenkins镜像(Dockerfile实现)

1.创建jenkins目录&#xff0c;并上传相应的包 mkdir /jenkins/ 2.创建一个Dockerfile文件 FROM daocloud.io/library/centos:7#把当前目录下的jenkins.war包传到内部容器的/ 下 ADD ./jenkins.war /#把当前目录下的jdk传到内部容器的/opt/,并解压 ADD ./jdk-11.0.19_linu…

网络编程基础api

1. IP 协议 1.1 IP 分片 &#xff08;1&#xff09;IP 分片和重组主要依靠 IP 头部三个字段&#xff1a;数据报标识、标志和片偏移 以太网帧的 MTU 是 1500 字节&#xff1b; 一个每个分片都有自己的 IP 头部&#xff0c;它们都具有相同的标识值&#xff0c;有不同的片偏移…

css 十字分割线(含四等分布局)

核心技术 伪类选择器含义li:nth-child(2)第2个 lili:nth-child(n)所有的lili:nth-child(2n)所有的第偶数个 lili:nth-child(2n1)所有的第奇数个 lili:nth-child(-n5)前5个 lili:nth-last-child(-n5)最后5个 lili:nth-child(7n)选中7的倍数 border-right: 3px solid white;borde…

EasyExcel-最简单的读写excel工具类

前言&#xff1a; easyExcel 的官网文档给的示例非常全&#xff0c;可以参考https://easyexcel.opensource.alibaba.com/docs/current/quickstart/read 在此我贴出自己的工具类&#xff0c;可以直接用 导包 <dependency><groupId>com.alibaba</groupId><…

机器学习第15天:GBDT模型

☁️主页 Nowl &#x1f525;专栏《机器学习实战》 《机器学习》 &#x1f4d1;君子坐而论道&#xff0c;少年起而行之 ​​ 文章目录 GBDT模型介绍 Boosting 残差 GBDT的缺点 python代码实现 代码 模型参数解释 结语 GBDT模型介绍 GBDT&#xff08;Gradient Boos…

php+mysql期末作业小项目

目录 1、登录界面 2、注册界面 3、主界面 4、学生表界面 5 、查询学生界面​编辑 6、修改学生信息界面​编辑 7、删除学生信息界面 8、添加学生信息界面 9、后台数据库​编辑 一个简单的php➕mysql项目学生信息管理系统&#xff0c;用于广大学子完成期末作业的参考&…

测试架构工程师需要具备哪些能力 ?

前言 相比于我们常见的研发架构师&#xff0c;测试架构师是近几年才出现的一个岗位&#xff0c;当然岗位title其实没有特殊的含义&#xff0c;在我看来测试架构师其实更像对某一类人的抽象称呼和对其复合能力的期待及认可。 在聊这篇文章的主题之前&#xff0c;先来看这样一个…

TCP通信

第二十一章 网络通信 本章节主要讲解的是TCP和UDP两种通信方式它们都有着自己的优点和缺点 这两种通讯方式不通的地方就是TCP是一对一通信 UDP是一对多的通信方式 接下来会一一讲解 TCP通信 TCP通信方式呢 主要的通讯方式是一对一的通讯方式&#xff0c;也有着优点和缺点…

如何在Android平板上远程连接Ubuntu服务器使用code-server代码开发

目录 1.ubuntu本地安装code-server 2. 安装cpolar内网穿透 3. 创建隧道映射本地端口 4. 安卓平板测试访问 5.固定域名公网地址 6.结语 1.ubuntu本地安装code-server 准备一台虚拟机,Ubuntu或者centos都可以&#xff0c;这里以VMwhere ubuntu系统为例 下载code server服务…

el-table 表格多选(后端接口搜索分页)实现已选中的记忆功能。实现表格数据和已选数据(前端分页)动态同步更新。

实现效果&#xff1a;&#xff08;可拉代码下来看&#xff1a;vue-demo: vueDemo&#xff09; 左侧表格为点击查询调用接口查询出来的数据&#xff0c;右侧表格为左侧表格所有选择的数据&#xff0c;由前端实现分页。 两个el-table勾选数据联动更新 实现逻辑&#xff1a; el-…

低代码开发到底是补品还是垃圾食品?

2023&#xff0c;低代码彻底火了&#xff0c;甚至火到没有点相关经验&#xff0c;都不好意思出去面试的程度。 从业者对低代码的发展充满了想象&#xff0c;都认为未来低代码的商业价值不可估量。 据Gartner的最新报告显示&#xff0c;2023年全球低代码开发技术市场规模预计将…

内部文件上传以及渲染-接口API

文件上传 地址http://172.16.0.118:8090/api/pm/base/affix/upload请求类型POSTContent-Type:text/plain;charsetutf-8参数 prjData {"prjId":"", "jobId":"3031b2c8-c809-4110-8e88-22c80a9c1ec0721aca89-96a1-4346-9b6e-022331d221d1Nec…

【EMNLP 2023】面向Stable Diffusion的自动Prompt工程算法BeautifulPrompt

近日&#xff0c;阿里云人工智能平台PAI与华南理工大学朱金辉教授团队合作在自然语言处理顶级会议EMNLP2023上发表了BeautifulPrompt的深度生成模型&#xff0c;可以从简单的图片描述中生成高质量的提示词&#xff0c;从而使文生图模型能够生成更美观的图像。BeautifulPrompt通…

【MATLAB】MODWT分解+FFT+HHT组合算法

有意向获取代码&#xff0c;请转文末观看代码获取方式~也可转原文链接获取~ 1 基本定义 MODWT分解FFTHHT组合算法是一种综合性的信号处理方法&#xff0c;它结合了经验小波变换&#xff08;Empirical Wavelet Transform&#xff0c;EWT&#xff09;、快速傅里叶变换&#xff…

爱智EdgerOS之深入解析如何应用爱智的视频流模块完成拉流

一、ONVIF 规范和常见视频流传输协议 ① ONVIF 规范 随着视频监控产业链的成熟&#xff0c;市面上陆陆续续出现了各式各样的网络摄像设备&#xff0c;这些设备都需要通讯协议才能进行数据传输。早期厂商都采用私有协议&#xff0c;但是现在厂商分工明确&#xff0c;有的负责生…