linux驱动 — 第一个实战驱动模板之Misc驱动

前言

前面所讲的字符设备驱动基本是从概念与流程上进行分析的,是用来学习与理解的,在实际应用中我们可不会这么一步一步去实现,在实际中我们往往会使用那些开发快、代码又简洁的途径,linux下都已经为我们准备好了;这就比如你家离镇上有10公里,你要去镇上赶集,可以走路去,可以骑车去,也可以坐车去,完全取决于你自己,不管用什么方式你都能到达,但是在实际中我想应该更多的是坐车去,因为速度快、轻松、少吃马路上的很多灰,但是有些杠精说:我就是要走路去,走路不仅可以锻炼身体又低碳环保,当然,这也是可以的,但是如果你有急事赶时间,尤其在我们现在这浮躁的社会,一个项目下来往往会催着你尽快搞出来,这样你不得不走速度快效率高的途径,linux下驱动开发就是这样的,一个驱动它有很多方式可以实现,但是你应该选择速度最快、效率最高的那一条,所以首先你就需要搞明白,哪一种才是最快最好的,好了,废话不多说,直接开干。

一、misc简介

Linux的驱动设计是趋向于分层的,大多数设备都有自己归属的类型,例如按键、触摸屏属于输入设备,Linux有一个input子系统框架。但是对于adc、蜂鸣器等设备,无法明确其属于什么类型,对于这种设备一般推荐使用misc驱动框架编写驱动程序,misc设备也是一个字符设备,在misc的初始化函数中注册了一个字符设备,主设备号为MISC_MAJOR (10),这个字符设备并不是具体的misc设备实例的实现,只是misc核心层用来将应用层的操作转发到具体的misc实例中!

二、驱动开发准备

实际开发中的驱动是怎样的,实际开发中编写驱动,我们需要编写或修改的就如下三个东西:

设备树

设备树的由来说明一下:大佬Linus在2011年3月17日的ARM Linux邮件列表中宣称“this wholeARM thing is a facking pain in the ass”,这引发了ARM Linux社区的地震,随后ARM社区进行了一系列重大修正。于是PowerPC等其他体系结构下已经使用的FDT(Flattened Device Tree)进入到了ARM社区的视野,自linux3.0之后就引入了设备树。

驱动编写

这个就是我们所讲的一个核心与重中之重,所谓驱动就是操作硬件寄存器与外设的一系列方法,我们需要实现这些方法,同时将这些方法提供给操作系统调用,其实我们只要按照操作系统的要求来进行编写填充,大部分工作linux已经替我们完成了

APP测试

我们写的驱动行不行、能不能工作,肯定要验证才行,将驱动安装好后要进行测试,于是测试app是需要的,我们需要通过应用层测试我们的驱动能否正常工作,功能是否符合要求等等

三、设备树节点

在设备树的根节点下增加如下节点,实际就是描述一个蜂鸣器设备,它的名称、IO口、驱动匹配名等,make dtbs后,开发板用新的设备树文件启动,在/proc/device-tree/下会生成beep节点,这个节点就是蜂鸣器的一些描述信息,在驱动中就可以对这个节点进行读取操作。

	beep{
		label = "beep";
		gpios = <&gpio5 1 GPIO_ACTIVE_LOW>;
		default-state = "off";
		
		#address-cells = <1>;
		#size-cells = <1>;
		compatible = "atkalpha-beep";
		pinctrl-names = "default";
		pinctrl-0 = <&pinctrl_beep>;
		beep-gpio = <&gpio5 1 GPIO_ACTIVE_HIGH>;
		status = "okay";
	};

三、misc驱动模板

#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/of.h>
#include <linux/of_address.h>
#include <linux/of_gpio.h>
#include <linux/platform_device.h>
#include <linux/miscdevice.h>

#define MISCBEEP_NAME		"misc_beep"	/* 名字 	*/
#define MISCBEEP_MINOR		121			/* 子设备号 */
#define BEEPOFF 			0			/* 关蜂鸣器 */
#define BEEPON 				1			/* 开蜂鸣器 */
#define CLOSE_CMD 			(_IO(0XEF, 0x1))/* 关闭命令 */
#define OPEN_CMD			(_IO(0XEF, 0x2))/* 打开命令 */

/* misc_beep设备结构体 */
struct misc_beep_dev{
	struct device_node *nd; /* 设备节点 */
	int beep_gpio;			/* beep所使用的GPIO编号 */
};
struct misc_beep_dev misc_beep;	/* 定义一个beep设备 */

/* 打开设备 */
static int misc_beep_open(struct inode *inode, struct file *filp)
{
	filp->private_data = &misc_beep; /* 设置私有数据 */
	return 0;
}

/* 从设备读取数据 */
static ssize_t misc_beep_read(struct file *filp, char __user *buf, size_t cnt, loff_t *offt)
{
	return 0;
}

/* 向设备写数据 */
static ssize_t misc_beep_write(struct file *filp, const char __user *buf, size_t cnt, loff_t *offt)
{
	int retvalue;
	unsigned char databuf[1];
	unsigned char beepstat;
	struct misc_beep_dev *dev = filp->private_data;

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

	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 int misc_beep_release(struct inode *inode, struct file *filp)
{
	return 0;
}

/* ioctl函数 */
static long misc_beep_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
{
	struct timer_dev *dev =  (struct timer_dev *)filp->private_data;
	int timerperiod;
	unsigned long flags;
	
	switch (cmd) {
		case CLOSE_CMD:	
			gpio_set_value(dev->beep_gpio, 1);	/* 关闭蜂鸣器 */
			break;
		case OPEN_CMD:
			gpio_set_value(dev->beep_gpio, 0);	/* 打开蜂鸣器 */
			break;
		default:
			break;
	}
	return 0;
}

/* 设备操作函数 */
static struct file_operations misc_beep_fops = {
	.owner = THIS_MODULE,
	.open = misc_beep_open,
	.read = misc_beep_read,
	.write = misc_beep_write,
	.release = misc_beep_release,
	.unlocked_ioctl = timer_unlocked_ioctl,
};

/* MISC设备结构体 */
static struct miscdevice beep_miscdev = {
	.minor = MISCBEEP_MINOR,
	.name  = MISCBEEP_NAME,
	.fops  = &misc_beep_fops,
};

 /* platform驱动的probe函数,驱动与设备匹配后此函数就会执行 */
static int misc_beep_probe(struct platform_device *dev)
{
	int ret = 0;

	/* 1、获取设备节点:beep */
	misc_beep.nd = of_find_node_by_path("/beep");
	if(misc_beep.nd == NULL) {
		printk("beep node not find!\r\n");
		return -EINVAL;
	} 

	/* 2、 获取设备树中的gpio属性,得到BEEP所使用的BEEP编号 */
	misc_beep.beep_gpio = of_get_named_gpio(misc_beep.nd, "beep-gpio", 0);
	if(misc_beep.beep_gpio < 0) {
		printk("can't get beep-gpio");
		return -EINVAL;
	}

	/* 3、设置GPIO5_IO01为输出,并且输出高电平,默认关闭BEEP */
	ret = gpio_direction_output(misc_beep.beep_gpio, 1);
	if(ret < 0) {
		printk("can't set gpio!\r\n");
	}
	
	/* 一般情况下会注册对应的字符设备,但是这里我们使用MISC设备
  	 * 所以我们不需要自己注册字符设备驱动,只需要注册misc设备驱动即可 */
	ret = misc_register(&beep_miscdev);//在sys/class/misc会自动生成.name命名的文件夹
	if(ret < 0){
		printk("misc device register failed!\r\n");
		return -EFAULT;
	}
	return 0;
}

/* platform驱动的remove函数,移除platform驱动的时候此函数会执行 */
static int misc_beep_remove(struct platform_device *dev)
{
	/* 注销设备的时候关闭LED灯 */
	gpio_set_value(misc_beep.beep_gpio, 1);
	/* 注销misc设备 */
	misc_deregister(&beep_miscdev);
	return 0;
}

/* 设备树匹配列表 */
 static const struct of_device_id beep_of_match[] = {
     { .compatible = "atkalpha-beep" },
     {}//这个空项是必须要的
 };
 
/* platform驱动结构体 */
static struct platform_driver beep_driver = {
     .driver     = {
         .name   = "imx6ul-beep",         /* 驱动名和设备名匹配 */
         .of_match_table = beep_of_match, /* 与设备树的匹配表   */
     },
     .probe      = misc_beep_probe,
     .remove     = misc_beep_remove,
};

/* 向paltform总线注册与注销 */
module_platform_driver(beep_driver);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("mstar");

四、总结

这个模板的精髓在misc_register与module_platform_driver接口,misc_register接口实现了字符设备的设备号申请、初始化cdev、创建类与创建设备,大大节省了代码量,module_platform_driver接口实现了platform总线的注册与注销,以及模块的入口与出口,也减少了代码量,其过程都是一样的,只是linux将其通过这个接口封装起来,所以说代码量虽然少了,但是路程是一样的,我们对字符设备驱动编写的整个过程同样是必须要熟悉的!

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

东皇※太一

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值