Linux MISC 驱动实验

我们板子上的某些外设无法进行分类的时候就可以使用 MISC 驱动。MISC 驱动其实就是最简单的字符设备驱动,通常嵌套在 platform 总线驱动中,实现复杂的驱动。

一、MISC 设备驱动简介

所有的 MISC 设备驱动的主设备号都为 10,不同的设备使用不同的从设备号。

// miscdevice 结构体代码
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;
};

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 MISC_DYNAMIC_MINOR 255

当设置好 miscdevice 以后就需要使用 misc_register 函数向系统中注册一个 MISC 设备,此
函数原型如下:

int misc_register(struct miscdevice * misc)

函数参数和返回值含义如下:
misc:要注册的 MISC 设备。
返回值:负数,失败;0,成功。

调用 misc_deregister 函数来注销掉 MISC 设备,函数原型如下:

int misc_deregister(struct miscdevice *misc)

函数参数和返回值含义如下:
misc:要注销的 MISC 设备。
返回值:负数,失败;0,成功。

用上面这两个函数可以省略之前字符设备驱动框架的大部分代码。

二、实验程序编写

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


/* miscbeep设备结构体 */

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编号		*/

};



struct miscbeep_dev miscbeep;		/* beep设备 */



/*

 * @description		: 打开设备

 * @param - inode 	: 传递给驱动的inode

 * @param - filp 	: 设备文件,file结构体有个叫做private_data的成员变量

 * 					  一般在open的时候将private_data指向设备结构体。

 * @return 			: 0 成功;其他 失败

 */

static int miscbeep_open(struct inode *inode, struct file *filp)

{

	filp->private_data = &miscbeep; /* 设置私有数据 */

	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 retvalue;

	unsigned char databuf[1];

	unsigned char beepstat;

	struct miscbeep_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 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,

};



 /*

  * @description     : flatform驱动的probe函数,当驱动与

  *                    设备匹配以后此函数就会执行

  * @param - dev     : platform设备

  * @return          : 0,成功;其他负值,失败

  */

static int miscbeep_probe(struct platform_device *dev)

{

	int ret = 0;



	printk("beep driver and device was matched!\r\n");

	/* 设置BEEP所使用的GPIO */

	/* 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 -EINVAL;

	}



	/* 3、设置GPIO5_IO01为输出,并且输出高电平,默认关闭BEEP */

	ret = gpio_direction_output(miscbeep.beep_gpio, 1);

	if(ret < 0) {

		printk("can't set gpio!\r\n");

	}

	

	/* 一般情况下会注册对应的字符设备,但是这里我们使用MISC设备

  	 * 所以我们不需要自己注册字符设备驱动,只需要注册misc设备驱动即可

	 */

	ret = misc_register(&beep_miscdev);

	if(ret < 0){

		printk("misc device register failed!\r\n");

		return -EFAULT;

	}



	return 0;

}



/*

 * @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);



	/* 注销misc设备 */

	misc_deregister(&beep_miscdev);

	return 0;

}



 /* 匹配列表 */

 static const struct of_device_id beep_of_match[] = {

     { .compatible = "atkalpha-beep" },  //必须与设备树的comparable一致

     { /* Sentinel */ }

 };

 

 /* platform驱动结构体 */

static struct platform_driver beep_driver = {

     .driver     = {

         .name   = "imx6ul-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("hsd");


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

}


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

嵌入式学习者。

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

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

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

打赏作者

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

抵扣说明:

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

余额充值