22 Linux MISC 驱动

一、MISC 设备驱动简介

  misc 意思是杂项,所以当板子上的某些外设无法进行分类的时候可以使用 misc 驱动,一般 misc 驱动嵌套在 platform 总线驱动。

 所有的 MISC 设备驱动的主设备号都是 10,不同的设备使用不同的设备号。MISC 设备会自动创建 cdev,可以不用像之前手动创建。

  首先得向 Linux 注册一个 miscdevice 设备。miscdevice 是一个结构体:

66 struct miscdevice {
67 int minor;                             /* 子设备号 */
68 const char *name;                      /* 设备名字 */
69 const struct file_operations *fops;    /* 设备操作集 */
70 struct list_head list;
71 struct device *parent;
72 struct device *this_device;
73 const struct attribute_group **groups;
74 const char *nodename;
75 umode_t mode;
76 };

  定义了 MISC 设备后需要自己去设置 minornamefops 三个成员变量。minor 表示子设备号;name 就是 MISC 设备名字,当设备注册成功后会在 /dev 目录下生成一个名为 name 设备文件;fops 就是字符操作集合。注意,minor 中的有些被 Linux 所使用了,在include/linux/miscdevice.h 文件中,使用的时候需要从预定义的子设备号挑一个,也可以自己定义。

  设置完成后,使用 misc_register 函数注册 MISC 设备:

/*
 * @description : 注册 MISC
 * @param - misc : 要注册的 MISC 设备
 * @return : 负数,失败; 0,成功
 */
int misc_register(struct miscdevice * misc);

  之前所有的实验中,需要自己去调用一堆函数去创建设备,现在使用 misc_register 后就可以把以下这些给省略了:

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

  注册使用完当然要卸载,使用 misc_deregister 完成卸载:

/*
 * @description : 注销 MISC
 * @param - misc : 要注册的 MISC 设备
 * @return : 负数,失败; 0,成功
 */
int misc_deregister(struct miscdevice *misc)

   以往需要注销的设备驱动需要调用一堆函数去删除,如下。但现在只需要使用 misc_deregister 完成注销。

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

 

二、驱动实验编写

  这次是 platform 和 msci 结合的方式。

 

2.1 修改设备树

  首先打开 /home/alientek/linux/atk-mpl/linux/my_linux/linux-5.4.31/arch/arm/boot/dts/stm32mp15-pinctrl.dtsi 文件,创建 pinctrl 节点:

beep_pins_a:beep-pins {
		pins {
			pinmux = <STM32_PINMUX('C', 7, GPIO)>;	// Beep
			drive-push-pull;
			bias-pull-up;
			output-high;
			slew-rate = <0>;
		};
	};

  之后打开 /home/alientek/linux/atk-mpl/linux/my_linux/linux-5.4.31/arch/arm/boot/dts/stm32mp157d-atk.dts 文件,在"/"根节点创建子节点:

beep {
		compatible = "alientek,beep";
		status = "okay";
		pinctrl-0 = <&beep_pins_a>;
		pinctrl-names = "defalut";
		beep-gpio = <&gpioc 7 GPIO_ACTIVE_LOW>;
	};

  完成后需要重新编译设备树,并把编译完成的文件放在 tftpboot 文件夹下。

 

2.2 beep 驱动编写

  在 linux/atk-mpl/Drivers 创建 19_miscbeep,在里面创建 Vscode 工作区,并且新建 miscbeep.c 文件,输入以下内容:

#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			/* 开蜂鸣器 */

struct miscbeep_dev{

	int beep_gpio;			/* beep所使用的GPIO编号		*/
};

struct miscbeep_dev miscbeep;		/* beep设备 */

/*
 * @description		: beep相关初始化操作
 * @param – pdev		: struct platform_device指针,也就是platform设备指针
 * @return			: 成功返回0,失败返回负数
 */
static int beep_gpio_init(struct device_node *nd)
{
    int err;

    /* 从设备树中获取GPIO */
    miscbeep.beep_gpio = of_get_named_gpio(nd, "beep-gpio", 0);
    if(!gpio_is_valid(miscbeep.beep_gpio)) {
		printk("miscbeep:Failed to get beep-gpio\n");
		return -EINVAL;
	}

    /* 申请使用GPIO */
    err = gpio_request(miscbeep.beep_gpio, "beep");
    if(err) {
		printk("beep: Failed to request beep-gpio\n");
		return err;
	}
    
    /* 将GPIO设置为输出模式并设置GPIO初始化电平状态 */
    gpio_direction_output(miscbeep.beep_gpio, 1);

    return 0;
}

/*
 * @description		: 打开设备
 * @param - inode 	: 传递给驱动的inode
 * @param - filp 	: 设备文件,file结构体有个叫做private_data的成员变量
 * 					  一般在open的时候将private_data指向设备结构体。
 * @return 			: 0 成功;其他 失败
 */
static int miscbeep_open(struct inode *inode, struct file *filp)
{
	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 error;

    unsigned char databuf[1];   // 获取的状态值
	unsigned char beepstat;     // beep的状态

    error = copy_from_user(databuf, buf, cnt);
    if(error < 0) {
		printk("kernel write failed!\r\n");
		return -EFAULT;
	}

    beepstat = databuf[0];  
    if (beepstat = BEEPON)
    {
        gpio_set_value(miscbeep.beep_gpio, 0);      // 打开蜂鳴器
    }
    else if (beepstat = BEEPOFF)
    {
        gpio_set_value(miscbeep.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 = {
    .name = MISCBEEP_NAME,
    .minor = MISCBEEP_MINOR,
    .fops = &miscbeep_fops,
};

 /*
  * @description     : flatform驱动的probe函数,当驱动与
  *                    设备匹配以后此函数就会执行
  * @param - dev     : platform设备
  * @return          : 0,成功;其他负值,失败
  */
static int miscbeep_probe(struct platform_device *pdev)
{
    int error;

    printk("beep driver and device was matched!\r\n");
    
    /* 初始化BEEP */
    error = beep_gpio_init(pdev->dev.of_node);
    if (error < 0)
    {
        return error;
    }

    /* 一般情况下会注册对应的字符设备,但是这里我们使用MISC设备
  	 * 所以我们不需要自己注册字符设备驱动,只需要注册misc设备驱动即可
	 */
    error = misc_register(&beep_miscdev);
    if(error < 0){
		printk("misc device register failed!\r\n");
		goto free_gpio;
	}
    
    return 0;

free_gpio:
    gpio_free(miscbeep.beep_gpio);
    return -EINVAL;
}

/*
 * @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);
	
	/* 释放BEEP */
	gpio_free(miscbeep.beep_gpio);

	/* 注销misc设备 */
	misc_deregister(&beep_miscdev);
	return 0;
}

/* 匹配列表 */
 static const struct of_device_id beep_of_match[] = {
     { .compatible = "alientek, bepp" },
     { /* Sentinel */ }
 };

/* platform驱动结构体 */
static struct platform_driver beep_driver = {
    .driver = {
        .name = "stm32mp1-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");
MODULE_AUTHOR("ALIENTEK");
MODULE_INFO(intree, "Y");

 

2.3 beep 测试 APP 编写

  新建 miscbeepApp.c 文件,输入以下内容:

#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;
}

 

三、运行测试

  首先编写 Makefile 文件:

KERNELDIR := /home/alientek/linux/atk-mpl/linux/my_linux/linux-5.4.31
CURRENT_PATH := $(shell pwd)

obj-m := miscbeep.o

build: kernel_modules

kernel_modules:
	$(MAKE) -C $(KERNELDIR) M=$(CURRENT_PATH) modules

clean:
	$(MAKE) -C $(KERNELDIR) M=$(CURRENT_PATH) clean

  之后编译 miscbeep.c 和 miscbeepApp.c 文件:

make
arm-none-linux-gnueabihf-gcc miscbeepApp.c -o miscbeepApp

  将编译好的 miscbeepApp 和 miscbeep.ko 复制:

sudo cp miscbeepApp miscbeep.ko /home/alientek/linux/nfs/rootfs/lib/modules/5.4.31/ -f

  开启开发板,进入 lib/modules/5.4.31,输入以下命令:

cd lib/modules/5.4.31/

  加载驱动:

depmod
modprobe miscbeep.ko

  可以输入以下命令来看设备号和从设备号:

ls /dev/miscbeep -l


  从设备号和我们自己设置的 144 号一样,其实只要不去占用 Linux 已经用过的从设备号,其他从设备号都可以使用。

  测试 beep 驱动:

./miscbeepApp /dev/miscbeep 1     # 打开 BEEP
./miscbeepApp /dev/miscbeep 0     # 关闭 BEEP

   卸载驱动:

rmmod miscbeep.ko

 

总结

  其实 MISC设备驱动就是变成了一种函数集合,这个函数集合包含注册字符设备驱动需要的工作。

  使用 MISC 设备驱动也简单,首先先设置 MISC 设备结构体包括name、minor(次设备号)和 fops。设置完成后向 Linux 注册 MISC 设备,这个注册函数(misc_register())一般放在 probe 函数,注销函数(misc_deregister())放在 remove 中。

  下面用两个流程图,一个流程图是传统框架,一个是 platform 驱动和 MISC 设备的常用框架对比:

  传统结构在刚开始编写设备结构体的时候需要编写很大一堆结构体成员变量(cdev、类、设备号、设备等),常用框架就可以省去这些,只需要 GPIO 编号编号即可。传统框架有注册字符设备等等,常用框架只需要一个注册 MISC 设备就可以完成这些。传统结构很多初始化的工作都放在驱动入口,不好管理;常用框架就分成了多层,每层就很明确有什么。

本文作者:烟儿公主

本文链接:https://www.cnblogs.com/toutiegongzhu/p/17686037.html

版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。

posted @   烟儿公主  阅读(228)  评论(0编辑  收藏  举报
点击右上角即可分享
微信分享提示
💬
评论
📌
收藏
💗
关注
👍
推荐
🚀
回顶
收起
  1. 1 夏日大冒险 暴躁的兔子
夏日大冒险 - 暴躁的兔子
00:00 / 00:00
An audio error has occurred.

作词 : 暴躁的兔子

作曲 : 暴躁的兔子

编曲 : IOF

混音:Gfanfan

出品:网易飓风

夏天 不要再浪费时间

实现 你承诺过的改变

别再 找一堆借口拖延

现在就和我一起飞向海边

人生苦短 你应该学会如何作乐

低着头还怎么应对挫折

人应该为自己活着

不用去迎合

要去寻欢作乐

撮合我的浪漫和悲欢

把这荒诞人生都塞满

生活难免磕磕绊绊

对抗生活的平庸就是浪漫

学会取悦自己逆风翻盘

去反抗变态的三观

把条条框框都砸烂

建立新的规则推翻谈判

无可救药的人呐

和我一起去海边

看那日出和晚霞 海天一线

看阳光穿越地平线

现实交织的明天

就在这个夏天

为自己改变

别怕山高路远

去冒险

我真的不care你是否会喜欢我

不跟风被定义的美 全都是灾祸

我才不讨好大多数绝不与示弱

过好你的生活

你管我应该怎么快活

没有人能有资格审判

别人的生活和牵绊

快闭上你的高谈阔论

乘风破浪吧 理想的风帆

我就是肆意张扬又如何

我就是锋芒毕露又如何

我就是离经叛道又如何

我就是要出格 你管我要如何

我就是与众不同又如何

我就是特立独行又如何

我就是不知好歹又如何

你管我怎样出格 你管我如何

无可救药的人呐

和我一起去海边

看那日出和晚霞 海天一线

看阳光穿越地平线

现实交织的明天

就在这个夏天

为自己改变

别怕山高路远

不知进退的人呐

和我一起去海边

聊聊曾经的理想 一起想当年

那曾想改变世界的人

是否还满腔热忱

不羁的我们放肆着

反抗那命运的指针

解放灵魂

推广:网易飓风

企划:贾焱祺

监制:徐思灵

出品人:谢奇笛