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-imx

all:
	clear
	make -C $(KERN_DIR) M=`pwd` modules 
	$(CROSS_COMPILE)gcc -o miscledApp miscledApp.c 

clean:
	clear
	make -C $(KERN_DIR) M=`pwd` modules clean
	rm -rf modules.order
	rm -f miscledApp


obj-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

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

信号完整性分析

目录 前言一、信号完整性SI1.1 信号失真1.2 串扰1.3 衰减 二、电源完整性PI2.1 地弹2.2 电源轨道塌陷 三、电磁兼容EMC3.1 电磁辐射3.2 抗干扰 前言 本篇介绍信号完整性分析的知识体系&#xff0c;以及部分分析方法。   什么是信号完整性?通俗来讲&#xff0c;信号在互连线的…

花生壳安装在ubuntu下,记住要SN号登陆

在ubantu18.0.4上下载花生壳 进入花生壳的下载链接 选择linux版本进行下载 记住选ubuntu 运行命令phddns start

使用阿里巴巴同步工具DataX实现Mysql与ElasticSearch(ES)数据同步

一、Linux环境要求 二、准备工作 2.1 Linux安装jdk 2.2 linux安装python 2.3 下载DataX&#xff1a; 三、DataX压缩包导入&#xff0c;解压缩 四、编写同步Job 五、执行Job 六、定时更新 6.1 创建定时任务 6.2 提交定时任务 6.3 查看定时任务 七、增量更新思路 一、Linux环境要…

智能优化算法应用:基于人工大猩猩部队算法无线传感器网络(WSN)覆盖优化 - 附代码

智能优化算法应用&#xff1a;基于人工大猩猩部队算法无线传感器网络(WSN)覆盖优化 - 附代码 文章目录 智能优化算法应用&#xff1a;基于人工大猩猩部队算法无线传感器网络(WSN)覆盖优化 - 附代码1.无线传感网络节点模型2.覆盖数学模型及分析3.人工大猩猩部队算法4.实验参数设…

kettle作业发送@163邮件

版本&#xff1a;20231207 用kettle做一个简单的邮件发送 使用模块 start、转换、邮件 在start设置好你需要的时间 在转换中随便添加一个你之前保存的一个任务 重点在邮件设置上 1.邮件的地址 2.邮件的服务器 这里最重要的一点就是发件人验证的第三方接入密码&#xff0c;这…

C++作业6

以下是一个简单的比喻&#xff0c;将多态概念与生活中的实际情况相联系&#xff1a; 比喻&#xff1a;动物园的讲解员和动物表演 想象一下你去了一家动物园&#xff0c;看到了许多不同种类的动物&#xff0c;如狮子、大象、猴子等。现在&#xff0c;动物园里有一位讲解员&…

记录 | centos源码编译bazel

tensorflow的源码编译依赖于 bazel 这里进行 bazel 的源码编译 1、安装依赖 sudo yum install -y java-11-openjdk sudo yum install -y java-11-openjdk-devel sudo yum install -y protobuf-compiler zip unzip2、知悉要安装的 bazel 的版本 务必安装受支持的 Bazel 版本…

设备间的指令通信

指令通信的概念 要进行设备和设备之间的交流就需要通过串口发送数据进行交流 而串口发送简单的数据只需要传输介质 但是要发送复杂的数据就需要介质和传输的规则了 三种应用场景 比如在上位机和mcu之间 通过上位机管理控制器 从而控制电池 单片机和单片机之间 用户输入数据到…

MySQL实战45讲-第1-2讲-一条SQL查询语句是如何执行的? 一条SQL更新语句是如何执行的

大体来说&#xff0c;MySQL可以分为Server层和存储引擎层两部分 Server层&#xff1a;Server层包括连接器、查询缓存、分析器、优化器、执行器等。以及所有的内置函数&#xff08;如日期、时间、数学和加密函数等&#xff09;&#xff0c;所有跨存储引擎的功能都在这一层实现&a…

添加cpack install功能

修改最外层的CMakeLists.txt, 添加几行代码&#xff1a; # If GNUInstallDirs is not included, CMAKE_INSTALL_BINDIR is empty. include(GNUInstallDirs)# it must go before project in order to work set(CMAKE_INSTALL_PREFIX "${PROJECT_SOURCE_DIR}" CACHE …

记录一下快速上手Springboot登录注册项目

本教程需要安装以下工具&#xff0c;如果不清楚怎么安装的可以看下我的这篇文章 链接: https://blog.csdn.net/qq_30627241/article/details/134804675 管理工具&#xff1a; maven IDE&#xff1a; IDEA 数据库&#xff1a; MySQL 测试工具&#xff1a; Postman 打开IDE…

若依角色与权限字符串

文章目录 一、简介1.基于权限字段串2.基于角色 二、若依的权限控制1.介绍2.实践 一、简介 基于权限字段串和基于角色的访问控制是两种不同的权限管理模型&#xff0c;它们各自有其优点和应用场景。下面是这两种模型的基本概念&#xff1a; 1.基于权限字段串 基于权限字段串&…

产品成本收集器流程演示

感谢大佬的文章&#xff0c;我只是一个翻译搬运工&#xff0c;原文地址&#xff1a;产品成本收集器 概述 SAP 令人兴奋的部分之一是它在不同操作模块之间的集成程度。使用产品成本收集器来跟踪生产就是一个很好的例子。在本博客中&#xff0c;我计划遵循产品成本收集器流程&a…

Unity中C#如何访问并修改Shader材质

文章目录 前言一、我们用点击按钮来改变Shader传入的颜色值1、在渲染GUI时&#xff0c;绘制一个按钮2、我们使用一个公共的成员变量存储需要进行修改的游戏对象3、最后给绘制的按钮点击增加逻辑即可 二、测试使用的代码1、Shader代码&#xff1a;2、C#脚本 前言 我们写好Shade…

揭秘C语言结构体:通往内存对齐的视觉之旅

揭秘C语言结构体&#xff1a;通往内存对齐的视觉之旅 引言 在C语言的编程旅程中&#xff0c;结构体&#xff08;structs&#xff09;是一个关键而强大的概念。结构体不仅允许我们组织和存储不同类型的数据&#xff0c;而且通过深入了解内存对齐&#xff0c;我们可以更好地优化…

[b01lers2020]Life on Mars 一个接口的sql schema.schemate表

这里还是很简单的 啥也没有 然后抓包看看 发现传递参数 直接尝试sql 然后如果正确就会返回值 否则 返回1 chryse_planitia union select database(),version() 发现回显 直接开始注入 chryse_planitia union select database(),version()chryse_planitia union select data…

在UniApp中使用uni.makePhoneCall方法调起电话拨打功能

目录 1.在manifest.json文件中添加权限 2. 组件中如何定义 3.如何授权 4.相关知识点总结 1.在manifest.json文件中添加权限 {"permissions": {"makePhoneCall": {"desc": "用于拨打电话"}} }2. 组件中如何定义 <template>…

【论文合集】在非欧空间中的图嵌入方法(Graph Embedding in Non-Euclidean Space)

文章目录 1. Hyperbolic Models1.1 Hyperbolic Graph Attention Network1.2 Poincar Embeddings for Learning Hierarchical Representations.1.3 Learning Continuous Hierarchies in the Lorentz Model of Hyperbolic Geometry1.4 Hyperbolic Graph Convolutional Neural Net…