热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

嵌入式Linuxmisc设备驱动

misc设备驱动简介那么杂项设备驱动是属于我们linux三大设备驱动的哪一项呢?由于linux驱动倾向于分层设计,所以每个具体的设备都可以找到它归属的类

misc 设备驱动简介
        那么杂项设备驱动是属于我们 linux 三大设备驱动的哪一项呢? 由于linux 驱动倾向于分层设计, 所以每个具体的设备都可以找到它归属的类型, 从而可以套到它相应的架构里面去, 我们只需要实现它最底层的那部分。 但是也有部分字符设备, 确实不知道它属于哪种类型, 一般推荐大家采用 miscdevice 的框架结构。 misc 的意思是混合的杂项的, 所以 misc 设备驱动也叫做杂项设
备驱动, 当板子上的某个设备没有办法分类时, 就可以用 misc 设备驱动。 它的注册跟使用比较的简单,所以比较适用于功能简单的设备。 正因为简单, 所以它通常嵌套在 platform 总线驱动中, 配合总线驱动达到更复杂, 多功能的效果。 杂项设备是字符设备的一种, 杂项设备可以自动生成设备节点。
        在学习 misc 设备驱动之前, 先来了解几个基础概念。
概念 1 设备节点
        我们可以启动我们的开发板, 进入到 dev 目录下, dev 目录下全部都是生成的设备节点, 如下图所示:

我们的系统里面有很多杂项设备。 我们可以输入以下命令来查看, 如下图所示:
cat /proc/misc
 

概念 2 杂项设备的优点
        杂项设备除了比字符设备代码简单, 还有别的区别吗? 所有的 misc 设备驱动的主设备号都为 10, 不同的设备使用不同的从设备号。 主设设备号相同就可以节省内核的资源, 在内核中大概可以找到 200 多处使用 miscdevice 框架结构的驱动。
概念 3 主设备号和次设备号的概念
        设备号包含主设备号和次设备号, 设备号是计算机识别设备的一种方式, 主设备号相同的就被视为同一类设备, 主设备号在 Linux 系统里面是唯一的, 次设备号不一定唯一。 主设备号可以比做成电话号码的区号。 比如北京的区号是 010, 次设备号可以比作成电话号码。
        主设备号可以通过以下命令来查看, 前面的数字就是主设备号, 如下图所示:
 cat /proc/devices

        misc 设备用 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;
};

        当我们创建一个 misc 设备的 miscdevice 结构体时, 需要我们指定 minor、 name 和 fops 这三个成员变量。 minor 表示次设备号, 需要用户设置, 在 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 MISC_DYNAMIC_MINOR 255

        设置子设备号时要注意不要重复使用其他设备的子设备号。 可以从这些预定义的子设备号中选择一个, 也可以自定义。name 就是这个 misc 设备的名字, 当设备注册成功后, 会在/dev 目录下自动生成一个名为 name 的设备文件。 fops 就是这个 misc 设备的操作集合。
        当创建好 miscdevice 结构体后, 使用 misc_register 函数向系统中注册一个 misc 设备, 函数原型如下:

函数int misc_register(struct miscdevice * misc)
参数 misc之前创建好的 miscdevice 结构体
返回值成功返回 0, 失败返回负数。

         在设备驱动的卸载函数中, 使用 misc_deregister 函数来注销掉 misc 设备。 函数原型如下

函数int misc_deregister(struct miscdevice *misc)
参数 misc要注销的 miscdevice 结构体。
返回值

        在 miscdevice 结构体的第四行, 它指向了一个 file_operation 的结构体。 file_operations 文件操作集在定义在 include/linux/fs.h 下面, 如下图所示。

          file_operations 中的成员函数实际是由 drivers/char/misc.c 中 misc 驱动核心层的 misc_fops 成员函数间接调用的。 file_operations 结构体里面的结构体成员都对应一个调用。 简单介绍一下其中比较常用的函数:
llseek()函数用来修改一个文件的当前的读写位置, 并将新位置返回。
read()函数用来从设备中读取数据, 成功时返回读取到的字节数, 出错返回一个负值。
write()函数用来向设备发送数据, 成功时返回该函数写入的字节数。
poll()函数用于查询设备是否可以进行非阻塞读写。
unlock_ioctl()函数提供设备相关控制命令的实现。
mmap()函数将设备内存映射到进程的虚拟地址空间中。
open()函数用于打开设备文件。
release()函数用于关闭设备文件。
注册杂项设备有一个通用的思路和方法, 这里给大家总结为三个步骤:
填充 miscdevice 这个结构体
填充 file_operations 这个结构体
注册杂项设备并生生成设备节点。
 实验程序

添加头文件

/*注册杂项设备头文件*/
#include
/*注册设备节点的文件结构体*/
#include

填充 miscdevice 结构体

struct miscdevice misc_dev = {.minor = MISC_DYNAMIC_MINOR,.name = "hello_misc",.fops = &misc_fops,
};

上述代码第 2 行的 minor 为 MISC_DYNAMIC_MINOR, miscdevice 核心层会自动找一个空闲的次设备号,否则用 minor 指定的次设备号。 上述代码第 3 行 name 是设备的名称, 自定义为"hello_misc"
填充 file_operations 结构体

struct file_operations misc_fops={.owner = THIS_MODULE
};

        THIS_MODULE 宏是什么意思呢? 它在 include/linux/module.h 里的定义是
    #define THIS_MODULE (&__this_module)
        它是一个 struct module 变量, 代表当前模块, 可以通过 THIS_MODULE 宏来引用模块的 struct module结构, 比如使用 THIS_MODULE->state 可以获得当前模块的状态。 这个 owner 指针指向的就是你的模块。
注册杂项设备并生成设备节点
        在 misc_init()函数中填充 misc_register()函数注册杂项设备, 并判断杂项设备是否注册成功。

static int misc_init(void){int ret;ret &#61; misc_register(&misc_dev); //注册杂项设备if(ret<0) //判断杂项设备是否注册成功{printk("misc registe is error \n"); //打印杂项设备注册失败} printk("misc registe is succeed \n"); //打印杂项设备注册成功return 0;
}在 misc_exit&#xff08;&#xff09; 函数中填充 misc_deregister()函数注销杂项设备。static void misc_exit(void){misc_deregister(&misc_dev); //注销杂项设备printk("misc gooodbye! \n"); //打印杂项设备注销成功
}

完整的代码如下图所示&#xff1a;
 

/*
* &#64;Descripttion: 最简单的杂项设备驱动
* &#64;version:
* &#64;Author: topeet
*/
#include //初始化头文件
#include //最基本的文件&#xff0c; 支持动态添加和卸载模块。
#include /*注册杂项设备头文件*/
#include /*注册设备节点的文件结构体*/struct file_operations misc_fops&#61;
{ //文件操作集.owner &#61; THIS_MODULE
};
struct miscdevice misc_dev &#61;
{ //杂项设备结构体.minor &#61; MISC_DYNAMIC_MINOR, //动态申请的次设备号.name &#61; "hello_misc", //杂项设备名字是 hello_misc.fops &#61; &misc_fops, //文件操作集
};static int misc_init(void)
{ //在初始化函数中注册杂项设备int ret;ret &#61; misc_register(&misc_dev);if(ret<0){printk("misc registe is error \n");} printk("misc registe is succeed \n");return 0;
} static void misc_exit(void){ //在卸载函数中注销杂项设备misc_deregister(&misc_dev);printk(" misc gooodbye! \n");
} module_init(misc_init);
module_exit(misc_exit);
MODULE_LICENSE("GPL");

        现在最简单的杂项设备的驱动就写完了&#xff0c; 那么接下来我们可以把这个驱动编译一下&#xff0c; 然后放到开发板上面运行。 编译驱动&#xff0c; 可以将它编译进内核里面&#xff0c; 也可以将它编译成模块。
编译驱动程序
Makefile 为&#xff1a;

obj-m &#43;&#61; misc.o #先写生成的中间文件的名字是什么&#xff0c; -m 的意思是把我们的驱动编译成模块
KDIR:&#61;/home/topeet/driver/imx6ull/linux-imx-rel_imx_4.1.15_2.1.0_ga/
PWD?&#61;$(shell pwd) #获取当前目录的变量
all:make -C $(KDIR) M&#61;$(PWD) modules #make 会进入内核源码的路径&#xff0c; 然后把当前路径下的代码编译成
模块

驱动编译成功生成了 ko 文件&#xff0c; 如下图所示&#xff1a;

运行测试
 进入到共享目录&#xff0c; 加载驱动模块如图所示&#xff1a;
cd imx6ull/
ls
cd misc/
insmod misc.ko

驱动加载成功后&#xff0c; 输入以下命令&#xff0c; 查看注册的设备节点是否存在&#xff0c; 如下图所示&#xff0c; 设备节点存在。
ls /dev/h*

输入以下命令拆卸驱动模块,如下图所示&#xff1a;
rmmod misc
 

 


推荐阅读
  • C基本语法C程序可以定义为对象的集合,这些对象通过调用彼此的方法进行交互。现在让我们简要地看一下什么是类、对象,方法、即时变量。对象-对象具有状态和行为 ... [详细]
  • 本文详细介绍了Java编程语言中的关键字及其用途,包括53个关键字和2个保留字。文章不仅解释了每个关键字的基本功能,还提供了实际应用场景中的使用示例。 ... [详细]
  • 本文详细介绍了如何利用go-zero框架从需求分析到最终部署至Kubernetes的全过程,特别聚焦于微服务架构中的网关设计与实现。项目采用了go-zero及其生态组件,涵盖了从API设计到RPC调用,再到生产环境下的监控与维护等多方面内容。 ... [详细]
  • Android开发经验分享:优化用户体验的关键因素
    随着Android市场的不断扩展,用户对于移动应用的期望也在不断提高。本文探讨了在Android开发中如何优化用户体验,以及为何用户体验的重要性超过了技术本身。 ... [详细]
  • 本文介绍如何使用Java实现AC自动机(Aho-Corasick算法),以实现高效的多模式字符串匹配。文章涵盖了Trie树和KMP算法的基础知识,并提供了一个详细的代码示例,包括构建Trie树、设置失败指针以及执行搜索的过程。 ... [详细]
  • 本文介绍了一种算法,用于在一个给定的二叉树中找到一个节点,该节点的子树包含最大数量的值小于该节点的节点。如果存在多个符合条件的节点,可以选择任意一个。 ... [详细]
  • 本文主要解决了在编译CM10.2时出现的关于Samsung Exynos 4 HDMI HAL库中SecHdmiV4L2Utils.cpp文件的编译错误。 ... [详细]
  • 本文介绍了在解决Hive表中复杂数据结构平铺化问题后,如何通过创建视图来准确计算广告日志的曝光PV,特别是针对用户对应多个标签的情况。同时,详细探讨了UDF的使用方法及其在实际项目中的应用。 ... [详细]
  • 本文总结了几个常用的Android开发技巧,包括检测设备上是否安装特定应用、获取应用的版本名称、设置状态栏透明以及如何从一个应用跳转至另一个应用的方法。 ... [详细]
  • 本文探讨了在使用 ClickOnce 部署方式时遇到的自动更新失败问题,包括本地安装与服务器安装的不同表现,并提供了详细的解决方案。 ... [详细]
  • 在现代移动应用开发中,尤其是iOS应用,处理来自服务器的JSON数据是一项基本技能。无论是使用Swift还是PHP,有效地解析和利用JSON数据对于提升用户体验至关重要。本文将探讨如何在Swift中优雅地处理JSON,以及PHP中处理JSON的一些技巧。 ... [详细]
  • 3144:[Hnoi2013]切糕TimeLimit:10SecMemoryLimit:128MBSubmit:1261Solved:700[Submit][St ... [详细]
  • 题目描述墨墨购买了一套N支彩色画笔(其中有些颜色可能相同),摆成一排,你需要回答墨墨的提问。墨墨会像你发布如下指令ÿ ... [详细]
  • 深入解析链表成环问题:剑指Offer第22天的新视角
    本文将详细介绍链表成环问题的多种解法,包括哈希表法、JSON.stringify特殊解法及双指针法,并提供详尽的代码示例。阅读本文,你不仅能够掌握这一经典算法问题的核心技巧,还能了解到更多编程思维的拓展。 ... [详细]
  • ZOJ 2760 - 最大流问题
    题目链接:How Many Shortest Paths。题目描述:给定一个包含n个节点的有向图,通过一个n*n的矩阵来表示。矩阵中的a[i][j]值为-1表示从节点i到节点j无直接路径;否则,该值表示从i到j的路径长度。输入起点vs和终点vt,计算从vs到vt的所有不共享任何边的最短路径数量。如果起点和终点相同,则输出无穷大。 ... [详细]
author-avatar
哥的微笑帅_655
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有