一、linux驱动开发-10.1-MISC驱动

一、前言

       misc驱动也叫杂项驱动,就是某些外设无法进行分类的时候就可以使用MISC驱动。MISC驱动其实就是字符设备驱动,通常嵌套在platform总线驱动中。

二、简介

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

       MISC设备会自动创建cdev,不需要手动创建,因此可以简化字符设备驱动的编写。

2.1、定义MISC设备

       linux中用miscdevice表示一个MISC设备,如:

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

        定义一个MISC设备后需要设备minor、name、fops三个成员变量。

①、minor:因为主设备号固定为10,所以只需要设置次设备号minor,linux在miscdevice.h中预定义了一些MISC设备的子设备号,可以使用这些预定义的,也可以自定义,只要这个子设备号没被其他设备使用;

②、name:就是此设备的名字,设备注册成功后就会在/dev目录下生成一个名为name的设备文件;

③、fops就是字符设备的操作集合;

2.2、注册设备

       设置好miscdevice后就需要使用misc_register函数向系统注册一个MISC设备:

/*
@misc:要注册的MISC设备

@return:0,成功
        <0,失败
*/
int misc_register(struct miscdevice *misc)

       这个函数用来代替之前创建设备的方法:

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

2.3、注销设备

       卸载驱动的时候,使用misc_deregister函数注销掉MISC设备。

/*
@misc:要注销的MISC设备

@return:0,成功
        <0,失败
*/
int misc_deregister(srtuct miscdevice *misc);

       同样,这个函数也代替了之前的一系列删除此前创建的cdev,设备的函数:

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

       这样确实简化了字符设备驱动编写。

三、驱动编写

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/fs.h>
#include <linux/slab.h>
#include <linux/uaccess.h>
#include <linux/io.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/of_gpio.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/platform_device.h>
#include <linux/miscdevice.h>

#define LEDDEV_CNT     1     
#define LEDDEV_NAME    "miscbeep"

#define	LED_ON      1
#define LED_OFF     0

//设备结构体
struct leddev_dev{
	dev_t devid;
	struct cdev cdev;
	struct class *class;
	struct device *device;
	int major;
	int minor;
	struct device_node *nd;		//设备节点
	int led_gpio;				//led所使用的GPIO编号
	struct miscdevice misc;
};

struct leddev_dev leddev;  

static void led_switch(uint8_t sta)
{
	if (sta == LED_ON)
	{
		gpio_set_value(leddev.led_gpio, 0);
	} else if (sta == LED_OFF) {
		gpio_set_value(leddev.led_gpio, 1);
	}
}

static int leddev_open(struct inode *inode, struct file *filp)
{
	filp->private_data = &leddev;
	printk("leddev open!\n");
	return 0;
}

static ssize_t leddev_read(struct file *filp, __user char *buf, size_t count, loff_t *ppos)
{	
	printk("leddev read\n");
	return 0;
}

static ssize_t leddev_write(struct file *filp, const char __user *buf, size_t count, loff_t *oppos)
{
	int ret;
	uint8_t data_buf[1];
	ret = copy_from_user(data_buf, buf, count);
	if (ret < 0)
	{
		printk("write data fail\n");
		return -EFAULT;
	}

	led_switch(data_buf[0]);

	return 0;
}

static int leddev_release(struct inode *inode, struct file *filp)
{
	printk("led release\n");
	return 0;
}

static const struct file_operations leddev_fops = {
	.owner = THIS_MODULE,
	.open  = leddev_open,
	.read  = leddev_read,
	.write = leddev_write,
	.release = leddev_release,
};

/*
*platform驱动的probe函数
*驱动与设备匹配成功以后此函数就会执行
*/

static int leddev_probe(struct platform_device *dev)
{
	int ret = 0;

	printk("led driver and device has match\r\n");

	//通过节点名字查找节点
	leddev.nd = of_find_node_by_name(NULL, "beep");
	if (leddev.nd == NULL)
	{
		printk("find %s fail\r\n", "beep");
		return -EINVAL;
	}

	//获取GPIO编号
	leddev.led_gpio = of_get_named_gpio(leddev.nd, "beep-gpio", 0);
	if (leddev.led_gpio < 0)
	{
		printk("get gpio fail\r\n");
		return -EINVAL;
	}
	else
	{
		printk("led_gpio value is %d", leddev.led_gpio);
	}

	//设置led状态
	ret = gpio_direction_output(leddev.led_gpio, 1);
	if (ret < 0)
	{
		printk("set led status fail\r\n");
		return -EINVAL;
	}

	leddev.misc.minor = 66;
	leddev.misc.name = LEDDEV_NAME;
	leddev.misc.fops = &leddev_fops;

	ret = misc_register(&leddev.misc);
	if (ret < 0)
	{
		printk("register miscbeep fail\r\n");
		return -EINVAL;
	}

	return 0;

	//分配设备号
	if (leddev.major) {
		leddev.devid = MKDEV(leddev.major, 0);
		register_chrdev_region(leddev.devid, LEDDEV_CNT, LEDDEV_NAME);
	} else {
		alloc_chrdev_region(&leddev.devid, 0, LEDDEV_CNT, LEDDEV_NAME);
		leddev.major = MAJOR(leddev.devid);
		leddev.minor = MINOR(leddev.devid);
	}
	printk("leddev major=%d, minor= %d\r\n", leddev.major, leddev.minor);
	//初始化cdev
	leddev.cdev.owner = THIS_MODULE;
	cdev_init(&leddev.cdev, &leddev_fops);
	//添加cdev
	cdev_add(&leddev.cdev, leddev.devid, LEDDEV_CNT);
	//创建类
	leddev.class = class_create(THIS_MODULE, LEDDEV_NAME);
	//创建设备
	leddev.device = device_create(leddev.class, NULL, leddev.devid, NULL, LEDDEV_NAME);
	
	return 0;
}

static int leddev_remove(struct platform_device *dev)
{
    led_switch(LED_OFF);

	misc_deregister(&leddev.misc);
}

static const struct of_device_id led_of_match[] = {
	{ .compatible = "atkmini-beep"},
	{}
};

/*
*platform平台驱动结构体
*/
static struct platform_driver led_driver = {
    .driver = {
        .name = "imx6ul-beep",
		.of_match_table = led_of_match,
    },
    .probe = leddev_probe,
    .remove = leddev_remove,
};

static int __init leddriver_init(void)
{
    return platform_driver_register(&led_driver);
}

static void __exit leddriver_exit(void)
{
    platform_driver_unregister(&led_driver);
		
	printk("newchrdev exit\n");
}

module_init(leddriver_init);
module_exit(leddriver_exit);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("ZK");

四、测试

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值