【IMX6ULL】Linux中的MISC设备驱动(搭配platform设备驱动)
2023-09-11 14:20:46 时间
Linux MISC驱动
MISC驱动也叫杂项驱动,但是本质上就是一个字符设备驱动,通常嵌套在platform总线驱动中。
MISC设备驱动的主设备号都为10,不同设备使用不同的从设备号,也就是说,这些不同的设备在设备树上的位置处于MISC设备节点的子节点的层次。即 根节点 --> MISC节点 --> 设备节点。
miscdevice 结构体
minor:子设备的设备号;
name:设备名字;
fops:操作函数集合,一个结构体变量,存放各种操作函数;
注册MISC设备API
/*
@function: 用于注册MISC设备
@param:
misc:要注册的 MISC 设备
@return:
0,成功;负数,失败
*/
int misc_register(struct miscdevice * misc)
MISC的优点
- MISC驱动本质上是一个字符设备驱动,但是内核集成一些API函数来帮助我们注册设备,这也就使得原来繁琐的字符设备驱动的注册和注销过程缩减了,即 init 和 exit 这两个函数内的内容。
- 所有的不知道在设备树上放哪的设备驱动节点,都可以挂载在MISC这个节点上,相当于作为MISC节点的子节点,那么这些设备的父设备号实际上就是MISC设备的设备号,而这些设备自身的设备号,就是MISC设备的子设备号。这样就节约了主设备号的浪费,就等于是原来只是一根树枝,现在是有很多枝杈的树枝。
学习过程中的猜想
MISC对于只需要一个IO进行通信的设备应该算是很友好的,那么一些依靠单总线协议的设备应该也能依靠MISC来编写相关驱动,例如DS18B20温湿度传感器等。
MISC设备下beep设备驱动代码编写
#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
/* MISC_beep 设备结构体 */
struct miscbeep_dev
{
dev_t devid; /* 设备号 */
struct cdev cdev; /* cdev */
struct class *class; /* 类 */
struct device *device; /* 设备 */
struct device_node *nd; /* 设备节点 */
int beep_gpio; /* beep 所使用的 GPIO 编号 */
};
/* beep 设备 */
struct miscbeep_dev miscbeep;
/* 打开设备 */
static int miscbeep_open(struct inode *inode, struct file *filp)
{
filp->private_data = &miscbeep;
return 0;
}
/* 向设备写数据 */
static ssize_t miscbeep_write(struct file *filp, const char __user *buf, size_t cnt, loff_t *offt)
{
int retvalue;
unsigned char databuf[1];
unsigned char beepstat;
struct miscbeep_dev *dev = filp->private_data;
retvalue = copy_from_user(databuf, buf, cnt);
beepstat = databuf[0];
if (beepstat == BEEPON)
{
gpio_set_value(dev->beep_gpio, 0); /* 打开蜂鸣器 */
}
else if (beepstat == BEEPOFF)
{
gpio_set_value(dev->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 = {
.minor = MISCBEEP_MINOR,
.name = MISCBEEP_NAME,
.fops = &miscbeep_fops,
};
/* flatform 驱动的 probe 函数,当驱动与设备匹配以后此函数就会执行 */
static int miscbeep_probe(struct platform_device *dev)
{
int ret = 0;
printk("beep driver and device was matched!\r\n");
/* 1.获取设备节点:beep */
miscbeep.nd = of_find_node_by_path("/beep");
if(miscbeep.nd == NULL)
{
printk("beep node not find!\r\n");
return -EINVAL;
}
/* 2.获取设备树中的GPIO属性,得到 beep 使用的 beep 编号 */
miscbeep.beep_gpio = of_get_named_gpio(miscbeep.nd, "beep-gpio", 0);
if(miscbeep.beep_gpio < 0)
{
printk("can't get beep-gpio");
return 0;
}
/* 3.设置 GPIO5_IO01 为输出,并且输出高电平, 默认关闭 beep */
ret = gpio_direction_output(miscbeep.beep_gpio, 1);
if(ret < 0)
{
printk("can't set gpio!\r\n");
}
/* 注册 misc 设备,取代了注册字符设备的那5个步骤 */
ret = misc_register(&beep_miscdev);
if(ret < 0)
{
printk("misc device register failed!\r\n");
return -EFAULT;
}
return 0;
}
/* remove 函数,移除 platform 驱动的时候此函数会执行 */
static int miscbeep_remove(struct platform_device *dev)
{
gpio_set_value(miscbeep.beep_gpio, 1);
/* 注销 misc 设备驱动 */
misc_deregister(&beep_miscdev);
return 0;
}
/* 匹配列表 */
static const struct of_device_id beep_of_match[] = {
{ .compatible = "atkalpha-beep" },
{ /* sentinel */ }
};
/* platform 驱动结构体 */
static struct platform_driver beep_driver = {
.driver = {
.name = "imx6ul-beep",
.of_match_table = beep_of_match,
},
.probe = miscbeep_probe,
.remove = miscbeep_remove,
};
/* 驱动入口函数 */
static int __init miscbeep_init(void)
{
return platform_driver_register(&beep_driver);
}
/* 驱动出口函数 */
static void __exit miscbeep_exit(void)
{
platform_driver_unregister(&beep_driver);
}
module_init(miscbeep_init);
module_exit(miscbeep_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Swiler");
相关文章
- 【Linux驱动】linux内核模块简介
- [Linux]linux如何把文件切成多块
- Linux 设备总线驱动模型
- 《网蜂A8实战演练》——8.Linux USB 主机控制器和设备驱动
- linux 混杂设备驱动之adc驱动
- linux lcd设备驱动剖析三
- linux lcd设备驱动剖析一
- Linux平台总线驱动设备模型
- 怎样在Linux 5上添加裸设备映射
- Linux 常用命令
- Linux基础之linux服务器服务器间拷贝文件
- 【学习总结】快速上手Linux玩转典型应用-第6章-linux常用命令讲解
- 字符设备驱动开发 Linux 设备号 字符设备驱动开发步骤 open 函数调用流程 设备号的组成 设备号的分配 Linux 应用程序对驱动程序的调用 字符设备注册与注销 实现设备的具体操作函数
- linux学习之八---Linux进程基础知识
- L51.linux命令每日一练 -- 第八章 Linux磁盘与文件系统管理命令 -- mkfs和dumpe2fs
- L31.linux命令每日一练 -- 第五章 Linux信息显示与搜索文件命令 -- uname和hostname
- L2.linux命令每日一练 -- 第一章 Linux命令行简介
- linux top命令及结果详解 top -p 查看Linux程序运行进程
- 第九章 linux-深入学习字符设备驱动编程①
- 第四章 linux字符设备的编写二
- Linux设备驱动中的ioctl