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.hqwc.cn/news/258624.html

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

相关文章

52 代码审计-PHP项目类RCE及文件包含下载删除

目录 漏洞关键字:演示案例:xhcms-无框架-文件包含跨站-搜索或应用-includeearmusic-无框架-文件下载-搜索或应用功能-down等zzzcms-无框架-文件删除RCE-搜索或应用-unlink、eval 漏洞关键字: SQL注入&#xff1a; select insert update mysql_query mysql等 文件上传&#xff…

《opencv实用探索·十五》inRange二值化图像

opencv接口如下&#xff1a; void inRange(InputArray src, InputArray lowerb, InputArray upperb, OutputArray dst);函数实现二值化功能&#xff0c;主要是将在两个阈值内的像素值设置为白色&#xff08;255&#xff09;&#xff0c;而不在阈值区间内的像素值设置为黑色&am…

C语言入门课程之课后习题之折半查找法

目录 1解题思路&#xff1a; 2代码所示&#xff1a; 3运行代码&#xff1a; 4习题不难&#xff0c;多刷题&#xff0c;练思路&#xff0c;最重要的不是学会了一道题&#xff0c;而是掌握其编程思想&#xff1b; 1解题思路&#xff1a; 折半查找法&#xff08;half-interval…

delphi/python 实现小红书xhs用户作品列表和图片/视频无水印解析

技术学习&#xff0c;请勿用与非法用途&#xff01;&#xff01;&#xff01; 成品图用户作品列表接口 /api/sns/web/v1/user_posted?num30&cursor&user_id642bf0850000000011022c4e&image_scenes http Get方式&#xff0c;请求头需要带上x-s x-t签名验证笔记明细…

前端:HTML+CSS+JavaScript实现轮播图2

前端&#xff1a;HTMLCSSJavaScript实现轮播图2 1. 和之前版本的区别2. 实现原理3. 针对上述的改进3. 参考代码 1. 和之前版本的区别 之前发布的那篇关于轮播图的文章在这&#xff1a;前端&#xff1a;HTMLCSSJavaScript实现轮播图&#xff0c;只能说存在问题吧&#xff01;比…

三防平板|手持终端PDA|8寸/10寸工业三防平板电脑主板方案定制

近年来&#xff0c;随着科技的快速发展&#xff0c;三防平板成为了各行各业中不可或缺的工具。三防平板采用IP67级别的防护设计&#xff0c;通过了多项测试标准&#xff0c;如国标和美标&#xff0c;具备防水、防摔、防尘、防撞、防震、防跌落以及防盐雾等多重防护功能。因此&a…

C++特性之多态

C作为面向对象的语言&#xff0c;三大特性之一多态在平时的编程中使用频率特别高。 本篇文章就来详细讲解一下多态。 什么是多态 不同的对象做相同的一件事会出现不同的状态&#xff0c;这就是多态。 举个列子&#xff1a;比如普通人买车票要全价购买&#xff0c;而军人只用半…

每天学习一点shell系列(2)—函数的参数传递

参考博客&#xff1a;shell 脚本-10函数_eno_zeng的博客-CSDN博客 $n 或 ${n} &#xff1a;函数内使用 $n 或 ${n} 访问对应的参数, 数字代表参数的前后顺序, $1 代表第一个参数, $2 代表第三个参数, $n 代表第n个参数&#xff1b;当n>10时&#xff0c;需要使用${n}来获取参…

苹果Vision Pro即将量产

据界面新闻消息&#xff0c;苹果公司将在今年12月正式量产第一代MR&#xff08;混合现实&#xff09;产品Vision Pro。苹果公司对Vision Pro寄予了厚望&#xff0c;预计首批备货40万台左右&#xff0c;2024年的销量目标是100万台&#xff0c;第三年达到1000万台。 苹果的供应…

Linux中的文件系统

本章主要介绍文件系统的管理 了解什么是文件系统对分区进行格式化的操作挂载分区查找文件 在Windows系统中&#xff0c;买了一块新的硬盘加到电脑后&#xff0c;需要对分区进行格式化才能使用&#xff0c;Linux系统中也是一样&#xff0c;首先我们需要了解什么是文件系统 1.…

python数据分析总结(pandas)

目录 前言 df导入数据 df基本增删改查 数据清洗 ​编辑 索引操作 数据统计 行列操作 ​编辑 df->types 数据格式化 ​编辑 日期数据处理 前言 此篇文章为个人python数据分析学习总结&#xff0c;总结内容大都为表格和结构图方式&#xff0c;仅供参考。 df导入数…

初识人工智能,一文读懂人工智能概论(1)

&#x1f3c6;作者简介&#xff0c;普修罗双战士&#xff0c;一直追求不断学习和成长&#xff0c;在技术的道路上持续探索和实践。 &#x1f3c6;多年互联网行业从业经验&#xff0c;历任核心研发工程师&#xff0c;项目技术负责人。 &#x1f389;欢迎 &#x1f44d;点赞✍评论…